1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2021 the Claws Mail team
4  *
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.
9  *
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.
14  *
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/>.
17  *
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24 
25 #include <glib.h>
26 #ifdef ENABLE_NLS
27 #include <glib/gi18n.h>
28 #else
29 #define _(a) (a)
30 #define N_(a) (a)
31 #endif
32 #include <stdio.h>
33 #include <string.h>
34 #include <errno.h>
35 
36 #include "oauth2.h"
37 #include "md5.h"
38 #include "utils.h"
39 #include "log.h"
40 #include "time.h"
41 #include "common/passcrypt.h"
42 
43 //Yahoo requires token requests to send POST header Authorization: Basic
44 //where the password is Base64 encoding of client_id:client_secret
45 
46 static gchar *OAUTH2info[4][17]={
47   {"accounts.google.com",
48    "",
49    ".",
50    "urn:ietf:wg:oauth:2.0:oob",
51    "/o/oauth2/auth",
52    "/o/oauth2/token",
53    "/o/oauth2/token",
54    "code",
55    "https://mail.google.com",
56    "authorization_code",
57    "refresh_token",
58    "",
59    "",
60    "",
61    "",
62    "",
63    ""},
64   {"login.microsoftonline.com",
65    "",
66    "",
67    "https://login.microsoftonline.com/common/oauth2/nativeclient",
68    "/common/oauth2/v2.0/authorize",
69    "/common/oauth2/v2.0/token",
70    "/common/oauth2/v2.0/token",
71    "code",
72    "wl.imap offline_access",
73    "authorization_code",
74    "refresh_token",
75    "common",
76    "",
77    "offline",
78    "wl.imap offline_access",
79    "fragment",
80    ""},
81   {"login.microsoftonline.com",
82    "",
83    "",
84    "https://login.microsoftonline.com/common/oauth2/nativeclient",
85    "/common/oauth2/v2.0/authorize",
86    "/common/oauth2/v2.0/token",
87    "/common/oauth2/v2.0/token",
88    "code",
89    "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
90    "authorization_code",
91    "refresh_token",
92    "common",
93    "",
94    "offline",
95    "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
96    "fragment",
97    ""},
98   {"api.login.yahoo.com",
99    "",
100    ".",
101    "oob",
102    "/oauth2/request_auth",
103    "/oauth2/get_token",
104    "/oauth2/get_token",
105    "code",
106    "",
107    "authorization_code",
108    "refresh_token",
109    "",
110    "",
111    "",
112    "",
113    "",
114    "1"}
115 };
116 
117 static gchar *OAUTH2CodeMarker[5][2] = {
118     {"",""},
119     {"google_begin_mark","google_end_mark"}, /* Not used since token avalable to user to copy in browser window */
120     {"#code=","&session_state"},
121     {"#code=","&session_state"},
122     {"yahoo_begin_mark","yahoo_end_mark"} /* Not used since token avalable to user to copy in browser window */
123 };
124 
125 static gint oauth2_post_request (gchar *buf, gchar *host, gchar *resource, gchar *header, gchar *body);
126 static gint oauth2_filter_refresh (gchar *json, gchar *refresh_token);
127 static gint oauth2_filter_access (gchar *json, gchar *access_token, gint *expiry);
128 static gint oauth2_contact_server (SockInfo *sock, gchar *request, gchar *response);
129 
130 
oauth2_post_request(gchar * buf,gchar * host,gchar * resource,gchar * header,gchar * body)131 static gint oauth2_post_request (gchar *buf, gchar *host, gchar *resource, gchar *header, gchar *body)
132 {
133        gint len;
134 
135        len = strlen(body);
136        if (header[0])
137 	 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);
138        else
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\r\n%s", resource, len, host, body);
140 }
141 
oauth2_filter_access(gchar * json,gchar * access_token,gint * expiry)142 static gint oauth2_filter_access (gchar *json, gchar *access_token, gint *expiry)
143 {
144        GMatchInfo *matchInfo;
145        GRegex *regex;
146 
147        regex = g_regex_new ("\"access_token\": ?\"(.*?)\",?", 0, 0, NULL);
148        g_regex_match (regex, json, 0, &matchInfo);
149        if (g_match_info_matches (matchInfo))
150 	 g_stpcpy (access_token,g_match_info_fetch (matchInfo, 1));
151        else{
152 	 g_match_info_free (matchInfo);
153 	 return (-1);
154        }
155 
156        g_match_info_free (matchInfo);
157 
158        regex = g_regex_new ("\"expires_in\": ?([0-9]*),?", 0, 0, NULL);
159        g_regex_match (regex, json, 0, &matchInfo);
160        if (g_match_info_matches (matchInfo)){
161 	 // Reduce available token life to avoid attempting connections with (near) expired tokens
162 	 *expiry = (g_get_real_time () / G_USEC_PER_SEC) + atoi(g_match_info_fetch (matchInfo, 1)) - 120;
163        }else{
164 	 g_match_info_free (matchInfo);
165 	 return (-2);
166        }
167 
168        g_match_info_free (matchInfo);
169 
170        return(0);
171 }
172 
oauth2_filter_refresh(gchar * json,gchar * refresh_token)173 static gint oauth2_filter_refresh (gchar *json, gchar *refresh_token)
174 {
175        GMatchInfo *matchInfo;
176        GRegex *regex;
177 
178        regex = g_regex_new ("\"refresh_token\": ?\"(.*?)\",?", 0, 0, NULL);
179        g_regex_match (regex, json, 0, &matchInfo);
180        if (g_match_info_matches (matchInfo))
181 	 g_stpcpy (refresh_token,g_match_info_fetch (matchInfo, 1));
182        else{
183 	 g_match_info_free (matchInfo);
184 	 return (-1);
185        }
186 
187        g_match_info_free (matchInfo);
188 
189        return(0);
190 }
191 
oauth2_get_token_from_response(Oauth2Service provider,const gchar * response)192 static gchar* oauth2_get_token_from_response(Oauth2Service provider, const gchar* response) {
193 	gchar* token = NULL;
194 
195         debug_print("Auth response: %s\n", response);
196         if (provider == OAUTH2AUTH_YAHOO || provider == OAUTH2AUTH_GOOGLE) {
197                 /* Providers which display auth token in browser for users to copy */
198                 token = g_strdup(response);
199         } else {
200                 gchar* start = g_strstr_len(response, strlen(response), OAUTH2CodeMarker[provider][0]);
201                 start += strlen(OAUTH2CodeMarker[provider][0]);
202                 if (start == NULL)
203                         return NULL;
204                 gchar* stop = g_strstr_len(response, strlen(response), OAUTH2CodeMarker[provider][1]);
205                 if (stop == NULL)
206                         return NULL;
207                 token = g_strndup(start, stop - start);
208         }
209 
210 	return token;
211 }
212 
oauth2_obtain_tokens(Oauth2Service provider,OAUTH2Data * OAUTH2Data,const gchar * authcode)213 int oauth2_obtain_tokens (Oauth2Service provider, OAUTH2Data *OAUTH2Data, const gchar *authcode)
214 {
215 	gchar *request;
216 	gchar *response;
217 	gchar *body;
218 	gchar *header;
219 	gchar *tmp_hd;
220 	gchar *access_token;
221 	gchar *refresh_token;
222 	gint expiry = 0;
223 	gint ret;
224 	SockInfo *sock;
225 	gchar *client_id;
226 	gchar *client_secret;
227     gchar *token = NULL;
228 
229 	gint i;
230 
231 	i = (int)provider - 1;
232 	if (i < 0 || i > (OAUTH2AUTH_LAST-1))
233 	  return (1);
234 
235         token = oauth2_get_token_from_response(provider, authcode);
236         debug_print("Auth token: %s\n", token);
237         if (token == NULL) {
238                 log_message(LOG_PROTOCOL, _("OAuth2 missing authentication code\n"));
239                 return (1);
240         }
241 
242 	sock = sock_connect(OAUTH2info[i][OA2_BASE_URL], 443);
243 	if (sock == NULL) {
244                 log_message(LOG_PROTOCOL, _("OAuth2 connection error\n"));
245                 g_free(token);
246                 return (1);
247         }
248         sock->ssl_cert_auto_accept = TRUE;
249 	sock_set_nonblocking_mode(sock, FALSE);
250 	sock_set_io_timeout(10);
251 	sock->gnutls_priority = "NORMAL:!VERS-SSL3.0:!VERS-TLS1.0:!VERS-TLS1.1";
252         if (ssl_init_socket(sock) == FALSE) {
253                 log_message(LOG_PROTOCOL, _("OAuth2 SSL connection error\n"));
254                 g_free(token);
255                 return (1);
256         }
257 
258 	refresh_token = g_malloc(OAUTH2BUFSIZE+1);
259 	access_token = g_malloc(OAUTH2BUFSIZE+1);
260 	request = g_malloc(OAUTH2BUFSIZE+1);
261 	response = g_malloc(OAUTH2BUFSIZE+1);
262 	body = g_malloc(OAUTH2BUFSIZE+1);
263 
264 	if(OAUTH2Data->custom_client_id)
265 	  client_id = g_strdup(OAUTH2Data->custom_client_id);
266 	else
267 	  client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
268 
269 	body = g_strconcat ("client_id=", g_uri_escape_string (client_id, NULL, FALSE),
270 			    "&code=",g_uri_escape_string (token, NULL, FALSE), NULL);
271     g_free(token);
272 
273 	if(OAUTH2info[i][OA2_CLIENT_SECRET][0]){
274 	  //Only allow custom client secret if the service provider would usually expect a client secret
275 	  if(OAUTH2Data->custom_client_secret)
276 	    client_secret = g_strdup(OAUTH2Data->custom_client_secret);
277 	  else
278 	    client_secret = oauth2_decode(OAUTH2info[i][OA2_CLIENT_SECRET]);
279 	  body = g_strconcat (body, "&client_secret=", g_uri_escape_string (client_secret, NULL, FALSE), NULL);
280 	}else{
281 	  client_secret = g_strconcat ("", NULL);
282 	}
283 
284 	if(OAUTH2info[i][OA2_REDIRECT_URI][0])
285 	  body = g_strconcat (body, "&redirect_uri=",g_uri_escape_string (OAUTH2info[i][OA2_REDIRECT_URI], NULL, FALSE), NULL);
286 	if(OAUTH2info[i][OA2_GRANT_TYPE_ACCESS][0])
287 	  body = g_strconcat (body, "&grant_type=", g_uri_escape_string (OAUTH2info[i][OA2_GRANT_TYPE_ACCESS], NULL, FALSE), NULL);
288 	if(OAUTH2info[i][OA2_TENANT][0])
289 	  body = g_strconcat (body, "&tenant=", g_uri_escape_string (OAUTH2info[i][OA2_TENANT], NULL, FALSE), NULL);
290 	if(OAUTH2info[i][OA2_SCOPE_FOR_ACCESS][0])
291 	  body = g_strconcat (body, "&scope=", g_uri_escape_string (OAUTH2info[i][OA2_SCOPE_FOR_ACCESS], NULL, FALSE), NULL);
292 	if(OAUTH2info[i][OA2_STATE][0])
293 	  body = g_strconcat (body, "&state=", g_uri_escape_string (OAUTH2info[i][OA2_STATE], NULL, FALSE), NULL);
294 
295 	if(OAUTH2info[i][OA2_HEADER_AUTH_BASIC][0]){
296 	  tmp_hd = g_strconcat(client_id, ":", client_secret, NULL);
297 	  header = g_strconcat ("Authorization: Basic ", g_base64_encode (tmp_hd, strlen(tmp_hd)), NULL);
298 	  g_free(tmp_hd);
299 	}else{
300 	  header = g_strconcat ("", NULL);
301 	}
302 
303 	oauth2_post_request (request, OAUTH2info[i][OA2_BASE_URL], OAUTH2info[i][OA2_ACCESS_RESOURCE], header, body);
304 	ret = oauth2_contact_server (sock, request, response);
305 
306 	if(oauth2_filter_access (response, access_token, &expiry) == 0){
307 	  OAUTH2Data->access_token = access_token;
308 	  OAUTH2Data->expiry = expiry;
309 	  OAUTH2Data->expiry_str = g_strdup_printf ("%i", expiry);
310 	  ret = 0;
311 	  log_message(LOG_PROTOCOL, _("OAuth2 access token obtained\n"));
312 	}else{
313 	  log_message(LOG_PROTOCOL, _("OAuth2 access token not obtained\n"));
314 	  debug_print("OAuth2 - request: %s\n Response: %s", request, response);
315 	  ret = 1;
316 	}
317 
318 	if(oauth2_filter_refresh (response, refresh_token) == 0){
319 	  OAUTH2Data->refresh_token = refresh_token;
320 	  log_message(LOG_PROTOCOL, _("OAuth2 refresh token obtained\n"));
321 	}else{
322 	  log_message(LOG_PROTOCOL, _("OAuth2 refresh token not obtained\n"));
323 	}
324 
325 	sock_close(sock, TRUE);
326 	g_free(body);
327 	g_free(header);
328 	g_free(request);
329 	g_free(response);
330 	g_free(client_id);
331 	g_free(client_secret);
332 
333 	return (ret);
334 }
335 
oauth2_use_refresh_token(Oauth2Service provider,OAUTH2Data * OAUTH2Data)336 gint oauth2_use_refresh_token (Oauth2Service provider, OAUTH2Data *OAUTH2Data)
337 {
338 
339 	gchar *request;
340 	gchar *response;
341 	gchar *body;
342 	gchar *header;
343 	gchar *tmp_hd;
344 	gchar *access_token;
345 	gint expiry = 0;
346 	gint ret;
347 	SockInfo *sock;
348 	gchar *client_id;
349 	gchar *client_secret;
350 
351 	gint i;
352 
353 	i = (int)provider - 1;
354 	if (i < 0 || i > (OAUTH2AUTH_LAST-1))
355 	  return (1);
356 
357 	sock = sock_connect(OAUTH2info[i][OA2_BASE_URL], 443);
358 	if (sock == NULL) {
359                 log_message(LOG_PROTOCOL, _("OAuth2 connection error\n"));
360                 return (1);
361         }
362         sock->ssl_cert_auto_accept = TRUE;
363 	sock_set_nonblocking_mode(sock, FALSE);
364 	sock_set_io_timeout(10);
365 	sock->gnutls_priority = "NORMAL:!VERS-SSL3.0:!VERS-TLS1.0:!VERS-TLS1.1";
366         if (ssl_init_socket(sock) == FALSE) {
367                 log_message(LOG_PROTOCOL, _("OAuth2 SSL connection error\n"));
368                 return (1);
369         }
370 
371 	access_token = g_malloc(OAUTH2BUFSIZE+1);
372 	request = g_malloc(OAUTH2BUFSIZE+1);
373 	response = g_malloc(OAUTH2BUFSIZE+1);
374 	body = g_malloc(OAUTH2BUFSIZE+1);
375 
376 	if(OAUTH2Data->custom_client_id)
377 	  client_id = g_strdup(OAUTH2Data->custom_client_id);
378 	else
379 	  client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
380 
381 	body = g_strconcat ("client_id=", g_uri_escape_string (client_id, NULL, FALSE),
382 			    "&refresh_token=",OAUTH2Data->refresh_token, NULL);
383 
384 	if(OAUTH2info[i][OA2_CLIENT_SECRET][0]){
385 	  //Only allow custom client secret if the service provider would usually expect a client secret
386 	  if(OAUTH2Data->custom_client_secret)
387 	    client_secret = g_strdup(OAUTH2Data->custom_client_secret);
388 	  else
389 	    client_secret = oauth2_decode(OAUTH2info[i][OA2_CLIENT_SECRET]);
390 	  body = g_strconcat (body, "&client_secret=", g_uri_escape_string (client_secret, NULL, FALSE), NULL);
391 	}else{
392 	  client_secret = g_strconcat ("", NULL);
393 	}
394 
395 	if(OAUTH2info[i][OA2_GRANT_TYPE_REFRESH][0])
396 	  body = g_strconcat (body, "&grant_type=", g_uri_escape_string (OAUTH2info[i][OA2_GRANT_TYPE_REFRESH], NULL, FALSE), NULL);
397 	if(OAUTH2info[i][OA2_SCOPE_FOR_ACCESS][0])
398 	  body = g_strconcat (body, "&scope=", g_uri_escape_string (OAUTH2info[i][OA2_SCOPE_FOR_ACCESS], NULL, FALSE), NULL);
399 	if(OAUTH2info[i][OA2_STATE][0])
400 	  body = g_strconcat (body, "&state=", g_uri_escape_string (OAUTH2info[i][OA2_STATE], NULL, FALSE), NULL);
401 
402 	if(OAUTH2info[i][OA2_HEADER_AUTH_BASIC][0]){
403 	  tmp_hd = g_strconcat(client_id, ":", client_secret, NULL);
404 	  header = g_strconcat ("Authorization: Basic ", g_base64_encode (tmp_hd, strlen(tmp_hd)), NULL);
405 	  g_free(tmp_hd);
406 	}else{
407 	  header = g_strconcat ("", NULL);
408 	}
409 
410 	oauth2_post_request (request, OAUTH2info[i][OA2_BASE_URL], OAUTH2info[i][OA2_REFRESH_RESOURCE], header, body);
411 	ret = oauth2_contact_server (sock, request, response);
412 
413 	if(oauth2_filter_access (response, access_token, &expiry) == 0){
414 	  OAUTH2Data->access_token = access_token;
415 	  OAUTH2Data->expiry = expiry;
416 	  OAUTH2Data->expiry_str = g_strdup_printf ("%i", expiry);
417 	  ret = 0;
418 	  log_message(LOG_PROTOCOL, _("OAuth2 access token obtained\n"));
419 	}else{
420 	  log_message(LOG_PROTOCOL, _("OAuth2 access token not obtained\n"));
421 	  debug_print("OAuth2 - request: %s\n Response: %s", request, response);
422 	  ret = 1;
423 	}
424 
425 	debug_print("OAuth2 - access token: %s\n", access_token);
426 	debug_print("OAuth2 - access token expiry: %i\n", expiry);
427 
428 	sock_close(sock, TRUE);
429 	g_free(body);
430 	g_free(header);
431 	g_free(request);
432 	g_free(response);
433 	g_free(client_id);
434 	g_free(client_secret);
435 
436 	return (ret);
437 }
438 
oauth2_contact_server(SockInfo * sock,gchar * request,gchar * response)439 static gint oauth2_contact_server (SockInfo *sock, gchar *request, gchar *response)
440 {
441 	gint len;
442 	gint ret;
443 	gchar *token;
444 	gint toread = OAUTH2BUFSIZE;
445 	time_t startplus = time(NULL);
446 
447 	len = strlen(request);
448 
449 	startplus += 10;
450 
451 	if (sock_write (sock, request, len+1) < 0) {
452 	  log_message(LOG_PROTOCOL, _("OAuth2 socket write error\n"));
453 	  return (1);
454 	}
455 
456 	token = g_strconcat ("", NULL);
457 	do {
458 
459 	  ret = sock_read  (sock, response, OAUTH2BUFSIZE);
460 	  if (ret < 0 && errno == EAGAIN)
461 	    continue;
462 	  if (ret < 0)
463 	    break;
464 	  if (ret == 0)
465 	    break;
466 
467 	  toread -= ret;
468 	  token = g_strconcat(token, response, NULL);
469 	} while ((toread > 0) && (time(NULL) < startplus));
470 
471 	if(time(NULL) >= startplus)
472 	  log_message(LOG_PROTOCOL, _("OAuth2 socket timeout error \n"));
473 
474 	g_free(token);
475 
476 	return (0);
477 }
478 
oauth2_authorisation_url(Oauth2Service provider,gchar ** url,const gchar * custom_client_id)479 gint oauth2_authorisation_url (Oauth2Service provider, gchar **url, const gchar *custom_client_id)
480 {
481         gint i;
482 	const gchar *client_id;
483 
484 	i = (int)provider - 1;
485 	if (i < 0 || i > (OAUTH2AUTH_LAST-1))
486 	  return (1);
487 
488 	if(custom_client_id)
489 	  client_id = custom_client_id;
490 	else
491 	  client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
492 
493 	*url = g_strconcat ("https://", OAUTH2info[i][OA2_BASE_URL],OAUTH2info[i][OA2_AUTH_RESOURCE], "?client_id=",
494 			    g_uri_escape_string (client_id, NULL, FALSE), NULL);
495 
496 	if(OAUTH2info[i][OA2_REDIRECT_URI][0])
497 	  *url = g_strconcat (*url, "&redirect_uri=", g_uri_escape_string (OAUTH2info[i][OA2_REDIRECT_URI], NULL, FALSE), NULL);
498 	if(OAUTH2info[i][OA2_RESPONSE_TYPE][0])
499 	  *url = g_strconcat (*url, "&response_type=",g_uri_escape_string (OAUTH2info[i][OA2_RESPONSE_TYPE], NULL, FALSE), NULL);
500 	if(OAUTH2info[i][OA2_SCOPE_FOR_AUTH][0])
501 	  *url = g_strconcat (*url, "&scope=", g_uri_escape_string (OAUTH2info[i][OA2_SCOPE_FOR_AUTH], NULL, FALSE), NULL);
502 	if(OAUTH2info[i][OA2_TENANT][0])
503 	  *url = g_strconcat (*url, "&tenant=", g_uri_escape_string (OAUTH2info[i][OA2_TENANT], NULL, FALSE), NULL);
504 	if(OAUTH2info[i][OA2_RESPONSE_MODE][0])
505 	  *url = g_strconcat (*url, "&response_mode=", g_uri_escape_string (OAUTH2info[i][OA2_RESPONSE_MODE], NULL, FALSE), NULL);
506 	if(OAUTH2info[i][OA2_STATE][0])
507 	  *url = g_strconcat (*url, "&state=", g_uri_escape_string (OAUTH2info[i][OA2_STATE], NULL, FALSE), NULL);
508 
509 	return (0);
510 }
511 
oauth2_check_passwds(PrefsAccount * ac_prefs)512 gint oauth2_check_passwds (PrefsAccount *ac_prefs)
513 {
514 	gchar *uid = g_strdup_printf("%d", ac_prefs->account_id);
515 	gint expiry;
516 	OAUTH2Data *OAUTH2Data = g_malloc(sizeof(* OAUTH2Data));
517 	gint ret;
518 
519 	oauth2_init (OAUTH2Data);
520 
521 	OAUTH2Data->custom_client_id = ac_prefs->oauth2_client_id;
522 	OAUTH2Data->custom_client_secret = ac_prefs->oauth2_client_secret;
523 
524 	if(passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_EXPIRY)) {
525 	  expiry = atoi(passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_EXPIRY));
526 	  if (expiry >  (g_get_real_time () / G_USEC_PER_SEC)){
527 	    g_free(OAUTH2Data);
528 	    log_message(LOG_PROTOCOL, _("OAuth2 access token still fresh\n"));
529 	    return (0);
530 	  }
531 	}
532 
533 	if(passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_REFRESH)) {
534 	  log_message(LOG_PROTOCOL, _("OAuth2 obtaining access token using refresh token\n"));
535 	  OAUTH2Data->refresh_token = passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_REFRESH);
536 	  ret = oauth2_use_refresh_token (ac_prefs->oauth2_provider, OAUTH2Data);
537 	}else if (passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_AUTH)) {
538 	  log_message(LOG_PROTOCOL, _("OAuth2 trying for fresh access token with auth code\n"));
539 	  ret = oauth2_obtain_tokens (ac_prefs->oauth2_provider, OAUTH2Data,
540 				      passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_AUTH));
541 	}else{
542 	  ret = 1;
543 	}
544 
545 	if (ret){
546 	  log_message(LOG_PROTOCOL, _("OAuth2 access token not obtained\n"));
547 	}else{
548 	  passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_RECV, OAUTH2Data->access_token, FALSE);
549       if (ac_prefs->use_smtp_auth && ac_prefs->smtp_auth_type == SMTPAUTH_OAUTH2)
550 	        passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_SEND, OAUTH2Data->access_token, FALSE);
551 	  passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_EXPIRY, OAUTH2Data->expiry_str, FALSE);
552 	  log_message(LOG_PROTOCOL, _("OAuth2 access token updated\n"));
553 	}
554 
555 	g_free(OAUTH2Data);
556 
557 	return (ret);
558 }
559 
560 /* returns allocated string which must be freed */
oauth2_decode(const gchar * in)561 guchar* oauth2_decode(const gchar *in)
562 {
563          guchar *tmp;
564 	 gsize len;
565 
566 	 tmp = g_base64_decode(in, &len);
567 	 passcrypt_decrypt(tmp, len);
568 	 return tmp;
569 }
570 
571 /* For testing */
oauth2_encode(const gchar * in)572 void oauth2_encode(const gchar *in)
573 {
574          guchar *tmp = g_strdup(in);
575 	 guchar *tmp2 = g_strdup(in);
576 	 gchar *result;
577 	 gsize len = strlen(in);
578 
579 	 passcrypt_encrypt(tmp, len);
580 	 result = g_base64_encode(tmp, len);
581 	 tmp2 = oauth2_decode(result);
582 
583 	 log_message(LOG_PROTOCOL, _("OAuth2 original: %s\n"), in);
584 	 log_message(LOG_PROTOCOL, _("OAuth2 encoded: %s\n"), result);
585 	 log_message(LOG_PROTOCOL, _("OAuth2 decoded: %s\n\n"), tmp2);
586 
587 	 g_free(tmp);
588 	 g_free(tmp2);
589 	 g_free(result);
590 }
591 
oauth2_init(OAUTH2Data * OAUTH2Data)592 gint oauth2_init (OAUTH2Data *OAUTH2Data)
593 {
594 	 OAUTH2Data->refresh_token = NULL;
595 	 OAUTH2Data->access_token = NULL;
596 	 OAUTH2Data->expiry_str = NULL;
597 	 OAUTH2Data->expiry = 0;
598 	 OAUTH2Data->custom_client_id = NULL;
599 	 OAUTH2Data->custom_client_secret = NULL;
600 
601 	 return (0);
602 }
603