improve readability of symbol list
[claws.git] / src / oauth2.c
blobeee796d1052d5cffd938e45d3ac9945202b89b17
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2021-2022 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 "oauth2.h"
39 #include "md5.h"
40 #include "utils.h"
41 #include "log.h"
42 #include "time.h"
43 #include "common/passcrypt.h"
45 //Yahoo requires token requests to send POST header Authorization: Basic
46 //where the password is Base64 encoding of client_id:client_secret
48 static gchar *OAUTH2info[4][17]={
49 {"accounts.google.com",
50 "",
51 ".",
52 "http://127.0.0.1:8888",
53 "/o/oauth2/auth",
54 "/o/oauth2/token",
55 "/o/oauth2/token",
56 "code",
57 "https://mail.google.com",
58 "authorization_code",
59 "refresh_token",
60 "",
61 "",
62 "",
63 "",
64 "",
65 ""},
66 {"login.microsoftonline.com",
67 "",
68 "",
69 "http://127.0.0.1:8888",
70 "/common/oauth2/v2.0/authorize",
71 "/common/oauth2/v2.0/token",
72 "/common/oauth2/v2.0/token",
73 "code",
74 "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
75 "authorization_code",
76 "refresh_token",
77 "common",
78 "",
79 "offline",
80 "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
81 "query",
82 ""},
83 {"login.microsoftonline.com",
84 "",
85 "",
86 "http://127.0.0.1:8888",
87 "/common/oauth2/v2.0/authorize",
88 "/common/oauth2/v2.0/token",
89 "/common/oauth2/v2.0/token",
90 "code",
91 "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
92 "authorization_code",
93 "refresh_token",
94 "common",
95 "",
96 "offline",
97 "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
98 "query",
99 ""},
100 {"api.login.yahoo.com",
102 ".",
103 "oob",
104 "/oauth2/request_auth",
105 "/oauth2/get_token",
106 "/oauth2/get_token",
107 "code",
109 "authorization_code",
110 "refresh_token",
116 "1"}
119 static gchar *OAUTH2CodeMarker[5][2] = {
120 {"",""},
121 {"code=","&scope="},
122 {"code="," HTTP"},
123 {"code=","&session_state="},
124 {"yahoo_begin_mark","yahoo_end_mark"} /* Not used since token avalable to user to copy in browser window */
127 static gint oauth2_post_request (gchar *buf, gchar *host, gchar *resource, gchar *header, gchar *body);
128 static gint oauth2_filter_refresh (gchar *json, gchar *refresh_token);
129 static gint oauth2_filter_access (gchar *json, gchar *access_token, gint *expiry);
130 static gint oauth2_contact_server (SockInfo *sock, gchar *request, gchar *response);
133 static gint oauth2_post_request (gchar *buf, gchar *host, gchar *resource, gchar *header, gchar *body)
135 gint len;
137 len = strlen(body);
138 if (header[0])
139 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);
140 else
141 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);
144 static gint oauth2_filter_access (gchar *json, gchar *access_token, gint *expiry)
146 GMatchInfo *matchInfo;
147 GRegex *regex;
149 regex = g_regex_new ("\"access_token\": ?\"(.*?)\",?", 0, 0, NULL);
150 g_regex_match (regex, json, 0, &matchInfo);
151 if (g_match_info_matches (matchInfo))
152 g_stpcpy (access_token,g_match_info_fetch (matchInfo, 1));
153 else{
154 g_match_info_free (matchInfo);
155 return (-1);
158 g_match_info_free (matchInfo);
160 regex = g_regex_new ("\"expires_in\": ?([0-9]*),?", 0, 0, NULL);
161 g_regex_match (regex, json, 0, &matchInfo);
162 if (g_match_info_matches (matchInfo)){
163 // Reduce available token life to avoid attempting connections with (near) expired tokens
164 *expiry = (g_get_real_time () / G_USEC_PER_SEC) + atoi(g_match_info_fetch (matchInfo, 1)) - 120;
165 }else{
166 g_match_info_free (matchInfo);
167 return (-2);
170 g_match_info_free (matchInfo);
172 return(0);
175 static gint oauth2_filter_refresh (gchar *json, gchar *refresh_token)
177 GMatchInfo *matchInfo;
178 GRegex *regex;
180 regex = g_regex_new ("\"refresh_token\": ?\"(.*?)\",?", 0, 0, NULL);
181 g_regex_match (regex, json, 0, &matchInfo);
182 if (g_match_info_matches (matchInfo))
183 g_stpcpy (refresh_token,g_match_info_fetch (matchInfo, 1));
184 else{
185 g_match_info_free (matchInfo);
186 return (-1);
189 g_match_info_free (matchInfo);
191 return(0);
194 static gchar* oauth2_get_token_from_response(Oauth2Service provider, const gchar* response) {
195 gchar* token = NULL;
197 debug_print("Auth response: %s\n", response);
198 if (provider == OAUTH2AUTH_YAHOO) {
199 /* Providers which display auth token in browser for users to copy */
200 token = g_strdup(response);
201 } else {
202 gchar* start = g_strstr_len(response, strlen(response), OAUTH2CodeMarker[provider][0]);
203 if (start == NULL)
204 return NULL;
205 start += strlen(OAUTH2CodeMarker[provider][0]);
206 gchar* stop = g_strstr_len(response, strlen(response), OAUTH2CodeMarker[provider][1]);
207 if (stop == NULL)
208 return NULL;
209 token = g_strndup(start, stop - start);
212 return token;
215 int oauth2_obtain_tokens (Oauth2Service provider, OAUTH2Data *OAUTH2Data, const gchar *authcode)
217 gchar *request;
218 gchar *response;
219 gchar *body;
220 gchar *uri;
221 gchar *header;
222 gchar *tmp_hd, *tmp_hd_encoded;
223 gchar *access_token;
224 gchar *refresh_token;
225 gint expiry = 0;
226 gint ret;
227 SockInfo *sock;
228 gchar *client_id;
229 gchar *client_secret;
230 gchar *token = NULL;
231 gchar *tmp;
232 gint i;
234 i = (int)provider - 1;
235 if (i < 0 || i > (OAUTH2AUTH_LAST-1))
236 return (1);
238 token = oauth2_get_token_from_response(provider, authcode);
239 debug_print("Auth token: %s\n", token);
240 if (token == NULL) {
241 log_message(LOG_PROTOCOL, _("OAuth2 missing authorization code\n"));
242 return (1);
244 debug_print("Connect: %s:443\n", OAUTH2info[i][OA2_BASE_URL]);
245 sock = sock_connect(OAUTH2info[i][OA2_BASE_URL], 443);
246 if (sock == NULL) {
247 log_message(LOG_PROTOCOL, _("OAuth2 connection error\n"));
248 g_free(token);
249 return (1);
251 sock->ssl_cert_auto_accept = TRUE;
252 sock->use_tls_sni = TRUE;
253 sock_set_nonblocking_mode(sock, FALSE);
254 sock_set_io_timeout(10);
255 sock->gnutls_priority = "NORMAL:!VERS-SSL3.0:!VERS-TLS1.0:!VERS-TLS1.1";
256 if (ssl_init_socket(sock) == FALSE) {
257 log_message(LOG_PROTOCOL, _("OAuth2 TLS connection error\n"));
258 g_free(token);
259 return (1);
262 refresh_token = g_malloc(OAUTH2BUFSIZE+1);
263 access_token = g_malloc(OAUTH2BUFSIZE+1);
264 request = g_malloc(OAUTH2BUFSIZE+1);
265 response = g_malloc0(OAUTH2BUFSIZE+1);
267 if(OAUTH2Data->custom_client_id)
268 client_id = g_strdup(OAUTH2Data->custom_client_id);
269 else
270 client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
272 body = g_strconcat ("client_id=", client_id, "&code=", token, NULL);
273 debug_print("Body: %s\n", body);
274 g_free(token);
276 if(OAUTH2info[i][OA2_CLIENT_SECRET][0]){
277 //Only allow custom client secret if the service provider would usually expect a client secret
278 if(OAUTH2Data->custom_client_secret)
279 client_secret = g_strdup(OAUTH2Data->custom_client_secret);
280 else
281 client_secret = oauth2_decode(OAUTH2info[i][OA2_CLIENT_SECRET]);
282 uri = g_uri_escape_string (client_secret, NULL, FALSE);
283 tmp = g_strconcat (body, "&client_secret=", uri, NULL);
284 g_free(body);
285 g_free(uri);
286 body = tmp;
287 }else{
288 client_secret = g_strconcat ("", NULL);
291 if(OAUTH2info[i][OA2_REDIRECT_URI][0]) {
292 tmp = g_strconcat(body, "&redirect_uri=", OAUTH2info[i][OA2_REDIRECT_URI], NULL);
293 g_free(body);
294 body = tmp;
296 if(OAUTH2info[i][OA2_GRANT_TYPE_ACCESS][0]) {
297 tmp = g_strconcat(body, "&grant_type=", OAUTH2info[i][OA2_GRANT_TYPE_ACCESS], NULL);
298 g_free(body);
299 body = tmp;
301 if(OAUTH2info[i][OA2_TENANT][0]) {
302 tmp = g_strconcat(body, "&tenant=", OAUTH2info[i][OA2_TENANT], NULL);
303 g_free(body);
304 body = tmp;
306 if(OAUTH2info[i][OA2_SCOPE_FOR_ACCESS][0]) {
307 tmp = g_strconcat(body, "&scope=", OAUTH2info[i][OA2_SCOPE_FOR_ACCESS], NULL);
308 g_free(body);
309 body = tmp;
311 if(OAUTH2info[i][OA2_STATE][0]) {
312 tmp = g_strconcat(body, "&state=", OAUTH2info[i][OA2_STATE], NULL);
313 g_free(body);
314 body = tmp;
317 if(OAUTH2info[i][OA2_HEADER_AUTH_BASIC][0]){
318 tmp_hd = g_strconcat(client_id, ":", client_secret, NULL);
319 tmp_hd_encoded = g_base64_encode (tmp_hd, strlen(tmp_hd));
320 header = g_strconcat ("Authorization: Basic ", tmp_hd_encoded, NULL);
321 g_free(tmp_hd_encoded);
322 g_free(tmp_hd);
323 }else{
324 header = g_strconcat ("", NULL);
327 debug_print("Complete body: %s\n", body);
328 oauth2_post_request (request, OAUTH2info[i][OA2_BASE_URL], OAUTH2info[i][OA2_ACCESS_RESOURCE], header, body);
329 ret = oauth2_contact_server (sock, request, response);
331 if(oauth2_filter_access (response, access_token, &expiry) == 0){
332 OAUTH2Data->access_token = g_strdup(access_token);
333 OAUTH2Data->expiry = expiry;
334 OAUTH2Data->expiry_str = g_strdup_printf ("%i", expiry);
335 ret = 0;
336 log_message(LOG_PROTOCOL, _("OAuth2 access token obtained\n"));
337 }else{
338 log_message(LOG_PROTOCOL, _("OAuth2 access token not obtained\n"));
339 debug_print("OAuth2 - request: %s\n Response: %s", request, response);
340 ret = 1;
343 if(oauth2_filter_refresh (response, refresh_token) == 0){
344 OAUTH2Data->refresh_token = g_strdup(refresh_token);
345 log_message(LOG_PROTOCOL, _("OAuth2 refresh token obtained\n"));
346 }else{
347 log_message(LOG_PROTOCOL, _("OAuth2 refresh token not obtained\n"));
350 sock_close(sock, TRUE);
351 g_free(body);
352 g_free(header);
353 g_free(request);
354 g_free(response);
355 g_free(client_id);
356 g_free(client_secret);
357 g_free(access_token);
358 g_free(refresh_token);
360 return (ret);
363 gint oauth2_use_refresh_token (Oauth2Service provider, OAUTH2Data *OAUTH2Data)
366 gchar *request;
367 gchar *response;
368 gchar *body;
369 gchar *uri;
370 gchar *header;
371 gchar *tmp_hd, *tmp_hd_encoded;
372 gchar *access_token;
373 gchar *refresh_token;
374 gint expiry = 0;
375 gint ret;
376 SockInfo *sock;
377 gchar *client_id;
378 gchar *client_secret;
379 gchar *tmp;
380 gint i;
382 i = (int)provider - 1;
383 if (i < 0 || i > (OAUTH2AUTH_LAST-1))
384 return (1);
386 sock = sock_connect(OAUTH2info[i][OA2_BASE_URL], 443);
387 if (sock == NULL) {
388 log_message(LOG_PROTOCOL, _("OAuth2 connection error\n"));
389 return (1);
391 sock->ssl_cert_auto_accept = TRUE;
392 sock->use_tls_sni = TRUE;
393 sock_set_nonblocking_mode(sock, FALSE);
394 sock_set_io_timeout(10);
395 sock->gnutls_priority = "NORMAL:!VERS-SSL3.0:!VERS-TLS1.0:!VERS-TLS1.1";
396 if (ssl_init_socket(sock) == FALSE) {
397 log_message(LOG_PROTOCOL, _("OAuth2 TLS connection error\n"));
398 return (1);
401 access_token = g_malloc(OAUTH2BUFSIZE+1);
402 refresh_token = g_malloc(OAUTH2BUFSIZE+1);
403 request = g_malloc(OAUTH2BUFSIZE+1);
404 response = g_malloc(OAUTH2BUFSIZE+1);
406 if(OAUTH2Data->custom_client_id)
407 client_id = g_strdup(OAUTH2Data->custom_client_id);
408 else
409 client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
411 uri = g_uri_escape_string (client_id, NULL, FALSE);
412 body = g_strconcat ("client_id=", uri, "&refresh_token=", OAUTH2Data->refresh_token, NULL);
413 g_free(uri);
415 if(OAUTH2info[i][OA2_CLIENT_SECRET][0]){
416 //Only allow custom client secret if the service provider would usually expect a client secret
417 if(OAUTH2Data->custom_client_secret)
418 client_secret = g_strdup(OAUTH2Data->custom_client_secret);
419 else
420 client_secret = oauth2_decode(OAUTH2info[i][OA2_CLIENT_SECRET]);
421 uri = g_uri_escape_string (client_secret, NULL, FALSE);
422 tmp = g_strconcat (body, "&client_secret=", uri, NULL);
423 g_free(body);
424 g_free(uri);
425 body = tmp;
426 }else{
427 client_secret = g_strconcat ("", NULL);
430 if(OAUTH2info[i][OA2_GRANT_TYPE_REFRESH][0]) {
431 uri = g_uri_escape_string (OAUTH2info[i][OA2_GRANT_TYPE_REFRESH], NULL, FALSE);
432 tmp = g_strconcat (body, "&grant_type=", uri, NULL);
433 g_free(body);
434 g_free(uri);
435 body = tmp;
437 if(OAUTH2info[i][OA2_SCOPE_FOR_ACCESS][0]) {
438 uri = g_uri_escape_string (OAUTH2info[i][OA2_SCOPE_FOR_ACCESS], NULL, FALSE);
439 tmp = g_strconcat (body, "&scope=", uri, NULL);
440 g_free(body);
441 g_free(uri);
442 body = tmp;
444 if(OAUTH2info[i][OA2_STATE][0]) {
445 uri = g_uri_escape_string (OAUTH2info[i][OA2_STATE], NULL, FALSE);
446 tmp = g_strconcat (body, "&state=", uri, NULL);
447 g_free(body);
448 g_free(uri);
449 body = tmp;
452 if(OAUTH2info[i][OA2_HEADER_AUTH_BASIC][0]){
453 tmp_hd = g_strconcat(client_id, ":", client_secret, NULL);
454 tmp_hd_encoded = g_base64_encode (tmp_hd, strlen(tmp_hd));
455 header = g_strconcat ("Authorization: Basic ", tmp_hd_encoded, NULL);
456 g_free(tmp_hd_encoded);
457 g_free(tmp_hd);
458 }else{
459 header = g_strconcat ("", NULL);
462 oauth2_post_request (request, OAUTH2info[i][OA2_BASE_URL], OAUTH2info[i][OA2_REFRESH_RESOURCE], header, body);
463 ret = oauth2_contact_server (sock, request, response);
465 if(oauth2_filter_access (response, access_token, &expiry) == 0){
466 OAUTH2Data->access_token = g_strdup(access_token);
467 OAUTH2Data->expiry = expiry;
468 OAUTH2Data->expiry_str = g_strdup_printf ("%i", expiry);
469 ret = 0;
470 log_message(LOG_PROTOCOL, _("OAuth2 access token obtained\n"));
471 }else{
472 log_message(LOG_PROTOCOL, _("OAuth2 access token not obtained\n"));
473 debug_print("OAuth2 - request: %s\n Response: %s", request, response);
474 ret = 1;
477 if (oauth2_filter_refresh (response, refresh_token) == 0) {
478 OAUTH2Data->refresh_token = g_strdup(refresh_token);
479 log_message(LOG_PROTOCOL, _("OAuth2 replacement refresh token provided\n"));
480 } else
481 log_message(LOG_PROTOCOL, _("OAuth2 replacement refresh token not provided\n"));
483 debug_print("OAuth2 - access token: %s\n", access_token);
484 debug_print("OAuth2 - access token expiry: %i\n", expiry);
486 sock_close(sock, TRUE);
487 g_free(body);
488 g_free(header);
489 g_free(request);
490 g_free(response);
491 g_free(client_id);
492 g_free(client_secret);
493 g_free(access_token);
494 g_free(refresh_token);
496 return (ret);
499 static gint oauth2_contact_server (SockInfo *sock, gchar *request, gchar *response)
501 gint len;
502 gint ret;
503 gchar *token;
504 gint toread = OAUTH2BUFSIZE;
505 time_t startplus = time(NULL);
506 gchar *tmp;
507 len = strlen(request);
509 startplus += 10;
511 if (sock_write (sock, request, len+1) < 0) {
512 log_message(LOG_PROTOCOL, _("OAuth2 socket write error\n"));
513 return (1);
516 token = g_strconcat ("", NULL);
517 do {
519 ret = sock_read (sock, response, OAUTH2BUFSIZE);
520 if (ret < 0 && errno == EAGAIN)
521 continue;
522 if (ret < 0)
523 break;
524 if (ret == 0)
525 break;
527 toread -= ret;
528 tmp = g_strconcat(token, response, NULL);
529 g_free(token);
530 token = tmp;
531 } while ((toread > 0) && (time(NULL) < startplus));
533 if(time(NULL) >= startplus)
534 log_message(LOG_PROTOCOL, _("OAuth2 socket timeout error\n"));
536 g_free(token);
538 return (0);
541 gint oauth2_authorisation_url (Oauth2Service provider, gchar **url, const gchar *custom_client_id)
543 gint i;
544 gchar *client_id = NULL;
545 gchar *tmp;
546 gchar *uri;
548 i = (int)provider - 1;
549 if (i < 0 || i > (OAUTH2AUTH_LAST-1))
550 return (1);
552 if(!custom_client_id)
553 client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
555 uri = g_uri_escape_string (custom_client_id ? custom_client_id : client_id, NULL, FALSE);
556 *url = g_strconcat ("https://", OAUTH2info[i][OA2_BASE_URL],OAUTH2info[i][OA2_AUTH_RESOURCE], "?client_id=",
557 uri, NULL);
558 g_free(uri);
559 if (client_id)
560 g_free(client_id);
562 if(OAUTH2info[i][OA2_REDIRECT_URI][0]) {
563 uri = g_uri_escape_string (OAUTH2info[i][OA2_REDIRECT_URI], NULL, FALSE);
564 tmp = g_strconcat (*url, "&redirect_uri=", uri, NULL);
565 g_free(*url);
566 *url = tmp;
567 g_free(uri);
570 if(OAUTH2info[i][OA2_RESPONSE_TYPE][0]) {
571 uri = g_uri_escape_string (OAUTH2info[i][OA2_RESPONSE_TYPE], NULL, FALSE);
572 tmp = g_strconcat (*url, "&response_type=", uri, NULL);
573 g_free(*url);
574 *url = tmp;
575 g_free(uri);
577 if(OAUTH2info[i][OA2_SCOPE_FOR_AUTH][0]) {
578 uri = g_uri_escape_string (OAUTH2info[i][OA2_SCOPE_FOR_AUTH], NULL, FALSE);
579 tmp = g_strconcat (*url, "&scope=", uri, NULL);
580 g_free(*url);
581 *url = tmp;
582 g_free(uri);
584 if(OAUTH2info[i][OA2_TENANT][0]) {
585 uri = g_uri_escape_string (OAUTH2info[i][OA2_TENANT], NULL, FALSE);
586 tmp = g_strconcat (*url, "&tenant=", uri, NULL);
587 g_free(*url);
588 *url = tmp;
589 g_free(uri);
591 if(OAUTH2info[i][OA2_RESPONSE_MODE][0]) {
592 uri = g_uri_escape_string (OAUTH2info[i][OA2_RESPONSE_MODE], NULL, FALSE);
593 tmp = g_strconcat (*url, "&response_mode=", uri, NULL);
594 g_free(*url);
595 *url = tmp;
596 g_free(uri);
598 if(OAUTH2info[i][OA2_STATE][0]) {
599 uri = g_uri_escape_string (OAUTH2info[i][OA2_STATE], NULL, FALSE);
600 tmp = g_strconcat (*url, "&state=", uri, NULL);
601 g_free(*url);
602 *url = tmp;
603 g_free(uri);
606 return (0);
609 gint oauth2_check_passwds (PrefsAccount *ac_prefs)
611 gchar *uid = g_strdup_printf("%d", ac_prefs->account_id);
612 gint expiry;
613 OAUTH2Data *OAUTH2Data = g_malloc(sizeof(* OAUTH2Data));
614 gint ret;
615 gchar *acc;
617 oauth2_init (OAUTH2Data);
619 OAUTH2Data->custom_client_id = ac_prefs->oauth2_client_id;
620 OAUTH2Data->custom_client_secret = ac_prefs->oauth2_client_secret;
622 if(passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_EXPIRY)) {
623 acc = passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_EXPIRY);
624 expiry = atoi(acc);
625 g_free(acc);
626 if (expiry > (g_get_real_time () / G_USEC_PER_SEC)){
627 g_free(OAUTH2Data);
628 log_message(LOG_PROTOCOL, _("OAuth2 access token still fresh\n"));
629 g_free(uid);
630 return (0);
634 if(passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_REFRESH)) {
635 log_message(LOG_PROTOCOL, _("OAuth2 obtaining access token using refresh token\n"));
636 OAUTH2Data->refresh_token = passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_REFRESH);
637 ret = oauth2_use_refresh_token (ac_prefs->oauth2_provider, OAUTH2Data);
638 }else if (passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_AUTH)) {
639 log_message(LOG_PROTOCOL, _("OAuth2 trying for fresh access token with authorization code\n"));
640 acc = passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_AUTH);
641 ret = oauth2_obtain_tokens (ac_prefs->oauth2_provider, OAUTH2Data, acc);
642 g_free(acc);
643 }else{
644 ret = 1;
647 if (ret){
648 log_message(LOG_PROTOCOL, _("OAuth2 access token not obtained\n"));
649 }else{
650 passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_RECV, OAUTH2Data->access_token, FALSE);
651 if (ac_prefs->use_smtp_auth && ac_prefs->smtp_auth_type == SMTPAUTH_OAUTH2)
652 passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_SEND, OAUTH2Data->access_token, FALSE);
653 passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_EXPIRY, OAUTH2Data->expiry_str, FALSE);
654 //Some providers issue replacement refresh tokens with each access token. Re-store whether replaced or not.
655 passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_REFRESH, OAUTH2Data->refresh_token, FALSE);
656 log_message(LOG_PROTOCOL, _("OAuth2 access and refresh token updated\n"));
659 g_free(OAUTH2Data);
660 g_free(uid);
662 return (ret);
665 /* returns allocated string which must be freed */
666 guchar* oauth2_decode(const gchar *in)
668 guchar *tmp;
669 gsize len;
671 tmp = g_base64_decode(in, &len);
672 passcrypt_decrypt(tmp, len);
673 return tmp;
676 /* For testing */
677 void oauth2_encode(const gchar *in)
679 guchar *tmp = g_strdup(in);
680 guchar *tmp2 = g_strdup(in);
681 gchar *result;
682 gsize len = strlen(in);
684 passcrypt_encrypt(tmp, len);
685 result = g_base64_encode(tmp, len);
686 tmp2 = oauth2_decode(result);
688 log_message(LOG_PROTOCOL, _("OAuth2 original: %s\n"), in);
689 log_message(LOG_PROTOCOL, _("OAuth2 encoded: %s\n"), result);
690 log_message(LOG_PROTOCOL, _("OAuth2 decoded: %s\n\n"), tmp2);
692 g_free(tmp);
693 g_free(tmp2);
694 g_free(result);
697 gint oauth2_init (OAUTH2Data *OAUTH2Data)
699 OAUTH2Data->refresh_token = NULL;
700 OAUTH2Data->access_token = NULL;
701 OAUTH2Data->expiry_str = NULL;
702 OAUTH2Data->expiry = 0;
703 OAUTH2Data->custom_client_id = NULL;
704 OAUTH2Data->custom_client_secret = NULL;
706 return (0);
709 #endif /* USE_GNUTLS */