I#27 - [IMAPx] Ignore DavMail's CR/LF in BODYSTRUCTURE response
[evolution-data-server.git] / src / libedataserver / e-oauth2-service.c
blob791fdc109d209898708c686608a4a52cc21c4211
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com)
5 * This library is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
18 /**
19 * SECTION: e-oauth2-service
20 * @include: libedataserver/libedataserver.h
21 * @short_description: An interface for an OAuth2 service
23 * An interface for an OAuth2 service. Any descendant might be defined
24 * as an extension of #EOAuth2Services and it should add itself into it
25 * with e_oauth2_services_add(). To make it easier, an #EOAuth2ServiceBase
26 * is provided for convenience.
27 **/
29 #include "evolution-data-server-config.h"
31 #include <string.h>
32 #include <glib/gi18n-lib.h>
34 #ifdef ENABLE_OAUTH2
35 #include <json-glib/json-glib.h>
36 #endif
38 #include "e-secret-store.h"
39 #include "e-soup-ssl-trust.h"
40 #include "e-source-authentication.h"
41 #include "e-source-goa.h"
42 #include "e-source-uoa.h"
44 #include "e-oauth2-service.h"
46 G_DEFINE_INTERFACE (EOAuth2Service, e_oauth2_service, G_TYPE_OBJECT)
48 static gboolean
49 eos_default_can_process (EOAuth2Service *service,
50 ESource *source)
52 gboolean can = FALSE;
54 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
56 if (e_source_has_extension (source, E_SOURCE_EXTENSION_GOA) ||
57 e_source_has_extension (source, E_SOURCE_EXTENSION_UOA)) {
58 return FALSE;
61 if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
62 ESourceAuthentication *auth_extension;
63 gchar *method;
65 auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
66 method = e_source_authentication_dup_method (auth_extension);
68 if (g_strcmp0 (method, e_oauth2_service_get_name (service)) == 0) {
69 g_free (method);
70 return TRUE;
73 g_free (method);
76 return can;
79 static gboolean
80 eos_default_guess_can_process (EOAuth2Service *service,
81 const gchar *protocol,
82 const gchar *hostname)
84 gboolean can = FALSE;
85 GSettings *settings;
86 gchar **values;
87 gint ii, name_len, hostname_len;
88 const gchar *name;
90 if (!hostname || !*hostname)
91 return FALSE;
93 name = e_oauth2_service_get_name (service);
94 g_return_val_if_fail (name != NULL, FALSE);
95 name_len = strlen (name);
96 hostname_len = strlen (hostname);
98 settings = g_settings_new ("org.gnome.evolution-data-server");
99 values = g_settings_get_strv (settings, "oauth2-services-hint");
100 g_object_unref (settings);
102 for (ii = 0; !can && values && values[ii]; ii++) {
103 const gchar *line = values[ii];
104 gint len;
106 if (!g_str_has_prefix (line, name) ||
107 (line[name_len] != ':' && line[name_len] != '-'))
108 continue;
110 if (line[name_len] == '-') {
111 len = protocol ? strlen (protocol) : -1;
113 if (len <= 0 || g_ascii_strncasecmp (line + name_len + 1, protocol, len) != 0 ||
114 line[name_len + len + 1] != ':')
115 continue;
117 line += name_len + len + 2;
118 } else { /* line[name_len] == ':' */
119 line += name_len + 1;
122 while (line && *line) {
123 if (g_ascii_strncasecmp (line, hostname, hostname_len) == 0 &&
124 (line[hostname_len] == ',' || line[hostname_len] == '\0')) {
125 can = TRUE;
126 break;
129 line = strchr (line, ',');
130 if (line)
131 line++;
135 g_strfreev (values);
137 return can;
140 static guint32
141 eos_default_get_flags (EOAuth2Service *service)
143 return E_OAUTH2_SERVICE_FLAG_NONE;
146 static const gchar *
147 eos_default_get_redirect_uri (EOAuth2Service *service,
148 ESource *source)
150 return "urn:ietf:wg:oauth:2.0:oob";
153 static void
154 eos_default_prepare_authentication_uri_query (EOAuth2Service *service,
155 ESource *source,
156 GHashTable *uri_query)
158 e_oauth2_service_util_set_to_form (uri_query, "response_type", "code");
159 e_oauth2_service_util_set_to_form (uri_query, "client_id", e_oauth2_service_get_client_id (service, source));
160 e_oauth2_service_util_set_to_form (uri_query, "redirect_uri", e_oauth2_service_get_redirect_uri (service, source));
162 if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
163 ESourceAuthentication *auth_extension;
164 gchar *user;
166 auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
167 user = e_source_authentication_dup_user (auth_extension);
169 if (user && *user)
170 e_oauth2_service_util_take_to_form (uri_query, "login_hint", user);
171 else
172 g_free (user);
176 static EOAuth2ServiceNavigationPolicy
177 eos_default_get_authentication_policy (EOAuth2Service *service,
178 ESource *source,
179 const gchar *uri)
181 return E_OAUTH2_SERVICE_NAVIGATION_POLICY_ALLOW;
184 static void
185 eos_default_prepare_get_token_form (EOAuth2Service *service,
186 ESource *source,
187 const gchar *authorization_code,
188 GHashTable *form)
190 e_oauth2_service_util_set_to_form (form, "code", authorization_code);
191 e_oauth2_service_util_set_to_form (form, "client_id", e_oauth2_service_get_client_id (service, source));
192 e_oauth2_service_util_set_to_form (form, "client_secret", e_oauth2_service_get_client_secret (service, source));
193 e_oauth2_service_util_set_to_form (form, "redirect_uri", e_oauth2_service_get_redirect_uri (service, source));
194 e_oauth2_service_util_set_to_form (form, "grant_type", "authorization_code");
197 static void
198 eos_default_prepare_get_token_message (EOAuth2Service *service,
199 ESource *source,
200 SoupMessage *message)
204 static void
205 eos_default_prepare_refresh_token_form (EOAuth2Service *service,
206 ESource *source,
207 const gchar *refresh_token,
208 GHashTable *form)
210 e_oauth2_service_util_set_to_form (form, "refresh_token", refresh_token);
211 e_oauth2_service_util_set_to_form (form, "client_id", e_oauth2_service_get_client_id (service, source));
212 e_oauth2_service_util_set_to_form (form, "client_secret", e_oauth2_service_get_client_secret (service, source));
213 e_oauth2_service_util_set_to_form (form, "grant_type", "refresh_token");
216 static void
217 eos_default_prepare_refresh_token_message (EOAuth2Service *service,
218 ESource *source,
219 SoupMessage *message)
223 static void
224 e_oauth2_service_default_init (EOAuth2ServiceInterface *iface)
226 iface->can_process = eos_default_can_process;
227 iface->guess_can_process = eos_default_guess_can_process;
228 iface->get_flags = eos_default_get_flags;
229 iface->get_redirect_uri = eos_default_get_redirect_uri;
230 iface->prepare_authentication_uri_query = eos_default_prepare_authentication_uri_query;
231 iface->get_authentication_policy = eos_default_get_authentication_policy;
232 iface->prepare_get_token_form = eos_default_prepare_get_token_form;
233 iface->prepare_get_token_message = eos_default_prepare_get_token_message;
234 iface->prepare_refresh_token_form = eos_default_prepare_refresh_token_form;
235 iface->prepare_refresh_token_message = eos_default_prepare_refresh_token_message;
239 * e_oauth2_service_can_process:
240 * @service: an #EOAuth2Service
241 * @source: an #ESource
243 * Checks whether the @service can be used with the given @source.
245 * The default implementation checks whether the @source has an #ESourceAuthentication
246 * extension and when its method matches e_oauth2_service_get_name(), then it automatically
247 * returns %TRUE. Contrary, when the @source contains GNOME Online Accounts or Ubuntu
248 * Online Accounts extension, then it returns %FALSE.
250 * The default implementation is tried always as the first and when it fails, then
251 * the descendant's implementation is called.
253 * Returns: Whether the @service can be used for the given @source
255 * Since: 3.28
257 gboolean
258 e_oauth2_service_can_process (EOAuth2Service *service,
259 ESource *source)
261 EOAuth2ServiceInterface *iface;
263 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), FALSE);
264 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
266 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
267 g_return_val_if_fail (iface != NULL, FALSE);
268 g_return_val_if_fail (iface->can_process != NULL, FALSE);
270 if (eos_default_can_process (service, source))
271 return TRUE;
273 return iface->can_process != eos_default_can_process &&
274 iface->can_process (service, source);
278 * e_oauth2_service_guess_can_process:
279 * @service: an #EOAuth2Service
280 * @protocol: (nullable): a protocol to search the service for, like "imap", or %NULL
281 * @hostname: (nullable): a host name to search the service for, like "server.example.com", or %NULL
283 * Checks whether the @service can be used with the given @protocol and/or @hostname.
284 * Any of @protocol and @hostname can be %NULL, but not both. It's up to each implementer
285 * to decide, which of the arguments are important and whether all or only any of them
286 * can be required.
288 * The function is meant to check whether the @service can be offered
289 * for example when configuring a new account. The real usage is
290 * determined by e_oauth2_service_can_process().
292 * The default implementation consults org.gnome.evolution-data-server.oauth2-services-hint
293 * GSettings key against given hostname. See its description for more information.
295 * The default implementation is tried always as the first and when it fails, then
296 * the descendant's implementation is called.
298 * Returns: Whether the @service can be used for the given arguments
300 * Since: 3.28
302 gboolean
303 e_oauth2_service_guess_can_process (EOAuth2Service *service,
304 const gchar *protocol,
305 const gchar *hostname)
307 EOAuth2ServiceInterface *iface;
309 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), FALSE);
310 g_return_val_if_fail (protocol || hostname, FALSE);
312 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
313 g_return_val_if_fail (iface != NULL, FALSE);
314 g_return_val_if_fail (iface->guess_can_process != NULL, FALSE);
316 if (eos_default_guess_can_process (service, protocol, hostname))
317 return TRUE;
319 return iface->guess_can_process != eos_default_guess_can_process &&
320 iface->guess_can_process (service, protocol, hostname);
324 * e_oauth2_service_get_flags:
325 * @service: an #EOAuth2Service
327 * Returns: bit-or of #EOAuth2ServiceFlags for the @service. The default
328 * implementation returns %E_OAUTH2_SERVICE_FLAG_NONE.
330 * Since: 3.28
332 guint32
333 e_oauth2_service_get_flags (EOAuth2Service *service)
335 EOAuth2ServiceInterface *iface;
337 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), E_OAUTH2_SERVICE_FLAG_NONE);
339 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
340 g_return_val_if_fail (iface != NULL, E_OAUTH2_SERVICE_FLAG_NONE);
341 g_return_val_if_fail (iface->get_flags != NULL, E_OAUTH2_SERVICE_FLAG_NONE);
343 return iface->get_flags (service);
347 * e_oauth2_service_get_name:
348 * @service: an #EOAuth2Service
350 * Returns a unique name of the service. It can be named for example
351 * by the server or the company from which it receives the OAuth2
352 * token and where it refreshes it, like "Company" for login.company.com.
354 * Returns: the name of the @service
356 * Since: 3.28
358 const gchar *
359 e_oauth2_service_get_name (EOAuth2Service *service)
361 EOAuth2ServiceInterface *iface;
363 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), NULL);
365 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
366 g_return_val_if_fail (iface != NULL, NULL);
367 g_return_val_if_fail (iface->get_name != NULL, NULL);
369 return iface->get_name (service);
373 * e_oauth2_service_get_display_name:
374 * @service: an #EOAuth2Service
376 * Returns a human readable name of the service. This is similar to
377 * e_oauth2_service_get_name(), except this string should be localized,
378 * because it will be used in user-visible strings.
380 * Returns: the display name of the @service
382 * Since: 3.28
384 const gchar *
385 e_oauth2_service_get_display_name (EOAuth2Service *service)
387 EOAuth2ServiceInterface *iface;
389 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), NULL);
391 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
392 g_return_val_if_fail (iface != NULL, NULL);
393 g_return_val_if_fail (iface->get_display_name != NULL, NULL);
395 return iface->get_display_name (service);
399 * e_oauth2_service_get_client_id:
400 * @service: an #EOAuth2Service
401 * @source: an associated #ESource
403 * Returns: application client ID, as provided by the server
405 * Since: 3.28
407 const gchar *
408 e_oauth2_service_get_client_id (EOAuth2Service *service,
409 ESource *source)
411 EOAuth2ServiceInterface *iface;
413 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), NULL);
414 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
416 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
417 g_return_val_if_fail (iface != NULL, NULL);
418 g_return_val_if_fail (iface->get_client_id != NULL, NULL);
420 return iface->get_client_id (service, source);
424 * e_oauth2_service_get_client_secret:
425 * @service: an #EOAuth2Service
426 * @source: an associated #ESource
428 * Returns: (nullable): application client secret, as provided by the server, or %NULL
430 * Since: 3.28
432 const gchar *
433 e_oauth2_service_get_client_secret (EOAuth2Service *service,
434 ESource *source)
436 EOAuth2ServiceInterface *iface;
438 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), NULL);
439 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
441 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
442 g_return_val_if_fail (iface != NULL, NULL);
443 g_return_val_if_fail (iface->get_client_secret != NULL, NULL);
445 return iface->get_client_secret (service, source);
449 * e_oauth2_service_get_authentication_uri:
450 * @service: an #EOAuth2Service
451 * @source: an associated #ESource
453 * Returns: an authentication URI, to be used to obtain
454 * the authentication code
456 * Since: 3.28
458 const gchar *
459 e_oauth2_service_get_authentication_uri (EOAuth2Service *service,
460 ESource *source)
462 EOAuth2ServiceInterface *iface;
464 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), NULL);
465 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
467 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
468 g_return_val_if_fail (iface != NULL, NULL);
469 g_return_val_if_fail (iface->get_authentication_uri != NULL, NULL);
471 return iface->get_authentication_uri (service, source);
475 * e_oauth2_service_get_refresh_uri:
476 * @service: an #EOAuth2Service
477 * @source: an associated #ESource
479 * Returns: a URI to be used to refresh the authentication token
481 * Since: 3.28
483 const gchar *
484 e_oauth2_service_get_refresh_uri (EOAuth2Service *service,
485 ESource *source)
487 EOAuth2ServiceInterface *iface;
489 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), NULL);
490 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
492 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
493 g_return_val_if_fail (iface != NULL, NULL);
494 g_return_val_if_fail (iface->get_refresh_uri != NULL, NULL);
496 return iface->get_refresh_uri (service, source);
500 * e_oauth2_service_get_redirect_uri:
501 * @service: an #EOAuth2Service
502 * @source: an associated #ESource
504 * Returns a value for the "redirect_uri" keys in the authenticate and get_token
505 * operations. The default implementation returns "urn:ietf:wg:oauth:2.0:oob".
507 * Returns: (nullable): The redirect_uri to use, or %NULL for none
509 * Since: 3.28
511 const gchar *
512 e_oauth2_service_get_redirect_uri (EOAuth2Service *service,
513 ESource *source)
515 EOAuth2ServiceInterface *iface;
517 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), NULL);
518 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
520 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
521 g_return_val_if_fail (iface != NULL, NULL);
522 g_return_val_if_fail (iface->get_redirect_uri != NULL, NULL);
524 return iface->get_redirect_uri (service, source);
528 * e_oauth2_service_prepare_authentication_uri_query:
529 * @service: an #EOAuth2Service
530 * @source: an associated #ESource
531 * @uri_query: (element-type utf8 utf8): query for the URI to use
533 * The @service can change what arguments are passed in the authentication URI
534 * in this method. The default implementation sets some values too, namely
535 * "response_type", "client_id", "redirect_uri" and "login_hint", if available
536 * in the @source. These parameters are always provided, even when the interface
537 * implementer overrides this method.
539 * The @uri_query hash table expects both key and value to be newly allocated
540 * strings, which will be freed together with the hash table or when the key
541 * is replaced.
543 * Since: 3.28
545 void
546 e_oauth2_service_prepare_authentication_uri_query (EOAuth2Service *service,
547 ESource *source,
548 GHashTable *uri_query)
550 EOAuth2ServiceInterface *iface;
552 g_return_if_fail (E_IS_OAUTH2_SERVICE (service));
553 g_return_if_fail (E_IS_SOURCE (source));
554 g_return_if_fail (uri_query != NULL);
556 eos_default_prepare_authentication_uri_query (service, source, uri_query);
558 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
559 g_return_if_fail (iface != NULL);
560 g_return_if_fail (iface->prepare_authentication_uri_query != NULL);
562 if (iface->prepare_authentication_uri_query != eos_default_prepare_authentication_uri_query)
563 iface->prepare_authentication_uri_query (service, source, uri_query);
567 * e_oauth2_service_get_authentication_policy:
568 * @service: an #EOAuth2Service
569 * @source: an associated #ESource
570 * @uri: a URI of the navigation resource
572 * Used to decide what to do when the server redirects to the next page.
573 * The default implementation always returns %E_OAUTH2_SERVICE_NAVIGATION_POLICY_ALLOW.
575 * This method is called before e_oauth2_service_extract_authorization_code() and
576 * can be used to block certain resources or to abort the authentication when
577 * the server redirects to an unexpected page (like when user denies authorization
578 * in the page).
580 * Returns: one of #EOAuth2ServiceNavigationPolicy
582 * Since: 3.28
584 EOAuth2ServiceNavigationPolicy
585 e_oauth2_service_get_authentication_policy (EOAuth2Service *service,
586 ESource *source,
587 const gchar *uri)
589 EOAuth2ServiceInterface *iface;
591 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), E_OAUTH2_SERVICE_NAVIGATION_POLICY_ABORT);
592 g_return_val_if_fail (E_IS_SOURCE (source), E_OAUTH2_SERVICE_NAVIGATION_POLICY_ABORT);
593 g_return_val_if_fail (uri != NULL, E_OAUTH2_SERVICE_NAVIGATION_POLICY_ABORT);
595 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
596 g_return_val_if_fail (iface != NULL, E_OAUTH2_SERVICE_NAVIGATION_POLICY_ABORT);
597 g_return_val_if_fail (iface->get_authentication_policy != NULL, E_OAUTH2_SERVICE_NAVIGATION_POLICY_ABORT);
599 return iface->get_authentication_policy (service, source, uri);
603 * e_oauth2_service_extract_authorization_code:
604 * @service: an #EOAuth2Service
605 * @source: an associated #ESource
606 * @page_title: a web page title
607 * @page_uri: a web page URI
608 * @page_content: (nullable): a web page content
609 * @out_authorization_code: (out) (transfer full): the extracted authorization code
611 * Tries to extract an authorization code from a web page provided by the server.
612 * The function can be called multiple times, whenever the page load is finished.
614 * There can happen three states: 1) either the @service cannot determine
615 * the authentication code from the page information, then the %FALSE is
616 * returned and the @out_authorization_code is left untouched; or 2) the server
617 * reported a failure, in which case the function returns %TRUE and lefts
618 * the @out_authorization_code untouched; or 3) the @service could extract
619 * the authentication code from the given arguments, then the function
620 * returns %TRUE and sets the received authorization code to @out_authorization_code.
622 * The @page_content is %NULL, unless flags returned by e_oauth2_service_get_flags()
623 * contain also %E_OAUTH2_SERVICE_FLAG_EXTRACT_REQUIRES_PAGE_CONTENT.
625 * This method is always called after e_oauth2_service_get_authentication_policy().
627 * Returns: whether could recognized successful or failed server response.
628 * The @out_authorization_code is populated on success too.
630 * Since: 3.28
632 gboolean
633 e_oauth2_service_extract_authorization_code (EOAuth2Service *service,
634 ESource *source,
635 const gchar *page_title,
636 const gchar *page_uri,
637 const gchar *page_content,
638 gchar **out_authorization_code)
640 EOAuth2ServiceInterface *iface;
642 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), FALSE);
643 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
645 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
646 g_return_val_if_fail (iface != NULL, FALSE);
647 g_return_val_if_fail (iface->extract_authorization_code != NULL, FALSE);
649 return iface->extract_authorization_code (service, source, page_title, page_uri, page_content, out_authorization_code);
653 * e_oauth2_service_prepare_get_token_form:
654 * @service: an #EOAuth2Service
655 * @source: an associated #ESource
656 * @authorization_code: authorization code, as returned from e_oauth2_service_extract_authorization_code()
657 * @form: (element-type utf8 utf8): form parameters to be used in the POST request
659 * Sets additional form parameters to be used in the POST request when requesting
660 * access token after successfully obtained authorization code.
661 * The default implementation sets some values too, namely
662 * "code", "client_id", "client_secret", "redirect_uri" and "grant_type".
663 * These parameters are always provided, even when the interface implementer overrides this method.
665 * The @form hash table expects both key and value to be newly allocated
666 * strings, which will be freed together with the hash table or when the key
667 * is replaced.
669 * Since: 3.28
671 void
672 e_oauth2_service_prepare_get_token_form (EOAuth2Service *service,
673 ESource *source,
674 const gchar *authorization_code,
675 GHashTable *form)
677 EOAuth2ServiceInterface *iface;
679 g_return_if_fail (E_IS_OAUTH2_SERVICE (service));
680 g_return_if_fail (E_IS_SOURCE (source));
681 g_return_if_fail (authorization_code != NULL);
682 g_return_if_fail (form != NULL);
684 eos_default_prepare_get_token_form (service, source, authorization_code, form);
686 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
687 g_return_if_fail (iface != NULL);
688 g_return_if_fail (iface->prepare_get_token_form != NULL);
690 if (iface->prepare_get_token_form != eos_default_prepare_get_token_form)
691 iface->prepare_get_token_form (service, source, authorization_code, form);
695 * e_oauth2_service_prepare_get_token_message:
696 * @service: an #EOAuth2Service
697 * @source: an associated #ESource
698 * @message: a #SoupMessage
700 * The @service can change the @message before it's sent to
701 * the e_oauth2_service_get_authentication_uri(), with POST data
702 * being provided by e_oauth2_service_prepare_get_token_form().
703 * The default implementation does nothing with the @message.
705 * Since: 3.28
707 void
708 e_oauth2_service_prepare_get_token_message (EOAuth2Service *service,
709 ESource *source,
710 SoupMessage *message)
712 EOAuth2ServiceInterface *iface;
714 g_return_if_fail (E_IS_OAUTH2_SERVICE (service));
715 g_return_if_fail (E_IS_SOURCE (source));
716 g_return_if_fail (SOUP_IS_MESSAGE (message));
718 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
719 g_return_if_fail (iface != NULL);
720 g_return_if_fail (iface->prepare_get_token_message != NULL);
722 iface->prepare_get_token_message (service, source, message);
726 * e_oauth2_service_prepare_refresh_token_form:
727 * @service: an #EOAuth2Service
728 * @source: an associated #ESource
729 * @refresh_token: a refresh token to be used
730 * @form: (element-type utf8 utf8): form parameters to be used in the POST request
732 * Sets additional form parameters to be used in the POST request when requesting
733 * to refresh an access token.
734 * The default implementation sets some values too, namely
735 * "refresh_token", "client_id", "client_secret" and "grant_type".
736 * These parameters are always provided, even when the interface implementer overrides this method.
738 * The @form hash table expects both key and value to be newly allocated
739 * strings, which will be freed together with the hash table or when the key
740 * is replaced.
742 * Since: 3.28
744 void
745 e_oauth2_service_prepare_refresh_token_form (EOAuth2Service *service,
746 ESource *source,
747 const gchar *refresh_token,
748 GHashTable *form)
750 EOAuth2ServiceInterface *iface;
752 g_return_if_fail (E_IS_OAUTH2_SERVICE (service));
753 g_return_if_fail (E_IS_SOURCE (source));
754 g_return_if_fail (refresh_token != NULL);
755 g_return_if_fail (form != NULL);
757 eos_default_prepare_refresh_token_form (service, source, refresh_token, form);
759 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
760 g_return_if_fail (iface != NULL);
761 g_return_if_fail (iface->prepare_refresh_token_form != NULL);
763 if (iface->prepare_refresh_token_form != eos_default_prepare_refresh_token_form)
764 iface->prepare_refresh_token_form (service, source, refresh_token, form);
768 * e_oauth2_service_prepare_refresh_token_message:
769 * @service: an #EOAuth2Service
770 * @source: an associated #ESource
771 * @message: a #SoupMessage
773 * The @service can change the @message before it's sent to
774 * the e_oauth2_service_get_refresh_uri(), with POST data
775 * being provided by e_oauth2_service_prepare_refresh_token_form().
776 * The default implementation does nothing with the @message.
778 * Since: 3.28
780 void
781 e_oauth2_service_prepare_refresh_token_message (EOAuth2Service *service,
782 ESource *source,
783 SoupMessage *message)
785 EOAuth2ServiceInterface *iface;
787 g_return_if_fail (E_IS_OAUTH2_SERVICE (service));
788 g_return_if_fail (E_IS_SOURCE (source));
789 g_return_if_fail (SOUP_IS_MESSAGE (message));
791 iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
792 g_return_if_fail (iface != NULL);
793 g_return_if_fail (iface->prepare_refresh_token_message != NULL);
795 iface->prepare_refresh_token_message (service, source, message);
798 static SoupSession *
799 eos_create_soup_session (EOAuth2ServiceRefSourceFunc ref_source,
800 gpointer ref_source_user_data,
801 ESource *source)
803 static gint oauth2_debug = -1;
804 ESourceAuthentication *auth_extension;
805 ESource *proxy_source = NULL;
806 SoupSession *session;
807 gchar *uid;
809 if (oauth2_debug == -1)
810 oauth2_debug = g_strcmp0 (g_getenv ("OAUTH2_DEBUG"), "1") == 0 ? 1 : 0;
812 session = soup_session_new ();
813 g_object_set (
814 session,
815 SOUP_SESSION_TIMEOUT, 90,
816 SOUP_SESSION_SSL_STRICT, TRUE,
817 SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
818 SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
819 NULL);
821 if (oauth2_debug) {
822 SoupLogger *logger;
824 logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
825 soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
826 g_object_unref (logger);
829 if (!e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION))
830 return session;
832 auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
833 uid = e_source_authentication_dup_proxy_uid (auth_extension);
834 if (uid) {
835 proxy_source = ref_source (ref_source_user_data, uid);
837 g_free (uid);
840 if (proxy_source) {
841 GProxyResolver *proxy_resolver;
843 proxy_resolver = G_PROXY_RESOLVER (proxy_source);
844 if (g_proxy_resolver_is_supported (proxy_resolver))
845 g_object_set (session, SOUP_SESSION_PROXY_RESOLVER, proxy_resolver, NULL);
847 g_object_unref (proxy_source);
850 return session;
853 static SoupMessage *
854 eos_create_soup_message (ESource *source,
855 const gchar *uri,
856 GHashTable *post_form)
858 SoupMessage *message;
859 gchar *post_data;
861 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
862 g_return_val_if_fail (uri != NULL, NULL);
863 g_return_val_if_fail (post_form != NULL, NULL);
865 message = soup_message_new (SOUP_METHOD_POST, uri);
866 g_return_val_if_fail (message != NULL, NULL);
868 post_data = soup_form_encode_hash (post_form);
869 if (!post_data) {
870 g_warn_if_fail (post_data != NULL);
871 g_object_unref (message);
873 return NULL;
876 soup_message_set_request (message, "application/x-www-form-urlencoded",
877 SOUP_MEMORY_TAKE, post_data, strlen (post_data));
879 e_soup_ssl_trust_connect (message, source);
881 soup_message_headers_append (message->request_headers, "Connection", "close");
883 return message;
886 static void
887 eos_abort_session_cb (GCancellable *cancellable,
888 SoupSession *session)
890 soup_session_abort (session);
893 static gboolean
894 eos_send_message (SoupSession *session,
895 SoupMessage *message,
896 gchar **out_response_body,
897 GCancellable *cancellable,
898 GError **error)
900 guint status_code = SOUP_STATUS_CANCELLED;
901 gboolean success = FALSE;
903 g_return_val_if_fail (SOUP_IS_SESSION (session), FALSE);
904 g_return_val_if_fail (SOUP_IS_MESSAGE (message), FALSE);
905 g_return_val_if_fail (out_response_body != NULL, FALSE);
907 if (!g_cancellable_set_error_if_cancelled (cancellable, error)) {
908 gulong cancel_handler_id = 0;
910 if (cancellable)
911 cancel_handler_id = g_cancellable_connect (cancellable, G_CALLBACK (eos_abort_session_cb), session, NULL);
913 status_code = soup_session_send_message (session, message);
915 if (cancel_handler_id)
916 g_cancellable_disconnect (cancellable, cancel_handler_id);
919 if (SOUP_STATUS_IS_SUCCESSFUL (status_code)) {
920 if (message->response_body) {
921 *out_response_body = g_strndup (message->response_body->data, message->response_body->length);
922 success = TRUE;
923 } else {
924 status_code = SOUP_STATUS_MALFORMED;
926 } else if (status_code != SOUP_STATUS_CANCELLED) {
927 GString *error_msg;
929 error_msg = g_string_new (message->reason_phrase);
930 if (message->response_body && message->response_body->length) {
931 g_string_append (error_msg, " (");
932 g_string_append_len (error_msg, message->response_body->data, message->response_body->length);
933 g_string_append (error_msg, ")");
936 g_set_error_literal (error, SOUP_HTTP_ERROR, message->status_code, error_msg->str);
938 g_string_free (error_msg, TRUE);
941 return success;
944 static gboolean
945 eos_generate_secret_uid (EOAuth2Service *service,
946 ESource *source,
947 gchar **out_uid)
949 ESourceAuthentication *authentication_extension;
950 gchar *user;
952 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), FALSE);
953 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
955 if (out_uid)
956 *out_uid = NULL;
958 if (!e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION))
959 return FALSE;
961 authentication_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
962 user = e_source_authentication_dup_user (authentication_extension);
963 if (!user || !*user) {
964 g_free (user);
965 return FALSE;
968 if (out_uid)
969 *out_uid = g_strdup_printf ("OAuth2::%s[%s]", e_oauth2_service_get_name (service), user);
971 g_free (user);
973 return TRUE;
976 static gboolean
977 eos_encode_to_secret (gchar **out_secret,
978 const gchar *key1_name,
979 const gchar *value1,
980 ...) G_GNUC_NULL_TERMINATED;
982 static gboolean
983 eos_encode_to_secret (gchar **out_secret,
984 const gchar *key1_name,
985 const gchar *value1,
986 ...)
988 #ifdef ENABLE_OAUTH2
989 JsonBuilder *builder;
990 JsonNode *node;
991 const gchar *key, *value;
992 va_list va;
994 g_return_val_if_fail (out_secret != NULL, FALSE);
995 g_return_val_if_fail (key1_name != NULL, FALSE);
996 g_return_val_if_fail (value1 != NULL, FALSE);
998 *out_secret = NULL;
1000 builder = json_builder_new ();
1002 va_start (va, value1);
1003 key = key1_name;
1004 value = value1;
1006 json_builder_begin_object (builder);
1008 while (key && value) {
1009 json_builder_set_member_name (builder, key);
1010 json_builder_add_string_value (builder, value);
1012 key = va_arg (va, const gchar *);
1013 if (!key)
1014 break;
1016 value = va_arg (va, const gchar *);
1017 g_warn_if_fail (value != NULL);
1020 va_end (va);
1022 json_builder_end_object (builder);
1023 node = json_builder_get_root (builder);
1025 g_object_unref (builder);
1027 if (node) {
1028 JsonGenerator *generator;
1030 generator = json_generator_new ();
1031 json_generator_set_root (generator, node);
1033 *out_secret = json_generator_to_data (generator, NULL);
1035 g_object_unref (generator);
1036 json_node_free (node);
1039 return *out_secret != NULL;
1040 #else
1041 return FALSE;
1042 #endif
1045 static gboolean
1046 eos_decode_from_secret (const gchar *secret,
1047 const gchar *key1_name,
1048 gchar **out_value1,
1049 ...) G_GNUC_NULL_TERMINATED;
1051 static gboolean
1052 eos_decode_from_secret (const gchar *secret,
1053 const gchar *key1_name,
1054 gchar **out_value1,
1055 ...)
1057 #ifdef ENABLE_OAUTH2
1058 JsonParser *parser;
1059 JsonReader *reader;
1060 const gchar *key;
1061 gchar **out_value;
1062 va_list va;
1063 GError *error = NULL;
1065 g_return_val_if_fail (key1_name != NULL, FALSE);
1066 g_return_val_if_fail (out_value1 != NULL, FALSE);
1068 if (!secret || !*secret)
1069 return FALSE;
1071 parser = json_parser_new ();
1072 if (!json_parser_load_from_data (parser, secret, -1, &error)) {
1073 g_object_unref (parser);
1075 g_debug ("%s: Failed to parse secret '%s': %s", G_STRFUNC, secret, error ? error->message : "Unknown error");
1076 g_clear_error (&error);
1078 return FALSE;
1081 reader = json_reader_new (json_parser_get_root (parser));
1082 key = key1_name;
1083 out_value = out_value1;
1085 va_start (va, out_value1);
1087 while (key && out_value) {
1088 *out_value = NULL;
1090 if (json_reader_read_member (reader, key)) {
1091 *out_value = g_strdup (json_reader_get_string_value (reader));
1092 if (!*out_value) {
1093 const GError *reader_error = json_reader_get_error (reader);
1095 if (g_error_matches (reader_error, JSON_READER_ERROR, JSON_READER_ERROR_INVALID_TYPE)) {
1096 gint64 iv64;
1098 json_reader_end_member (reader);
1100 iv64 = json_reader_get_int_value (reader);
1102 if (!json_reader_get_error (reader))
1103 *out_value = g_strdup_printf ("%" G_GINT64_FORMAT, iv64);
1107 if (*out_value && !**out_value) {
1108 g_free (*out_value);
1109 *out_value = NULL;
1113 json_reader_end_member (reader);
1115 key = va_arg (va, const gchar *);
1116 if (!key)
1117 break;
1119 out_value = va_arg (va, gchar **);
1120 g_warn_if_fail (out_value != NULL);
1123 g_object_unref (reader);
1124 g_object_unref (parser);
1125 va_end (va);
1127 return TRUE;
1128 #else
1129 return FALSE;
1130 #endif
1133 static gboolean
1134 eos_store_token_sync (EOAuth2Service *service,
1135 ESource *source,
1136 const gchar *refresh_token,
1137 const gchar *access_token,
1138 const gchar *expires_in,
1139 GCancellable *cancellable,
1140 GError **error)
1142 gint64 expires_after_tm;
1143 gchar *expires_after, *secret = NULL, *uid = NULL;
1144 gboolean success = FALSE;
1146 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), FALSE);
1148 if (!refresh_token || !access_token || !expires_in)
1149 return FALSE;
1151 if (g_cancellable_set_error_if_cancelled (cancellable, error))
1152 return FALSE;
1154 expires_after_tm = g_get_real_time () / G_USEC_PER_SEC;
1155 expires_after_tm += g_ascii_strtoll (expires_in, NULL, 10);
1156 expires_after = g_strdup_printf ("%" G_GINT64_FORMAT, expires_after_tm);
1158 if (eos_encode_to_secret (&secret,
1159 E_OAUTH2_SECRET_REFRESH_TOKEN, refresh_token,
1160 E_OAUTH2_SECRET_ACCESS_TOKEN, access_token,
1161 E_OAUTH2_SECRET_EXPIRES_AFTER, expires_after, NULL) &&
1162 eos_generate_secret_uid (service, source, &uid)) {
1163 gchar *label;
1165 label = g_strdup_printf ("Evolution Data Source - %s", strstr (uid, "::") + 2);
1167 success = e_secret_store_store_sync (uid, secret, label, TRUE, cancellable, error);
1169 g_free (label);
1172 g_free (uid);
1173 g_free (secret);
1174 g_free (expires_after);
1176 return success;
1179 /* Can return success when the access token is already expired and refresh token is available */
1180 static gboolean
1181 eos_lookup_token_sync (EOAuth2Service *service,
1182 ESource *source,
1183 gchar **out_refresh_token,
1184 gchar **out_access_token,
1185 gint *out_expires_in,
1186 GCancellable *cancellable,
1187 GError **error)
1189 gchar *secret = NULL, *uid = NULL, *expires_after = NULL;
1190 gboolean success = FALSE;
1192 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), FALSE);
1193 g_return_val_if_fail (out_refresh_token != NULL, FALSE);
1194 g_return_val_if_fail (out_access_token != NULL, FALSE);
1195 g_return_val_if_fail (out_expires_in != NULL, FALSE);
1197 *out_refresh_token = NULL;
1198 *out_access_token = NULL;
1199 *out_expires_in = -1;
1201 if (g_cancellable_set_error_if_cancelled (cancellable, error))
1202 return FALSE;
1204 if (!eos_generate_secret_uid (service, source, &uid)) {
1205 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1206 /* Translators: The first %s is a display name of the source, the second is its UID and
1207 the third is the name of the OAuth service. */
1208 _("Source ā€œ%sā€ (%s) is not valid for ā€œ%sā€ OAuth2 service"),
1209 e_source_get_display_name (source),
1210 e_source_get_uid (source),
1211 e_oauth2_service_get_name (service));
1212 return FALSE;
1215 if (!e_secret_store_lookup_sync (uid, &secret, cancellable, error)) {
1216 g_free (uid);
1217 return FALSE;
1220 g_free (uid);
1222 if (!secret) {
1223 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("OAuth2 secret not found"));
1224 return FALSE;
1227 success = eos_decode_from_secret (secret,
1228 E_OAUTH2_SECRET_REFRESH_TOKEN, out_refresh_token,
1229 E_OAUTH2_SECRET_ACCESS_TOKEN, out_access_token,
1230 E_OAUTH2_SECRET_EXPIRES_AFTER, &expires_after,
1231 NULL);
1233 if (success && expires_after) {
1234 gint64 num_expires_after, num_now;
1236 num_expires_after = g_ascii_strtoll (expires_after, NULL, 10);
1237 num_now = g_get_real_time () / G_USEC_PER_SEC;
1239 if (num_now < num_expires_after)
1240 *out_expires_in = num_expires_after - num_now - 1;
1243 e_util_safe_free_string (secret);
1244 g_free (expires_after);
1246 return success && *out_refresh_token != NULL;
1250 * e_oauth2_service_receive_and_store_token_sync:
1251 * @service: an #EOAuth2Service
1252 * @source: an #ESource
1253 * @authorization_code: authorization code provided by the server
1254 * @ref_source: (scope call): an #EOAuth2ServiceRefSourceFunc function to obtain an #ESource
1255 * @ref_source_user_data user data for @ref_source
1256 * @cancellable: optional #GCancellable object, or %NULL
1257 * @error: return location for a #GError, or %NULL
1259 * Queries @service at e_oauth2_service_get_refresh_uri() with a request to obtain
1260 * a new access token, associated with the given @authorization_code and stores
1261 * it into the secret store on success.
1263 * Returns: whether succeeded
1265 * Since: 3.28
1267 gboolean
1268 e_oauth2_service_receive_and_store_token_sync (EOAuth2Service *service,
1269 ESource *source,
1270 const gchar *authorization_code,
1271 EOAuth2ServiceRefSourceFunc ref_source,
1272 gpointer ref_source_user_data,
1273 GCancellable *cancellable,
1274 GError **error)
1276 SoupSession *session;
1277 SoupMessage *message;
1278 GHashTable *post_form;
1279 gchar *response_json = NULL;
1280 gboolean success;
1282 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), FALSE);
1283 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
1284 g_return_val_if_fail (authorization_code != NULL, FALSE);
1285 g_return_val_if_fail (ref_source != NULL, FALSE);
1287 session = eos_create_soup_session (ref_source, ref_source_user_data, source);
1288 if (!session)
1289 return FALSE;
1291 post_form = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1293 e_oauth2_service_prepare_get_token_form (service, source, authorization_code, post_form);
1295 message = eos_create_soup_message (source, e_oauth2_service_get_refresh_uri (service, source), post_form);
1297 g_hash_table_destroy (post_form);
1299 if (!message) {
1300 g_object_unref (session);
1301 return FALSE;
1304 e_oauth2_service_prepare_get_token_message (service, source, message);
1306 success = eos_send_message (session, message, &response_json, cancellable, error);
1307 if (success) {
1308 gchar *access_token = NULL, *refresh_token = NULL, *expires_in = NULL, *token_type = NULL;
1310 if (eos_decode_from_secret (response_json,
1311 "access_token", &access_token,
1312 "refresh_token", &refresh_token,
1313 "expires_in", &expires_in,
1314 "token_type", &token_type,
1315 NULL) && access_token && refresh_token && expires_in && token_type) {
1317 g_warn_if_fail (g_ascii_strcasecmp (token_type, "Bearer") == 0);
1319 success = eos_store_token_sync (service, source,
1320 refresh_token, access_token, expires_in, cancellable, error);
1321 } else {
1322 success = FALSE;
1325 e_util_safe_free_string (access_token);
1326 e_util_safe_free_string (refresh_token);
1327 g_free (expires_in);
1328 g_free (token_type);
1331 g_object_unref (message);
1332 g_object_unref (session);
1333 e_util_safe_free_string (response_json);
1335 return success;
1339 * e_oauth2_service_refresh_and_store_token_sync:
1340 * @service: an #EOAuth2Service
1341 * @source: an #ESource
1342 * @refresh_token: refresh token as provided by the server
1343 * @ref_source: (scope call): an #EOAuth2ServiceRefSourceFunc function to obtain an #ESource
1344 * @ref_source_user_data: user data for @ref_source
1345 * @cancellable: optional #GCancellable object, or %NULL
1346 * @error: return location for a #GError, or %NULL
1348 * Queries @service at e_oauth2_service_get_refresh_uri() with a request to refresh
1349 * existing access token with provided @refresh_token and stores it into the secret
1350 * store on success.
1352 * Returns: whether succeeded
1354 * Since: 3.28
1356 gboolean
1357 e_oauth2_service_refresh_and_store_token_sync (EOAuth2Service *service,
1358 ESource *source,
1359 const gchar *refresh_token,
1360 EOAuth2ServiceRefSourceFunc ref_source,
1361 gpointer ref_source_user_data,
1362 GCancellable *cancellable,
1363 GError **error)
1365 SoupSession *session;
1366 SoupMessage *message;
1367 GHashTable *post_form;
1368 gchar *response_json = NULL;
1369 gboolean success;
1370 GError *local_error = NULL;
1372 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), FALSE);
1373 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
1374 g_return_val_if_fail (refresh_token != NULL, FALSE);
1375 g_return_val_if_fail (ref_source != NULL, FALSE);
1377 session = eos_create_soup_session (ref_source, ref_source_user_data, source);
1378 if (!session)
1379 return FALSE;
1381 post_form = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1383 e_oauth2_service_prepare_refresh_token_form (service, source, refresh_token, post_form);
1385 message = eos_create_soup_message (source, e_oauth2_service_get_refresh_uri (service, source), post_form);
1387 g_hash_table_destroy (post_form);
1389 if (!message) {
1390 g_object_unref (session);
1391 return FALSE;
1394 e_oauth2_service_prepare_refresh_token_message (service, source, message);
1396 success = eos_send_message (session, message, &response_json, cancellable, &local_error);
1397 if (success) {
1398 gchar *access_token = NULL, *expires_in = NULL, *new_refresh_token = NULL;
1400 if (eos_decode_from_secret (response_json,
1401 "access_token", &access_token,
1402 "expires_in", &expires_in,
1403 "refresh_token", &new_refresh_token,
1404 NULL) && access_token && expires_in) {
1405 success = eos_store_token_sync (service, source,
1406 (new_refresh_token && *new_refresh_token) ? new_refresh_token : refresh_token,
1407 access_token, expires_in, cancellable, error);
1408 } else {
1409 success = FALSE;
1411 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Received incorrect response from server ā€œ%sā€."),
1412 e_oauth2_service_get_refresh_uri (service, source));
1415 e_util_safe_free_string (access_token);
1416 g_free (new_refresh_token);
1417 g_free (expires_in);
1418 } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_BAD_REQUEST)) {
1419 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED,
1420 _("Failed to refresh access token. Sign to the server again, please."));
1421 g_clear_error (&local_error);
1424 if (local_error)
1425 g_propagate_error (error, local_error);
1427 g_object_unref (message);
1428 g_object_unref (session);
1429 e_util_safe_free_string (response_json);
1431 return success;
1435 * e_oauth2_service_delete_token_sync:
1436 * @service: an #EOAuth2Service
1437 * @source: an #ESource
1438 * @cancellable: optional #GCancellable object, or %NULL
1439 * @error: return location for a #GError, or %NULL
1441 * Deletes token information for the @service and @source from the secret store.
1443 * Returns: whether succeeded
1445 * Since: 3.28
1447 gboolean
1448 e_oauth2_service_delete_token_sync (EOAuth2Service *service,
1449 ESource *source,
1450 GCancellable *cancellable,
1451 GError **error)
1453 gchar *uid = NULL;
1454 gboolean success;
1456 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), FALSE);
1457 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
1459 if (!eos_generate_secret_uid (service, source, &uid)) {
1460 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1461 /* Translators: The first %s is a display name of the source, the second is its UID. */
1462 _("Source ā€œ%sā€ (%s) is not a valid OAuth2 source"),
1463 e_source_get_display_name (source),
1464 e_source_get_uid (source));
1465 return FALSE;
1468 success = e_secret_store_delete_sync (uid, cancellable, error);
1470 g_free (uid);
1472 return success;
1476 * e_oauth2_service_get_access_token_sync:
1477 * @service: an #EOAuth2Service
1478 * @source: an #ESource
1479 * @ref_source: (scope call): an #EOAuth2ServiceRefSourceFunc function to obtain an #ESource
1480 * @ref_source_user_data: user data for @ref_source
1481 * @out_access_token: (out) (transfer full): return location for the access token
1482 * @out_expires_in: (out): how many seconds the access token expires in
1483 * @cancellable: optional #GCancellable object, or %NULL
1484 * @error: return location for a #GError, or %NULL
1486 * Reads access token information from the secret store for the @source and
1487 * in case it's expired it refreshes the token, if possible.
1489 * Free the returned @out_access_token with g_free(), when no longer needed.
1491 * Returns: %TRUE, when the returned access token has been set and it's not expired,
1492 * %FALSE otherwise.
1494 * Since: 3.28
1496 gboolean
1497 e_oauth2_service_get_access_token_sync (EOAuth2Service *service,
1498 ESource *source,
1499 EOAuth2ServiceRefSourceFunc ref_source,
1500 gpointer ref_source_user_data,
1501 gchar **out_access_token,
1502 gint *out_expires_in,
1503 GCancellable *cancellable,
1504 GError **error)
1506 gchar *refresh_token = NULL;
1507 gboolean success = TRUE;
1509 g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), FALSE);
1510 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
1511 g_return_val_if_fail (ref_source != NULL, FALSE);
1512 g_return_val_if_fail (out_access_token != NULL, FALSE);
1513 g_return_val_if_fail (out_expires_in != NULL, FALSE);
1515 if (!eos_lookup_token_sync (service, source, &refresh_token, out_access_token, out_expires_in, cancellable, error))
1516 return FALSE;
1518 if (*out_expires_in <= 0 && refresh_token) {
1519 success = e_oauth2_service_refresh_and_store_token_sync (service, source, refresh_token,
1520 ref_source, ref_source_user_data, cancellable, error);
1522 g_clear_pointer (&refresh_token, e_util_safe_free_string);
1524 success = success && eos_lookup_token_sync (service, source, &refresh_token, out_access_token, out_expires_in, cancellable, error);
1527 e_util_safe_free_string (refresh_token);
1529 if (success && *out_expires_in <= 0) {
1530 e_util_safe_free_string (*out_access_token);
1531 *out_access_token = NULL;
1532 success = FALSE;
1534 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED,
1535 _("The access token is expired and it failed to refresh it. Sign to the server again, please."));
1538 return success;
1542 * e_oauth2_service_util_set_to_form:
1543 * @form: a #GHashTable
1544 * @name: a property name
1545 * @value: (nullable): a property value
1547 * Sets @value for @name to @form. The @form should be
1548 * the one used in e_oauth2_service_prepare_authentication_uri_query(),
1549 * e_oauth2_service_prepare_get_token_form() or
1550 * e_oauth2_service_prepare_refresh_token_form().
1552 * If the @value is %NULL, then the property named @name is removed
1553 * from the @form instead.
1555 * Since: 3.28
1557 void
1558 e_oauth2_service_util_set_to_form (GHashTable *form,
1559 const gchar *name,
1560 const gchar *value)
1562 g_return_if_fail (form != NULL);
1563 g_return_if_fail (name != NULL);
1565 if (value)
1566 g_hash_table_insert (form, g_strdup (name), g_strdup (value));
1567 else
1568 g_hash_table_remove (form, name);
1572 * e_oauth2_service_util_take_to_form:
1573 * @form: a #GHashTable
1574 * @name: a property name
1575 * @value: (transfer full) (nullable): a property value
1577 * Takes ownership of @value and sets it for @name to @form. The @value
1578 * will be freed with g_free(), when no longer needed. The @form should be
1579 * the one used in e_oauth2_service_prepare_authentication_uri_query(),
1580 * e_oauth2_service_prepare_get_token_form() or
1581 * e_oauth2_service_prepare_refresh_token_form().
1583 * If the @value is %NULL, then the property named @name is removed
1584 * from the @form instead.
1586 * Since: 3.28
1588 void
1589 e_oauth2_service_util_take_to_form (GHashTable *form,
1590 const gchar *name,
1591 gchar *value)
1593 g_return_if_fail (form != NULL);
1594 g_return_if_fail (name != NULL);
1596 if (value)
1597 g_hash_table_insert (form, g_strdup (name), value);
1598 else
1599 g_hash_table_remove (form, name);