Fix error creation and warning
[claws.git] / src / oauth2.c
blobf9bd4780281a207c212f95368c8824738ad98f7e
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2021-2023 the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #include "claws-features.h"
23 #endif
25 #ifdef USE_OAUTH2
27 #include <glib.h>
28 #ifdef ENABLE_NLS
29 #include <glib/gi18n.h>
30 #else
31 #define _(a) (a)
32 #define N_(a) (a)
33 #endif
34 #include <stdio.h>
35 #include <string.h>
36 #include <errno.h>
38 #include "imap.h"
39 #include "oauth2.h"
40 #include "md5.h"
41 #include "utils.h"
42 #include "log.h"
43 #include "time.h"
44 #include "common/passcrypt.h"
45 #include "prefs_common.h"
47 #define GNUTLS_PRIORITY "NORMAL:!VERS-SSL3.0:!VERS-TLS1.0:!VERS-TLS1.1"
48 //Yahoo requires token requests to send POST header Authorization: Basic
49 //where the password is Base64 encoding of client_id:client_secret
51 static gchar *OAUTH2info[4][17]={
52 {"accounts.google.com",
53 "",
54 ".",
55 "http://127.0.0.1:8888",
56 "/o/oauth2/auth",
57 "/o/oauth2/token",
58 "/o/oauth2/token",
59 "code",
60 "https://mail.google.com",
61 "authorization_code",
62 "refresh_token",
63 "",
64 "",
65 "",
66 "",
67 "",
68 ""},
69 {"login.microsoftonline.com",
70 "",
71 "",
72 "http://127.0.0.1:8888",
73 "/common/oauth2/v2.0/authorize",
74 "/common/oauth2/v2.0/token",
75 "/common/oauth2/v2.0/token",
76 "code",
77 "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
78 "authorization_code",
79 "refresh_token",
80 "common",
81 "",
82 "offline",
83 "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
84 "query",
85 ""},
86 {"login.microsoftonline.com",
87 "",
88 "",
89 "http://127.0.0.1:8888",
90 "/common/oauth2/v2.0/authorize",
91 "/common/oauth2/v2.0/token",
92 "/common/oauth2/v2.0/token",
93 "code",
94 "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
95 "authorization_code",
96 "refresh_token",
97 "common",
98 "",
99 "offline",
100 "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
101 "query",
102 ""},
103 {"api.login.yahoo.com",
105 ".",
106 "oob",
107 "/oauth2/request_auth",
108 "/oauth2/get_token",
109 "/oauth2/get_token",
110 "code",
112 "authorization_code",
113 "refresh_token",
119 "1"}
122 static gchar *OAUTH2CodeMarker[5][2] = {
123 {"",""},
124 {"code=","&scope="},
125 {"code="," HTTP"},
126 {"code=","&session_state="},
127 {"yahoo_begin_mark","yahoo_end_mark"} /* Not used since token avalable to user to copy in browser window */
130 static gint oauth2_post_request (gchar *buf, gchar *host, gchar *resource, gchar *header, gchar *body);
131 static gint oauth2_filter_refresh (gchar *json, gchar *refresh_token);
132 static gint oauth2_filter_access (gchar *json, gchar *access_token, gint *expiry);
133 static gint oauth2_contact_server (SockInfo *sock, gchar *request, gchar *response);
136 static gint oauth2_post_request (gchar *buf, gchar *host, gchar *resource, gchar *header, gchar *body)
138 gint len;
140 len = strlen(body);
141 if (header[0])
142 return snprintf(buf, OAUTH2BUFSIZE, "POST %s HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept: text/html,application/json\r\nContent-Length: %i\r\nHost: %s\r\nConnection: close\r\nUser-Agent: ClawsMail\r\n%s\r\n\r\n%s", resource, len, host, header, body);
143 else
144 return snprintf(buf, OAUTH2BUFSIZE, "POST %s HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept: text/html,application/json\r\nContent-Length: %i\r\nHost: %s\r\nConnection: close\r\nUser-Agent: ClawsMail\r\n\r\n%s", resource, len, host, body);
147 static gint oauth2_filter_access (gchar *json, gchar *access_token, gint *expiry)
149 GMatchInfo *matchInfo;
150 GRegex *regex;
152 regex = g_regex_new ("\"access_token\": ?\"(.*?)\",?", G_REGEX_RAW, 0, NULL);
153 g_regex_match (regex, json, 0, &matchInfo);
154 if (g_match_info_matches (matchInfo))
155 g_stpcpy (access_token,g_match_info_fetch (matchInfo, 1));
156 else{
157 g_match_info_free (matchInfo);
158 return (-1);
161 g_match_info_free (matchInfo);
163 regex = g_regex_new ("\"expires_in\": ?([0-9]*),?", G_REGEX_RAW, 0, NULL);
164 g_regex_match (regex, json, 0, &matchInfo);
165 if (g_match_info_matches (matchInfo)){
166 // Reduce available token life to avoid attempting connections with (near) expired tokens
167 *expiry = (g_get_real_time () / G_USEC_PER_SEC) + atoi(g_match_info_fetch (matchInfo, 1)) - 120;
168 }else{
169 g_match_info_free (matchInfo);
170 return (-2);
173 g_match_info_free (matchInfo);
175 return(0);
178 static gint oauth2_filter_refresh (gchar *json, gchar *refresh_token)
180 GMatchInfo *matchInfo;
181 GRegex *regex;
183 regex = g_regex_new ("\"refresh_token\": ?\"(.*?)\",?", G_REGEX_RAW, 0, NULL);
184 g_regex_match (regex, json, 0, &matchInfo);
185 if (g_match_info_matches (matchInfo))
186 g_stpcpy (refresh_token,g_match_info_fetch (matchInfo, 1));
187 else{
188 g_match_info_free (matchInfo);
189 return (-1);
192 g_match_info_free (matchInfo);
194 return(0);
197 static gchar* oauth2_get_token_from_response(Oauth2Service provider, const gchar* response) {
198 gchar* token = NULL;
200 debug_print("Auth response: %s\n", response);
201 if (provider == OAUTH2AUTH_YAHOO) {
202 /* Providers which display auth token in browser for users to copy */
203 token = g_strdup(response);
204 } else {
205 gchar* start = g_strstr_len(response, strlen(response), OAUTH2CodeMarker[provider][0]);
206 if (start == NULL)
207 return NULL;
208 start += strlen(OAUTH2CodeMarker[provider][0]);
209 gchar* stop = g_strstr_len(response, strlen(response), OAUTH2CodeMarker[provider][1]);
210 if (stop == NULL)
211 return NULL;
212 token = g_strndup(start, stop - start);
215 return token;
218 int oauth2_obtain_tokens (Oauth2Service provider, OAUTH2Data *OAUTH2Data, const gchar *authcode)
220 gchar *request;
221 gchar *response;
222 gchar *body;
223 gchar *uri;
224 gchar *header;
225 gchar *tmp_hd, *tmp_hd_encoded;
226 gchar *access_token;
227 gchar *refresh_token;
228 gint expiry = 0;
229 gint ret;
230 SockInfo *sock;
231 gchar *client_id;
232 gchar *client_secret;
233 gchar *token = NULL;
234 gchar *tmp;
235 gint i;
237 i = (int)provider - 1;
238 if (i < 0 || i > (OAUTH2AUTH_LAST-1))
239 return (1);
241 token = oauth2_get_token_from_response(provider, authcode);
242 debug_print("Auth token: %s\n", token);
243 if (token == NULL) {
244 log_message(LOG_PROTOCOL, _("OAuth2 missing authorization code\n"));
245 return (1);
247 debug_print("Connect: %s:443\n", OAUTH2info[i][OA2_BASE_URL]);
248 sock = sock_connect(OAUTH2info[i][OA2_BASE_URL], 443);
249 if (sock == NULL) {
250 log_message(LOG_PROTOCOL, _("OAuth2 connection error\n"));
251 g_free(token);
252 return (1);
254 sock->ssl_cert_auto_accept = TRUE;
255 sock->use_tls_sni = TRUE;
256 sock_set_nonblocking_mode(sock, FALSE);
257 gint timeout_secs = prefs_common_get_prefs()->io_timeout_secs;
258 debug_print("Socket timeout: %i sec(s)\n", timeout_secs);
259 sock_set_io_timeout(timeout_secs);
260 sock->gnutls_priority = GNUTLS_PRIORITY;
261 if (ssl_init_socket(sock) == FALSE) {
262 log_message(LOG_PROTOCOL, _("OAuth2 TLS connection error\n"));
263 g_free(token);
264 return (1);
267 refresh_token = g_malloc(OAUTH2BUFSIZE+1);
268 access_token = g_malloc(OAUTH2BUFSIZE+1);
269 request = g_malloc(OAUTH2BUFSIZE+1);
270 response = g_malloc0(OAUTH2BUFSIZE+1);
272 if(OAUTH2Data->custom_client_id)
273 client_id = g_strdup(OAUTH2Data->custom_client_id);
274 else
275 client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
277 body = g_strconcat ("client_id=", client_id, "&code=", token, NULL);
278 debug_print("Body: %s\n", body);
279 g_free(token);
281 if(OAUTH2info[i][OA2_CLIENT_SECRET][0]){
282 //Only allow custom client secret if the service provider would usually expect a client secret
283 if(OAUTH2Data->custom_client_secret)
284 client_secret = g_strdup(OAUTH2Data->custom_client_secret);
285 else
286 client_secret = oauth2_decode(OAUTH2info[i][OA2_CLIENT_SECRET]);
287 uri = g_uri_escape_string (client_secret, NULL, FALSE);
288 tmp = g_strconcat (body, "&client_secret=", uri, NULL);
289 g_free(body);
290 g_free(uri);
291 body = tmp;
292 }else{
293 client_secret = g_strconcat ("", NULL);
296 if(OAUTH2info[i][OA2_REDIRECT_URI][0]) {
297 tmp = g_strconcat(body, "&redirect_uri=", OAUTH2info[i][OA2_REDIRECT_URI], NULL);
298 g_free(body);
299 body = tmp;
301 if(OAUTH2info[i][OA2_GRANT_TYPE_ACCESS][0]) {
302 tmp = g_strconcat(body, "&grant_type=", OAUTH2info[i][OA2_GRANT_TYPE_ACCESS], NULL);
303 g_free(body);
304 body = tmp;
306 if(OAUTH2info[i][OA2_TENANT][0]) {
307 tmp = g_strconcat(body, "&tenant=", OAUTH2info[i][OA2_TENANT], NULL);
308 g_free(body);
309 body = tmp;
311 if(OAUTH2info[i][OA2_SCOPE_FOR_ACCESS][0]) {
312 tmp = g_strconcat(body, "&scope=", OAUTH2info[i][OA2_SCOPE_FOR_ACCESS], NULL);
313 g_free(body);
314 body = tmp;
316 if(OAUTH2info[i][OA2_STATE][0]) {
317 tmp = g_strconcat(body, "&state=", OAUTH2info[i][OA2_STATE], NULL);
318 g_free(body);
319 body = tmp;
322 if(OAUTH2info[i][OA2_HEADER_AUTH_BASIC][0]){
323 tmp_hd = g_strconcat(client_id, ":", client_secret, NULL);
324 tmp_hd_encoded = g_base64_encode (tmp_hd, strlen(tmp_hd));
325 header = g_strconcat ("Authorization: Basic ", tmp_hd_encoded, NULL);
326 g_free(tmp_hd_encoded);
327 g_free(tmp_hd);
328 }else{
329 header = g_strconcat ("", NULL);
332 debug_print("Complete body: %s\n", body);
333 oauth2_post_request (request, OAUTH2info[i][OA2_BASE_URL], OAUTH2info[i][OA2_ACCESS_RESOURCE], header, body);
334 ret = oauth2_contact_server (sock, request, response);
336 if(oauth2_filter_access (response, access_token, &expiry) == 0){
337 OAUTH2Data->access_token = g_strdup(access_token);
338 OAUTH2Data->expiry = expiry;
339 OAUTH2Data->expiry_str = g_strdup_printf ("%i", expiry);
340 ret = 0;
341 log_message(LOG_PROTOCOL, _("OAuth2 access token obtained\n"));
342 }else{
343 log_message(LOG_PROTOCOL, _("OAuth2 access token not obtained\n"));
344 debug_print("OAuth2 - request: %s\n Response: %s", request, response);
345 ret = 1;
348 if(oauth2_filter_refresh (response, refresh_token) == 0){
349 OAUTH2Data->refresh_token = g_strdup(refresh_token);
350 log_message(LOG_PROTOCOL, _("OAuth2 refresh token obtained\n"));
351 }else{
352 log_message(LOG_PROTOCOL, _("OAuth2 refresh token not obtained\n"));
355 sock_close(sock, TRUE);
356 g_free(body);
357 g_free(header);
358 g_free(request);
359 g_free(response);
360 g_free(client_id);
361 g_free(client_secret);
362 g_free(access_token);
363 g_free(refresh_token);
365 return (ret);
368 gint oauth2_use_refresh_token (Oauth2Service provider, OAUTH2Data *OAUTH2Data)
371 gchar *request;
372 gchar *response;
373 gchar *body;
374 gchar *uri;
375 gchar *header;
376 gchar *tmp_hd, *tmp_hd_encoded;
377 gchar *access_token;
378 gchar *refresh_token;
379 gint expiry = 0;
380 gint ret;
381 SockInfo *sock;
382 gchar *client_id;
383 gchar *client_secret;
384 gchar *tmp;
385 gint i;
387 i = (int)provider - 1;
388 if (i < 0 || i > (OAUTH2AUTH_LAST-1))
389 return (1);
391 sock = sock_connect(OAUTH2info[i][OA2_BASE_URL], 443);
392 if (sock == NULL) {
393 log_message(LOG_PROTOCOL, _("OAuth2 connection error\n"));
394 return (1);
396 sock->ssl_cert_auto_accept = TRUE;
397 sock->use_tls_sni = TRUE;
398 sock_set_nonblocking_mode(sock, FALSE);
399 gint timeout_secs = prefs_common_get_prefs()->io_timeout_secs;
400 debug_print("Socket timeout: %i sec(s)\n", timeout_secs);
401 sock_set_io_timeout(timeout_secs);
402 sock->gnutls_priority = GNUTLS_PRIORITY;
403 if (ssl_init_socket(sock) == FALSE) {
404 log_message(LOG_PROTOCOL, _("OAuth2 TLS connection error\n"));
405 return (1);
408 access_token = g_malloc(OAUTH2BUFSIZE+1);
409 refresh_token = g_malloc(OAUTH2BUFSIZE+1);
410 request = g_malloc(OAUTH2BUFSIZE+1);
411 response = g_malloc(OAUTH2BUFSIZE+1);
413 if(OAUTH2Data->custom_client_id)
414 client_id = g_strdup(OAUTH2Data->custom_client_id);
415 else
416 client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
418 uri = g_uri_escape_string (client_id, NULL, FALSE);
419 body = g_strconcat ("client_id=", uri, "&refresh_token=", OAUTH2Data->refresh_token, NULL);
420 g_free(uri);
422 if(OAUTH2info[i][OA2_CLIENT_SECRET][0]){
423 //Only allow custom client secret if the service provider would usually expect a client secret
424 if(OAUTH2Data->custom_client_secret)
425 client_secret = g_strdup(OAUTH2Data->custom_client_secret);
426 else
427 client_secret = oauth2_decode(OAUTH2info[i][OA2_CLIENT_SECRET]);
428 uri = g_uri_escape_string (client_secret, NULL, FALSE);
429 tmp = g_strconcat (body, "&client_secret=", uri, NULL);
430 g_free(body);
431 g_free(uri);
432 body = tmp;
433 }else{
434 client_secret = g_strconcat ("", NULL);
437 if(OAUTH2info[i][OA2_GRANT_TYPE_REFRESH][0]) {
438 uri = g_uri_escape_string (OAUTH2info[i][OA2_GRANT_TYPE_REFRESH], NULL, FALSE);
439 tmp = g_strconcat (body, "&grant_type=", uri, NULL);
440 g_free(body);
441 g_free(uri);
442 body = tmp;
444 if(OAUTH2info[i][OA2_SCOPE_FOR_ACCESS][0]) {
445 uri = g_uri_escape_string (OAUTH2info[i][OA2_SCOPE_FOR_ACCESS], NULL, FALSE);
446 tmp = g_strconcat (body, "&scope=", uri, NULL);
447 g_free(body);
448 g_free(uri);
449 body = tmp;
451 if(OAUTH2info[i][OA2_STATE][0]) {
452 uri = g_uri_escape_string (OAUTH2info[i][OA2_STATE], NULL, FALSE);
453 tmp = g_strconcat (body, "&state=", uri, NULL);
454 g_free(body);
455 g_free(uri);
456 body = tmp;
459 if(OAUTH2info[i][OA2_HEADER_AUTH_BASIC][0]){
460 tmp_hd = g_strconcat(client_id, ":", client_secret, NULL);
461 tmp_hd_encoded = g_base64_encode (tmp_hd, strlen(tmp_hd));
462 header = g_strconcat ("Authorization: Basic ", tmp_hd_encoded, NULL);
463 g_free(tmp_hd_encoded);
464 g_free(tmp_hd);
465 }else{
466 header = g_strconcat ("", NULL);
469 oauth2_post_request (request, OAUTH2info[i][OA2_BASE_URL], OAUTH2info[i][OA2_REFRESH_RESOURCE], header, body);
470 ret = oauth2_contact_server (sock, request, response);
472 if(oauth2_filter_access (response, access_token, &expiry) == 0){
473 OAUTH2Data->access_token = g_strdup(access_token);
474 OAUTH2Data->expiry = expiry;
475 OAUTH2Data->expiry_str = g_strdup_printf ("%i", expiry);
476 ret = 0;
477 log_message(LOG_PROTOCOL, _("OAuth2 access token obtained\n"));
478 }else{
479 log_message(LOG_PROTOCOL, _("OAuth2 access token not obtained\n"));
480 debug_print("OAuth2 - request: %s\n Response: %s", request, response);
481 ret = 1;
484 if (oauth2_filter_refresh (response, refresh_token) == 0) {
485 OAUTH2Data->refresh_token = g_strdup(refresh_token);
486 log_message(LOG_PROTOCOL, _("OAuth2 replacement refresh token provided\n"));
487 } else
488 log_message(LOG_PROTOCOL, _("OAuth2 replacement refresh token not provided\n"));
490 debug_print("OAuth2 - access token: %s\n", access_token);
491 debug_print("OAuth2 - access token expiry: %i\n", expiry);
493 sock_close(sock, TRUE);
494 g_free(body);
495 g_free(header);
496 g_free(request);
497 g_free(response);
498 g_free(client_id);
499 g_free(client_secret);
500 g_free(access_token);
501 g_free(refresh_token);
503 return (ret);
506 static gint oauth2_contact_server (SockInfo *sock, gchar *request, gchar *response)
508 gint len;
509 gint ret;
510 gchar *token;
511 gint toread = OAUTH2BUFSIZE;
512 time_t startplus = time(NULL);
513 gchar *tmp;
514 len = strlen(request);
516 gint timeout_secs = prefs_common_get_prefs()->io_timeout_secs;
517 startplus += timeout_secs;
519 if (sock_write (sock, request, len+1) < 0) {
520 log_message(LOG_PROTOCOL, _("OAuth2 socket write error\n"));
521 return (1);
524 token = g_strconcat ("", NULL);
525 do {
527 ret = sock_read (sock, response, OAUTH2BUFSIZE);
528 if (ret < 0 && errno == EAGAIN)
529 continue;
530 if (ret < 0)
531 break;
532 if (ret == 0)
533 break;
535 toread -= ret;
536 tmp = g_strconcat(token, response, NULL);
537 g_free(token);
538 token = tmp;
539 } while ((toread > 0) && (time(NULL) < startplus));
541 if(time(NULL) >= startplus)
542 log_message(LOG_PROTOCOL, _("OAuth2 socket timeout error\n"));
544 g_free(token);
546 return (0);
549 gint oauth2_authorisation_url (Oauth2Service provider, gchar **url, const gchar *custom_client_id)
551 gint i;
552 gchar *client_id = NULL;
553 gchar *tmp;
554 gchar *uri;
556 i = (int)provider - 1;
557 if (i < 0 || i > (OAUTH2AUTH_LAST-1))
558 return (1);
560 if(!custom_client_id)
561 client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
563 uri = g_uri_escape_string (custom_client_id ? custom_client_id : client_id, NULL, FALSE);
564 *url = g_strconcat ("https://", OAUTH2info[i][OA2_BASE_URL],OAUTH2info[i][OA2_AUTH_RESOURCE], "?client_id=",
565 uri, NULL);
566 g_free(uri);
567 if (client_id)
568 g_free(client_id);
570 if(OAUTH2info[i][OA2_REDIRECT_URI][0]) {
571 uri = g_uri_escape_string (OAUTH2info[i][OA2_REDIRECT_URI], NULL, FALSE);
572 tmp = g_strconcat (*url, "&redirect_uri=", uri, NULL);
573 g_free(*url);
574 *url = tmp;
575 g_free(uri);
578 if(OAUTH2info[i][OA2_RESPONSE_TYPE][0]) {
579 uri = g_uri_escape_string (OAUTH2info[i][OA2_RESPONSE_TYPE], NULL, FALSE);
580 tmp = g_strconcat (*url, "&response_type=", uri, NULL);
581 g_free(*url);
582 *url = tmp;
583 g_free(uri);
585 if(OAUTH2info[i][OA2_SCOPE_FOR_AUTH][0]) {
586 uri = g_uri_escape_string (OAUTH2info[i][OA2_SCOPE_FOR_AUTH], NULL, FALSE);
587 tmp = g_strconcat (*url, "&scope=", uri, NULL);
588 g_free(*url);
589 *url = tmp;
590 g_free(uri);
592 if(OAUTH2info[i][OA2_TENANT][0]) {
593 uri = g_uri_escape_string (OAUTH2info[i][OA2_TENANT], NULL, FALSE);
594 tmp = g_strconcat (*url, "&tenant=", uri, NULL);
595 g_free(*url);
596 *url = tmp;
597 g_free(uri);
599 if(OAUTH2info[i][OA2_RESPONSE_MODE][0]) {
600 uri = g_uri_escape_string (OAUTH2info[i][OA2_RESPONSE_MODE], NULL, FALSE);
601 tmp = g_strconcat (*url, "&response_mode=", uri, NULL);
602 g_free(*url);
603 *url = tmp;
604 g_free(uri);
606 if(OAUTH2info[i][OA2_STATE][0]) {
607 uri = g_uri_escape_string (OAUTH2info[i][OA2_STATE], NULL, FALSE);
608 tmp = g_strconcat (*url, "&state=", uri, NULL);
609 g_free(*url);
610 *url = tmp;
611 g_free(uri);
614 return (0);
617 gint oauth2_check_passwds (PrefsAccount *ac_prefs)
619 gchar *uid = g_strdup_printf("%d", ac_prefs->account_id);
620 gint expiry;
621 OAUTH2Data *OAUTH2Data = g_malloc(sizeof(* OAUTH2Data));
622 gint ret;
623 gchar *acc;
625 oauth2_init (OAUTH2Data);
627 OAUTH2Data->custom_client_id = ac_prefs->oauth2_client_id;
628 OAUTH2Data->custom_client_secret = ac_prefs->oauth2_client_secret;
630 if (passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_EXPIRY)) {
631 acc = passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_EXPIRY);
632 expiry = atoi(acc);
633 g_free(acc);
634 if (expiry > (g_get_real_time () / G_USEC_PER_SEC)) {
635 g_free(OAUTH2Data);
636 log_message(LOG_PROTOCOL, _("OAuth2 access token still fresh\n"));
637 g_free(uid);
638 return (0);
642 if (passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_REFRESH)) {
643 log_message(LOG_PROTOCOL, _("OAuth2 obtaining access token using refresh token\n"));
644 OAUTH2Data->refresh_token = passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_REFRESH);
645 ret = oauth2_use_refresh_token (ac_prefs->oauth2_provider, OAUTH2Data);
646 } else if (passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_AUTH)) {
647 log_message(LOG_PROTOCOL, _("OAuth2 trying for fresh access token with authorization code\n"));
648 acc = passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_AUTH);
649 ret = oauth2_obtain_tokens (ac_prefs->oauth2_provider, OAUTH2Data, acc);
650 g_free(acc);
651 } else
652 ret = 1;
654 if (ret)
655 log_message(LOG_PROTOCOL, _("OAuth2 access token not obtained\n"));
656 else {
657 if (ac_prefs->imap_auth_type == IMAP_AUTH_OAUTH2 ||
658 (ac_prefs->use_pop_auth && ac_prefs->pop_auth_type == POPAUTH_OAUTH2))
659 passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_RECV, OAUTH2Data->access_token, FALSE);
660 if (ac_prefs->use_smtp_auth && ac_prefs->smtp_auth_type == SMTPAUTH_OAUTH2)
661 passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_SEND, OAUTH2Data->access_token, FALSE);
662 passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_EXPIRY, OAUTH2Data->expiry_str, FALSE);
663 //Some providers issue replacement refresh tokens with each access token. Re-store whether replaced or not.
664 passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_REFRESH, OAUTH2Data->refresh_token, FALSE);
665 log_message(LOG_PROTOCOL, _("OAuth2 access and refresh token updated\n"));
668 g_free(OAUTH2Data);
669 g_free(uid);
671 return (ret);
674 /* returns allocated string which must be freed */
675 guchar* oauth2_decode(const gchar *in)
677 guchar *tmp;
678 gsize len;
680 tmp = g_base64_decode(in, &len);
681 passcrypt_decrypt(tmp, len);
682 return tmp;
685 /* For testing */
686 void oauth2_encode(const gchar *in)
688 guchar *tmp = g_strdup(in);
689 guchar *tmp2 = g_strdup(in);
690 gchar *result;
691 gsize len = strlen(in);
693 passcrypt_encrypt(tmp, len);
694 result = g_base64_encode(tmp, len);
695 tmp2 = oauth2_decode(result);
697 log_message(LOG_PROTOCOL, _("OAuth2 original: %s\n"), in);
698 log_message(LOG_PROTOCOL, _("OAuth2 encoded: %s\n"), result);
699 log_message(LOG_PROTOCOL, _("OAuth2 decoded: %s\n\n"), tmp2);
701 g_free(tmp);
702 g_free(tmp2);
703 g_free(result);
706 gint oauth2_init (OAUTH2Data *OAUTH2Data)
708 OAUTH2Data->refresh_token = NULL;
709 OAUTH2Data->access_token = NULL;
710 OAUTH2Data->expiry_str = NULL;
711 OAUTH2Data->expiry = 0;
712 OAUTH2Data->custom_client_id = NULL;
713 OAUTH2Data->custom_client_secret = NULL;
715 return (0);
718 #endif /* USE_GNUTLS */