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/>.
22 #include "claws-features.h"
29 #include <glib/gi18n.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",
52 "http://127.0.0.1:8888",
57 "https://mail.google.com",
66 {"login.microsoftonline.com",
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",
74 "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
80 "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
83 {"login.microsoftonline.com",
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",
91 "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
97 "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
100 {"api.login.yahoo.com",
104 "/oauth2/request_auth",
109 "authorization_code",
119 static gchar
*OAUTH2CodeMarker
[5][2] = {
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
)
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
);
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
;
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));
154 g_match_info_free (matchInfo
);
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;
166 g_match_info_free (matchInfo
);
170 g_match_info_free (matchInfo
);
175 static gint
oauth2_filter_refresh (gchar
*json
, gchar
*refresh_token
)
177 GMatchInfo
*matchInfo
;
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));
185 g_match_info_free (matchInfo
);
189 g_match_info_free (matchInfo
);
194 static gchar
* oauth2_get_token_from_response(Oauth2Service provider
, const gchar
* response
) {
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
);
202 gchar
* start
= g_strstr_len(response
, strlen(response
), OAUTH2CodeMarker
[provider
][0]);
205 start
+= strlen(OAUTH2CodeMarker
[provider
][0]);
206 gchar
* stop
= g_strstr_len(response
, strlen(response
), OAUTH2CodeMarker
[provider
][1]);
209 token
= g_strndup(start
, stop
- start
);
215 int oauth2_obtain_tokens (Oauth2Service provider
, OAUTH2Data
*OAUTH2Data
, const gchar
*authcode
)
222 gchar
*tmp_hd
, *tmp_hd_encoded
;
224 gchar
*refresh_token
;
229 gchar
*client_secret
;
234 i
= (int)provider
- 1;
235 if (i
< 0 || i
> (OAUTH2AUTH_LAST
-1))
238 token
= oauth2_get_token_from_response(provider
, authcode
);
239 debug_print("Auth token: %s\n", token
);
241 log_message(LOG_PROTOCOL
, _("OAuth2 missing authorization code\n"));
244 debug_print("Connect: %s:443\n", OAUTH2info
[i
][OA2_BASE_URL
]);
245 sock
= sock_connect(OAUTH2info
[i
][OA2_BASE_URL
], 443);
247 log_message(LOG_PROTOCOL
, _("OAuth2 connection error\n"));
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"));
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
);
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
);
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
);
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
);
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
);
296 if(OAUTH2info
[i
][OA2_GRANT_TYPE_ACCESS
][0]) {
297 tmp
= g_strconcat(body
, "&grant_type=", OAUTH2info
[i
][OA2_GRANT_TYPE_ACCESS
], NULL
);
301 if(OAUTH2info
[i
][OA2_TENANT
][0]) {
302 tmp
= g_strconcat(body
, "&tenant=", OAUTH2info
[i
][OA2_TENANT
], NULL
);
306 if(OAUTH2info
[i
][OA2_SCOPE_FOR_ACCESS
][0]) {
307 tmp
= g_strconcat(body
, "&scope=", OAUTH2info
[i
][OA2_SCOPE_FOR_ACCESS
], NULL
);
311 if(OAUTH2info
[i
][OA2_STATE
][0]) {
312 tmp
= g_strconcat(body
, "&state=", OAUTH2info
[i
][OA2_STATE
], NULL
);
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
);
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
);
336 log_message(LOG_PROTOCOL
, _("OAuth2 access token obtained\n"));
338 log_message(LOG_PROTOCOL
, _("OAuth2 access token not obtained\n"));
339 debug_print("OAuth2 - request: %s\n Response: %s", request
, response
);
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"));
347 log_message(LOG_PROTOCOL
, _("OAuth2 refresh token not obtained\n"));
350 sock_close(sock
, TRUE
);
356 g_free(client_secret
);
357 g_free(access_token
);
358 g_free(refresh_token
);
363 gint
oauth2_use_refresh_token (Oauth2Service provider
, OAUTH2Data
*OAUTH2Data
)
371 gchar
*tmp_hd
, *tmp_hd_encoded
;
373 gchar
*refresh_token
;
378 gchar
*client_secret
;
382 i
= (int)provider
- 1;
383 if (i
< 0 || i
> (OAUTH2AUTH_LAST
-1))
386 sock
= sock_connect(OAUTH2info
[i
][OA2_BASE_URL
], 443);
388 log_message(LOG_PROTOCOL
, _("OAuth2 connection error\n"));
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"));
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
470 log_message(LOG_PROTOCOL
, _("OAuth2 access token obtained\n"));
472 log_message(LOG_PROTOCOL
, _("OAuth2 access token not obtained\n"));
473 debug_print("OAuth2 - request: %s\n Response: %s", request
, response
);
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"));
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
);
492 g_free(client_secret
);
493 g_free(access_token
);
494 g_free(refresh_token
);
499 static gint
oauth2_contact_server (SockInfo
*sock
, gchar
*request
, gchar
*response
)
504 gint toread
= OAUTH2BUFSIZE
;
505 time_t startplus
= time(NULL
);
507 len
= strlen(request
);
511 if (sock_write (sock
, request
, len
+1) < 0) {
512 log_message(LOG_PROTOCOL
, _("OAuth2 socket write error\n"));
516 token
= g_strconcat ("", NULL
);
519 ret
= sock_read (sock
, response
, OAUTH2BUFSIZE
);
520 if (ret
< 0 && errno
== EAGAIN
)
528 tmp
= g_strconcat(token
, response
, NULL
);
531 } while ((toread
> 0) && (time(NULL
) < startplus
));
533 if(time(NULL
) >= startplus
)
534 log_message(LOG_PROTOCOL
, _("OAuth2 socket timeout error\n"));
541 gint
oauth2_authorisation_url (Oauth2Service provider
, gchar
**url
, const gchar
*custom_client_id
)
544 gchar
*client_id
= NULL
;
548 i
= (int)provider
- 1;
549 if (i
< 0 || i
> (OAUTH2AUTH_LAST
-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=",
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
);
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
);
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
);
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
);
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
);
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
);
609 gint
oauth2_check_passwds (PrefsAccount
*ac_prefs
)
611 gchar
*uid
= g_strdup_printf("%d", ac_prefs
->account_id
);
613 OAUTH2Data
*OAUTH2Data
= g_malloc(sizeof(* OAUTH2Data
));
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
);
626 if (expiry
> (g_get_real_time () / G_USEC_PER_SEC
)){
628 log_message(LOG_PROTOCOL
, _("OAuth2 access token still fresh\n"));
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
);
648 log_message(LOG_PROTOCOL
, _("OAuth2 access token not obtained\n"));
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"));
665 /* returns allocated string which must be freed */
666 guchar
* oauth2_decode(const gchar
*in
)
671 tmp
= g_base64_decode(in
, &len
);
672 passcrypt_decrypt(tmp
, len
);
677 void oauth2_encode(const gchar
*in
)
679 guchar
*tmp
= g_strdup(in
);
680 guchar
*tmp2
= g_strdup(in
);
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
);
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
;
709 #endif /* USE_GNUTLS */