oauth2: show body for every HTTP request
[claws.git] / src / oauth2.c
blobbb15603cb5750e07c6c60899f7c886ed7753154d
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);
135 static gint oauth2_post_request (gchar *buf, gchar *host, gchar *resource, gchar *header, gchar *body)
137 gint len;
139 debug_print("Complete body: %s\n", body);
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 static gchar *oauth2_contact_server(SockInfo *sock, const gchar *request)
220 gboolean got_some_error, timeout;
221 gint ret;
222 char buf[1024];
223 GString *response = g_string_sized_new(sizeof(buf));
224 time_t end_time = time(NULL);
226 end_time += prefs_common_get_prefs()->io_timeout_secs;
228 if (!response)
229 return NULL;
231 if (sock_write(sock, request, strlen(request)) < 0) {
232 log_message(LOG_PROTOCOL, _("OAuth2 socket write error\n"));
233 return NULL;
236 do {
237 ret = sock_read(sock, buf, sizeof(buf) - 1);
238 got_some_error = ret < 0;
239 timeout = time(NULL) > end_time;
241 if (timeout)
242 break;
244 if (ret < 0 && errno == EAGAIN)
245 continue;
247 if (got_some_error)
248 break;
250 if (ret) {
251 buf[ret] = '\0';
252 g_string_append_len(response, buf, ret);
254 } while (ret);
256 if (timeout)
257 log_message(LOG_PROTOCOL, _("OAuth2 socket timeout error\n"));
259 return g_string_free(response, got_some_error || timeout);
262 int oauth2_obtain_tokens (Oauth2Service provider, OAUTH2Data *OAUTH2Data, const gchar *authcode)
264 gchar *request;
265 gchar *response;
266 gchar *body;
267 gchar *uri;
268 gchar *header;
269 gchar *tmp_hd, *tmp_hd_encoded;
270 gchar *access_token;
271 gchar *refresh_token;
272 gint expiry = 0;
273 gint ret;
274 SockInfo *sock;
275 gchar *client_id;
276 gchar *client_secret;
277 gchar *token = NULL;
278 gchar *tmp;
279 gint i;
281 i = (int)provider - 1;
282 if (i < 0 || i > (OAUTH2AUTH_LAST-1))
283 return (1);
285 token = oauth2_get_token_from_response(provider, authcode);
286 debug_print("Auth token: %s\n", token);
287 if (token == NULL) {
288 log_message(LOG_PROTOCOL, _("OAuth2 missing authorization code\n"));
289 return (1);
291 debug_print("Connect: %s:443\n", OAUTH2info[i][OA2_BASE_URL]);
292 sock = sock_connect(OAUTH2info[i][OA2_BASE_URL], 443);
293 if (sock == NULL) {
294 log_message(LOG_PROTOCOL, _("OAuth2 connection error\n"));
295 g_free(token);
296 return (1);
298 sock->ssl_cert_auto_accept = TRUE;
299 sock->use_tls_sni = TRUE;
300 sock_set_nonblocking_mode(sock, FALSE);
301 gint timeout_secs = prefs_common_get_prefs()->io_timeout_secs;
302 debug_print("Socket timeout: %i sec(s)\n", timeout_secs);
303 sock_set_io_timeout(timeout_secs);
304 sock->gnutls_priority = GNUTLS_PRIORITY;
305 if (ssl_init_socket(sock) == FALSE) {
306 log_message(LOG_PROTOCOL, _("OAuth2 TLS connection error\n"));
307 g_free(token);
308 return (1);
311 refresh_token = g_malloc(OAUTH2BUFSIZE+1);
312 access_token = g_malloc(OAUTH2BUFSIZE+1);
313 request = g_malloc(OAUTH2BUFSIZE+1);
315 if(OAUTH2Data->custom_client_id)
316 client_id = g_strdup(OAUTH2Data->custom_client_id);
317 else
318 client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
320 body = g_strconcat ("client_id=", client_id, "&code=", token, NULL);
321 debug_print("Body: %s\n", body);
322 g_free(token);
324 if(OAUTH2info[i][OA2_CLIENT_SECRET][0]){
325 //Only allow custom client secret if the service provider would usually expect a client secret
326 if(OAUTH2Data->custom_client_secret)
327 client_secret = g_strdup(OAUTH2Data->custom_client_secret);
328 else
329 client_secret = oauth2_decode(OAUTH2info[i][OA2_CLIENT_SECRET]);
330 uri = g_uri_escape_string (client_secret, NULL, FALSE);
331 tmp = g_strconcat (body, "&client_secret=", uri, NULL);
332 g_free(body);
333 g_free(uri);
334 body = tmp;
335 }else{
336 client_secret = g_strconcat ("", NULL);
339 if(OAUTH2info[i][OA2_REDIRECT_URI][0]) {
340 tmp = g_strconcat(body, "&redirect_uri=", OAUTH2info[i][OA2_REDIRECT_URI], NULL);
341 g_free(body);
342 body = tmp;
344 if(OAUTH2info[i][OA2_GRANT_TYPE_ACCESS][0]) {
345 tmp = g_strconcat(body, "&grant_type=", OAUTH2info[i][OA2_GRANT_TYPE_ACCESS], NULL);
346 g_free(body);
347 body = tmp;
349 if(OAUTH2info[i][OA2_TENANT][0]) {
350 tmp = g_strconcat(body, "&tenant=", OAUTH2info[i][OA2_TENANT], NULL);
351 g_free(body);
352 body = tmp;
354 if(OAUTH2info[i][OA2_SCOPE_FOR_ACCESS][0]) {
355 tmp = g_strconcat(body, "&scope=", OAUTH2info[i][OA2_SCOPE_FOR_ACCESS], NULL);
356 g_free(body);
357 body = tmp;
359 if(OAUTH2info[i][OA2_STATE][0]) {
360 tmp = g_strconcat(body, "&state=", OAUTH2info[i][OA2_STATE], NULL);
361 g_free(body);
362 body = tmp;
365 if(OAUTH2info[i][OA2_HEADER_AUTH_BASIC][0]){
366 tmp_hd = g_strconcat(client_id, ":", client_secret, NULL);
367 tmp_hd_encoded = g_base64_encode (tmp_hd, strlen(tmp_hd));
368 header = g_strconcat ("Authorization: Basic ", tmp_hd_encoded, NULL);
369 g_free(tmp_hd_encoded);
370 g_free(tmp_hd);
371 }else{
372 header = g_strconcat ("", NULL);
375 oauth2_post_request (request, OAUTH2info[i][OA2_BASE_URL], OAUTH2info[i][OA2_ACCESS_RESOURCE], header, body);
376 response = oauth2_contact_server (sock, request);
377 debug_print("Response from server: %s\n", response);
379 if(response && oauth2_filter_access (response, access_token, &expiry) == 0){
380 OAUTH2Data->access_token = g_strdup(access_token);
381 OAUTH2Data->expiry = expiry;
382 OAUTH2Data->expiry_str = g_strdup_printf ("%i", expiry);
383 ret = 0;
384 log_message(LOG_PROTOCOL, _("OAuth2 access token obtained\n"));
385 }else{
386 log_message(LOG_PROTOCOL, _("OAuth2 access token not obtained\n"));
387 debug_print("OAuth2 - request: %s\n Response: %s", request, response);
388 ret = 1;
391 if(response && oauth2_filter_refresh (response, refresh_token) == 0){
392 OAUTH2Data->refresh_token = g_strdup(refresh_token);
393 log_message(LOG_PROTOCOL, _("OAuth2 refresh token obtained\n"));
394 }else{
395 log_message(LOG_PROTOCOL, _("OAuth2 refresh token not obtained\n"));
398 sock_close(sock, TRUE);
399 g_free(body);
400 g_free(header);
401 g_free(request);
402 g_free(response);
403 g_free(client_id);
404 g_free(client_secret);
405 g_free(access_token);
406 g_free(refresh_token);
408 return (ret);
411 gint oauth2_use_refresh_token (Oauth2Service provider, OAUTH2Data *OAUTH2Data)
414 gchar *request;
415 gchar *response;
416 gchar *body;
417 gchar *uri;
418 gchar *header;
419 gchar *tmp_hd, *tmp_hd_encoded;
420 gchar *access_token;
421 gchar *refresh_token;
422 gint expiry = 0;
423 gint ret;
424 SockInfo *sock;
425 gchar *client_id;
426 gchar *client_secret;
427 gchar *tmp;
428 gint i;
430 i = (int)provider - 1;
431 if (i < 0 || i > (OAUTH2AUTH_LAST-1))
432 return (1);
434 sock = sock_connect(OAUTH2info[i][OA2_BASE_URL], 443);
435 if (sock == NULL) {
436 log_message(LOG_PROTOCOL, _("OAuth2 connection error\n"));
437 return (1);
439 sock->ssl_cert_auto_accept = TRUE;
440 sock->use_tls_sni = TRUE;
441 sock_set_nonblocking_mode(sock, FALSE);
442 gint timeout_secs = prefs_common_get_prefs()->io_timeout_secs;
443 debug_print("Socket timeout: %i sec(s)\n", timeout_secs);
444 sock_set_io_timeout(timeout_secs);
445 sock->gnutls_priority = GNUTLS_PRIORITY;
446 if (ssl_init_socket(sock) == FALSE) {
447 log_message(LOG_PROTOCOL, _("OAuth2 TLS connection error\n"));
448 return (1);
451 access_token = g_malloc(OAUTH2BUFSIZE+1);
452 refresh_token = g_malloc(OAUTH2BUFSIZE+1);
453 request = g_malloc(OAUTH2BUFSIZE+1);
455 if(OAUTH2Data->custom_client_id)
456 client_id = g_strdup(OAUTH2Data->custom_client_id);
457 else
458 client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
460 uri = g_uri_escape_string (client_id, NULL, FALSE);
461 body = g_strconcat ("client_id=", uri, "&refresh_token=", OAUTH2Data->refresh_token, NULL);
462 g_free(uri);
464 if(OAUTH2info[i][OA2_CLIENT_SECRET][0]){
465 //Only allow custom client secret if the service provider would usually expect a client secret
466 if(OAUTH2Data->custom_client_secret)
467 client_secret = g_strdup(OAUTH2Data->custom_client_secret);
468 else
469 client_secret = oauth2_decode(OAUTH2info[i][OA2_CLIENT_SECRET]);
470 uri = g_uri_escape_string (client_secret, NULL, FALSE);
471 tmp = g_strconcat (body, "&client_secret=", uri, NULL);
472 g_free(body);
473 g_free(uri);
474 body = tmp;
475 }else{
476 client_secret = g_strconcat ("", NULL);
479 if(OAUTH2info[i][OA2_GRANT_TYPE_REFRESH][0]) {
480 uri = g_uri_escape_string (OAUTH2info[i][OA2_GRANT_TYPE_REFRESH], NULL, FALSE);
481 tmp = g_strconcat (body, "&grant_type=", uri, NULL);
482 g_free(body);
483 g_free(uri);
484 body = tmp;
486 if(OAUTH2info[i][OA2_SCOPE_FOR_ACCESS][0]) {
487 uri = g_uri_escape_string (OAUTH2info[i][OA2_SCOPE_FOR_ACCESS], NULL, FALSE);
488 tmp = g_strconcat (body, "&scope=", uri, NULL);
489 g_free(body);
490 g_free(uri);
491 body = tmp;
493 if(OAUTH2info[i][OA2_STATE][0]) {
494 uri = g_uri_escape_string (OAUTH2info[i][OA2_STATE], NULL, FALSE);
495 tmp = g_strconcat (body, "&state=", uri, NULL);
496 g_free(body);
497 g_free(uri);
498 body = tmp;
501 if(OAUTH2info[i][OA2_HEADER_AUTH_BASIC][0]){
502 tmp_hd = g_strconcat(client_id, ":", client_secret, NULL);
503 tmp_hd_encoded = g_base64_encode (tmp_hd, strlen(tmp_hd));
504 header = g_strconcat ("Authorization: Basic ", tmp_hd_encoded, NULL);
505 g_free(tmp_hd_encoded);
506 g_free(tmp_hd);
507 }else{
508 header = g_strconcat ("", NULL);
511 oauth2_post_request (request, OAUTH2info[i][OA2_BASE_URL], OAUTH2info[i][OA2_REFRESH_RESOURCE], header, body);
512 debug_print("Request: %s\n", request);
513 response = oauth2_contact_server (sock, request);
514 debug_print("Response from server: %s\n", response);
517 if(response && oauth2_filter_access (response, access_token, &expiry) == 0){
518 OAUTH2Data->access_token = g_strdup(access_token);
519 OAUTH2Data->expiry = expiry;
520 OAUTH2Data->expiry_str = g_strdup_printf ("%i", expiry);
521 ret = 0;
522 log_message(LOG_PROTOCOL, _("OAuth2 access token obtained\n"));
523 }else{
524 log_message(LOG_PROTOCOL, _("OAuth2 access token not obtained\n"));
525 debug_print("OAuth2 - request: %s\n Response: %s", request, response);
526 ret = 1;
529 if (response && oauth2_filter_refresh (response, refresh_token) == 0) {
530 OAUTH2Data->refresh_token = g_strdup(refresh_token);
531 log_message(LOG_PROTOCOL, _("OAuth2 replacement refresh token provided\n"));
532 } else
533 log_message(LOG_PROTOCOL, _("OAuth2 replacement refresh token not provided\n"));
535 debug_print("OAuth2 - access token: %s\n", access_token);
536 debug_print("OAuth2 - access token expiry: %i\n", expiry);
538 sock_close(sock, TRUE);
539 g_free(body);
540 g_free(header);
541 g_free(request);
542 g_free(response);
543 g_free(client_id);
544 g_free(client_secret);
545 g_free(access_token);
546 g_free(refresh_token);
548 return (ret);
551 gint oauth2_authorisation_url (Oauth2Service provider, gchar **url, const gchar *custom_client_id)
553 gint i;
554 gchar *client_id = NULL;
555 gchar *tmp;
556 gchar *uri;
558 i = (int)provider - 1;
559 if (i < 0 || i > (OAUTH2AUTH_LAST-1))
560 return (1);
562 if(!custom_client_id)
563 client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
565 uri = g_uri_escape_string (custom_client_id ? custom_client_id : client_id, NULL, FALSE);
566 *url = g_strconcat ("https://", OAUTH2info[i][OA2_BASE_URL],OAUTH2info[i][OA2_AUTH_RESOURCE], "?client_id=",
567 uri, NULL);
568 g_free(uri);
569 if (client_id)
570 g_free(client_id);
572 if(OAUTH2info[i][OA2_REDIRECT_URI][0]) {
573 uri = g_uri_escape_string (OAUTH2info[i][OA2_REDIRECT_URI], NULL, FALSE);
574 tmp = g_strconcat (*url, "&redirect_uri=", uri, NULL);
575 g_free(*url);
576 *url = tmp;
577 g_free(uri);
580 if(OAUTH2info[i][OA2_RESPONSE_TYPE][0]) {
581 uri = g_uri_escape_string (OAUTH2info[i][OA2_RESPONSE_TYPE], NULL, FALSE);
582 tmp = g_strconcat (*url, "&response_type=", uri, NULL);
583 g_free(*url);
584 *url = tmp;
585 g_free(uri);
587 if(OAUTH2info[i][OA2_SCOPE_FOR_AUTH][0]) {
588 uri = g_uri_escape_string (OAUTH2info[i][OA2_SCOPE_FOR_AUTH], NULL, FALSE);
589 tmp = g_strconcat (*url, "&scope=", uri, NULL);
590 g_free(*url);
591 *url = tmp;
592 g_free(uri);
594 if(OAUTH2info[i][OA2_TENANT][0]) {
595 uri = g_uri_escape_string (OAUTH2info[i][OA2_TENANT], NULL, FALSE);
596 tmp = g_strconcat (*url, "&tenant=", uri, NULL);
597 g_free(*url);
598 *url = tmp;
599 g_free(uri);
601 if(OAUTH2info[i][OA2_RESPONSE_MODE][0]) {
602 uri = g_uri_escape_string (OAUTH2info[i][OA2_RESPONSE_MODE], NULL, FALSE);
603 tmp = g_strconcat (*url, "&response_mode=", uri, NULL);
604 g_free(*url);
605 *url = tmp;
606 g_free(uri);
608 if(OAUTH2info[i][OA2_STATE][0]) {
609 uri = g_uri_escape_string (OAUTH2info[i][OA2_STATE], NULL, FALSE);
610 tmp = g_strconcat (*url, "&state=", uri, NULL);
611 g_free(*url);
612 *url = tmp;
613 g_free(uri);
616 return (0);
619 gint oauth2_check_passwds (PrefsAccount *ac_prefs)
621 gchar *uid = g_strdup_printf("%d", ac_prefs->account_id);
622 gint expiry;
623 OAUTH2Data *OAUTH2Data = g_malloc(sizeof(* OAUTH2Data));
624 gint ret;
625 gchar *acc;
627 oauth2_init (OAUTH2Data);
629 OAUTH2Data->custom_client_id = ac_prefs->oauth2_client_id;
630 OAUTH2Data->custom_client_secret = ac_prefs->oauth2_client_secret;
632 if (passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_EXPIRY)) {
633 acc = passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_EXPIRY);
634 expiry = atoi(acc);
635 g_free(acc);
636 if (expiry > (g_get_real_time () / G_USEC_PER_SEC)) {
637 g_free(OAUTH2Data);
638 log_message(LOG_PROTOCOL, _("OAuth2 access token still fresh\n"));
639 g_free(uid);
640 return (0);
644 if (passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_REFRESH)) {
645 log_message(LOG_PROTOCOL, _("OAuth2 obtaining access token using refresh token\n"));
646 OAUTH2Data->refresh_token = passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_REFRESH);
647 ret = oauth2_use_refresh_token (ac_prefs->oauth2_provider, OAUTH2Data);
648 } else if (passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_AUTH)) {
649 log_message(LOG_PROTOCOL, _("OAuth2 trying for fresh access token with authorization code\n"));
650 acc = passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_AUTH);
651 ret = oauth2_obtain_tokens (ac_prefs->oauth2_provider, OAUTH2Data, acc);
652 g_free(acc);
653 } else
654 ret = 1;
656 if (ret)
657 log_message(LOG_PROTOCOL, _("OAuth2 access token not obtained\n"));
658 else {
659 if (ac_prefs->imap_auth_type == IMAP_AUTH_OAUTH2 ||
660 (ac_prefs->use_pop_auth && ac_prefs->pop_auth_type == POPAUTH_OAUTH2))
661 passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_RECV, OAUTH2Data->access_token, FALSE);
662 if (ac_prefs->use_smtp_auth && ac_prefs->smtp_auth_type == SMTPAUTH_OAUTH2)
663 passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_SEND, OAUTH2Data->access_token, FALSE);
664 passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_EXPIRY, OAUTH2Data->expiry_str, FALSE);
665 //Some providers issue replacement refresh tokens with each access token. Re-store whether replaced or not.
666 if (OAUTH2Data->refresh_token != NULL)
667 passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_REFRESH, OAUTH2Data->refresh_token, FALSE);
668 passwd_store_write_config();
669 log_message(LOG_PROTOCOL, _("OAuth2 access and refresh token updated\n"));
672 g_free(OAUTH2Data);
673 g_free(uid);
675 return (ret);
678 /* returns allocated string which must be freed */
679 guchar* oauth2_decode(const gchar *in)
681 guchar *tmp;
682 gsize len;
684 tmp = g_base64_decode(in, &len);
685 passcrypt_decrypt(tmp, len);
686 return tmp;
689 /* For testing */
690 void oauth2_encode(const gchar *in)
692 guchar *tmp = g_strdup(in);
693 guchar *tmp2 = g_strdup(in);
694 gchar *result;
695 gsize len = strlen(in);
697 passcrypt_encrypt(tmp, len);
698 result = g_base64_encode(tmp, len);
699 tmp2 = oauth2_decode(result);
701 log_message(LOG_PROTOCOL, _("OAuth2 original: %s\n"), in);
702 log_message(LOG_PROTOCOL, _("OAuth2 encoded: %s\n"), result);
703 log_message(LOG_PROTOCOL, _("OAuth2 decoded: %s\n\n"), tmp2);
705 g_free(tmp);
706 g_free(tmp2);
707 g_free(result);
710 gint oauth2_init (OAUTH2Data *OAUTH2Data)
712 OAUTH2Data->refresh_token = NULL;
713 OAUTH2Data->access_token = NULL;
714 OAUTH2Data->expiry_str = NULL;
715 OAUTH2Data->expiry = 0;
716 OAUTH2Data->custom_client_id = NULL;
717 OAUTH2Data->custom_client_secret = NULL;
719 return (0);
722 #endif /* USE_GNUTLS */