[evolution-data-server] Bug 749974 - Use OAuth2 for Google sources
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Bug 749974 - Use OAuth2 for Google sources
- Date: Mon, 21 Sep 2015 13:36:56 +0000 (UTC)
commit 3ed35881531088dd9eceb1a800390daf3fb214b3
Author: Milan Crha <mcrha redhat com>
Date: Mon Sep 21 15:36:32 2015 +0200
Bug 749974 - Use OAuth2 for Google sources
.../backends/google/e-book-backend-google.c | 133 +--
.../backends/google/e-gdata-oauth2-authorizer.c | 40 +-
.../backends/google/e-gdata-oauth2-authorizer.h | 6 +
addressbook/libebook/e-book-client-view.c | 2 +
calendar/backends/caldav/e-cal-backend-caldav.c | 210 ++--
calendar/backends/gtasks/e-cal-backend-gtasks.c | 173 +++-
.../backends/gtasks/e-gdata-oauth2-authorizer.c | 40 +-
.../backends/gtasks/e-gdata-oauth2-authorizer.h | 6 +
camel/providers/imapx/camel-imapx-command.c | 2 +
camel/providers/imapx/camel-imapx-server.c | 3 +-
camel/providers/smtp/camel-smtp-transport.c | 4 +-
configure.ac | 39 +
libedataserver/Makefile.am | 4 +
libedataserver/e-data-server-util.c | 38 +
libedataserver/e-data-server-util.h | 8 +-
.../e-source-credentials-provider-impl-google.c | 475 ++++++++
.../e-source-credentials-provider-impl-google.h | 99 ++
libedataserver/e-source-credentials-provider.c | 2 +
libedataserver/e-webdav-discover.c | 54 +-
libedataserver/libedataserver.h | 1 +
libedataserverui/Makefile.am | 4 +
.../e-credentials-prompter-impl-google.c | 1127 ++++++++++++++++++++
.../e-credentials-prompter-impl-google.h | 78 ++
.../e-credentials-prompter-impl-password.c | 9 +-
libedataserverui/e-credentials-prompter.c | 2 +
libedataserverui/libedataserverui.h | 1 +
modules/google-backend/module-google-backend.c | 143 +++-
modules/secret-monitor/module-secret-monitor.c | 4 +
po/POTFILES.in | 2 +
29 files changed, 2416 insertions(+), 293 deletions(-)
---
diff --git a/addressbook/backends/google/e-book-backend-google.c
b/addressbook/backends/google/e-book-backend-google.c
index 7e106c5..fc30395 100644
--- a/addressbook/backends/google/e-book-backend-google.c
+++ b/addressbook/backends/google/e-book-backend-google.c
@@ -84,40 +84,7 @@ data_book_error_from_gdata_error (GError **error,
g_return_if_fail (gdata_error != NULL);
/* Authentication errors */
- if (gdata_error->domain == GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR) {
- switch (gdata_error->code) {
- case GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_BAD_AUTHENTICATION:
- g_set_error_literal (
- error, E_CLIENT_ERROR,
- E_CLIENT_ERROR_AUTHENTICATION_FAILED,
- e_client_error_to_string (
- E_CLIENT_ERROR_AUTHENTICATION_FAILED));
- break;
- case GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_NOT_VERIFIED:
- case GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_TERMS_NOT_AGREED:
- case GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_CAPTCHA_REQUIRED:
- case GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_DELETED:
- case GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_DISABLED:
- g_set_error_literal (
- error, E_CLIENT_ERROR,
- E_CLIENT_ERROR_PERMISSION_DENIED,
- e_client_error_to_string (
- E_CLIENT_ERROR_PERMISSION_DENIED));
- break;
- case GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_SERVICE_DISABLED:
- g_set_error_literal (
- error, E_CLIENT_ERROR,
- E_CLIENT_ERROR_REPOSITORY_OFFLINE,
- e_client_error_to_string (
- E_CLIENT_ERROR_REPOSITORY_OFFLINE));
- break;
- default:
- use_fallback = TRUE;
- break;
- }
-
- /* General service errors */
- } else if (gdata_error->domain == GDATA_SERVICE_ERROR) {
+ if (gdata_error->domain == GDATA_SERVICE_ERROR) {
switch (gdata_error->code) {
case GDATA_SERVICE_ERROR_UNAVAILABLE:
g_set_error_literal (
@@ -1012,7 +979,8 @@ get_groups (EBookBackend *backend,
static void
get_groups_sync (EBookBackend *backend,
- GCancellable *cancellable)
+ GCancellable *cancellable,
+ GError **error)
{
EBookBackendGooglePrivate *priv;
GDataQuery *query;
@@ -1033,7 +1001,7 @@ get_groups_sync (EBookBackend *backend,
cancellable,
(GDataQueryProgressCallback) process_group,
backend,
- NULL);
+ error);
if (feed)
g_object_unref (feed);
@@ -1201,38 +1169,26 @@ connect_without_password (EBookBackend *backend,
GCancellable *cancellable,
GError **error)
{
+ ESource *source;
+ ESourceAuthentication *extension;
+ EGDataOAuth2Authorizer *authorizer;
+ gchar *method;
+ gboolean is_oauth2_method;
EBookBackendGooglePrivate *priv;
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
+ source = e_backend_get_source (E_BACKEND (backend));
+ extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+ method = e_source_authentication_dup_method (extension);
+ is_oauth2_method = g_strcmp0 (method, "OAuth2") == 0;
+ g_free (method);
+
/* Make sure we have the GDataService configured
* before requesting authorization. */
if (priv->authorizer == NULL) {
- ESource *source;
- ESourceAuthentication *extension;
- EGDataOAuth2Authorizer *authorizer;
- const gchar *extension_name;
- gchar *method;
-
- extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
- source = e_backend_get_source (E_BACKEND (backend));
- extension = e_source_get_extension (source, extension_name);
- method = e_source_authentication_dup_method (extension);
-
- if (g_strcmp0 (method, "OAuth2") == 0) {
- authorizer = e_gdata_oauth2_authorizer_new (source);
- priv->authorizer = GDATA_AUTHORIZER (authorizer);
- }
-
- g_free (method);
- }
-
- if (priv->authorizer == NULL) {
- GDataClientLoginAuthorizer *authorizer;
-
- authorizer = gdata_client_login_authorizer_new (
- CLIENT_ID, GDATA_TYPE_CONTACTS_SERVICE);
+ authorizer = e_gdata_oauth2_authorizer_new (source);
priv->authorizer = GDATA_AUTHORIZER (authorizer);
}
@@ -1261,10 +1217,10 @@ connect_without_password (EBookBackend *backend,
/* If we're using OAuth tokens, then as far as the backend
* is concerned it's always authorized. The GDataAuthorizer
* will take care of everything in the background. */
- if (!GDATA_IS_CLIENT_LOGIN_AUTHORIZER (priv->authorizer))
+ if (is_oauth2_method)
return TRUE;
- /* Otherwise it's up to us to obtain a login secret. */
+ /* Otherwise it's up to us to obtain an OAuth 2 token. */
return FALSE;
}
@@ -1812,7 +1768,7 @@ book_backend_google_create_contacts_sync (EBookBackend *backend,
/* Ensure the system groups have been fetched. */
if (g_hash_table_size (priv->system_groups_by_id) == 0)
- get_groups_sync (backend, cancellable);
+ get_groups_sync (backend, cancellable, NULL);
/* Build the GDataEntry from the vCard */
contact = e_contact_new_from_vcard (vcards[0]);
@@ -1987,7 +1943,7 @@ book_backend_google_modify_contacts_sync (EBookBackend *backend,
/* Ensure the system groups have been fetched. */
if (g_hash_table_size (priv->system_groups_by_id) == 0)
- get_groups_sync (backend, cancellable);
+ get_groups_sync (backend, cancellable, NULL);
/* Update the old GDataEntry from the new contact. */
gdata_entry_update_from_e_contact (
@@ -2329,58 +2285,39 @@ book_backend_google_authenticate_sync (EBackend *backend,
GCancellable *cancellable,
GError **error)
{
+ EBookBackend *book_backend = E_BOOK_BACKEND (backend);
EBookBackendGooglePrivate *priv;
- ESourceAuthentication *auth_extension;
ESourceAuthenticationResult result;
- ESource *source;
- const gchar *username;
- gchar *user;
+ EGDataOAuth2Authorizer *authorizer;
GError *local_error = NULL;
g_debug (G_STRFUNC);
/* We should not have gotten here if we're offline. */
- g_return_val_if_fail (
- e_backend_get_online (E_BACKEND (backend)),
- E_SOURCE_AUTHENTICATION_ERROR);
-
- /* Nor should we have gotten here if we're already authorized. */
- g_return_val_if_fail (
- !backend_is_authorized (E_BOOK_BACKEND (backend)),
- E_SOURCE_AUTHENTICATION_ERROR);
+ g_return_val_if_fail (e_backend_get_online (backend), E_SOURCE_AUTHENTICATION_ERROR);
priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
- source = e_backend_get_source (backend);
- auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
- user = e_source_authentication_dup_user (auth_extension);
+ g_return_val_if_fail (E_IS_GDATA_OAUTH2_AUTHORIZER (priv->authorizer), E_SOURCE_AUTHENTICATION_ERROR);
- username = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME);
- if (!username || !*username)
- username = user;
+ authorizer = E_GDATA_OAUTH2_AUTHORIZER (priv->authorizer);
+ e_gdata_oauth2_authorizer_set_credentials (authorizer, credentials);
- if (gdata_client_login_authorizer_authenticate (GDATA_CLIENT_LOGIN_AUTHORIZER (priv->authorizer),
- user, e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD), cancellable,
&local_error)) {
- EBookBackend *book_backend = E_BOOK_BACKEND (backend);
+ get_groups_sync (E_BOOK_BACKEND (backend), cancellable, &local_error);
+
+ if (local_error == NULL) {
+ result = E_SOURCE_AUTHENTICATION_ACCEPTED;
if (backend_is_authorized (book_backend)) {
e_book_backend_set_writable (book_backend, TRUE);
cache_refresh_if_needed (book_backend);
}
- }
-
- g_free (user);
-
- if (local_error == NULL) {
- result = E_SOURCE_AUTHENTICATION_ACCEPTED;
-
- } else if (g_error_matches (
- local_error, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR,
- GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_BAD_AUTHENTICATION)) {
-
+ } else if (g_error_matches (local_error, GDATA_SERVICE_ERROR,
GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED)) {
+ if (!e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD))
+ result = E_SOURCE_AUTHENTICATION_REQUIRED;
+ else
+ result = E_SOURCE_AUTHENTICATION_REJECTED;
g_clear_error (&local_error);
- result = E_SOURCE_AUTHENTICATION_REJECTED;
-
} else {
g_propagate_error (error, local_error);
result = E_SOURCE_AUTHENTICATION_ERROR;
diff --git a/addressbook/backends/google/e-gdata-oauth2-authorizer.c
b/addressbook/backends/google/e-gdata-oauth2-authorizer.c
index a8e384b..3012ca6 100644
--- a/addressbook/backends/google/e-gdata-oauth2-authorizer.c
+++ b/addressbook/backends/google/e-gdata-oauth2-authorizer.c
@@ -27,6 +27,7 @@ struct _EGDataOAuth2AuthorizerPrivate {
/* These members are protected by the global mutex. */
gchar *access_token;
GHashTable *authorization_domains;
+ ENamedParameters *credentials;
};
enum {
@@ -137,6 +138,8 @@ gdata_oauth2_authorizer_finalize (GObject *object)
g_hash_table_destroy (priv->authorization_domains);
g_weak_ref_clear (&priv->source);
+ e_named_parameters_free (priv->credentials);
+
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_gdata_oauth2_authorizer_parent_class)->
finalize (object);
@@ -240,8 +243,8 @@ gdata_oauth2_authorizer_refresh_authorization (GDataAuthorizer *authorizer,
g_free (*ptr_access_token);
*ptr_access_token = NULL;
- success = e_source_get_oauth2_access_token_sync (
- source, cancellable, ptr_access_token, NULL, error);
+ success = e_util_get_source_oauth2_access_token_sync (source, oauth2_authorizer->priv->credentials,
+ ptr_access_token, NULL, cancellable, error);
g_mutex_unlock (&mutex);
@@ -323,3 +326,36 @@ e_gdata_oauth2_authorizer_ref_source (EGDataOAuth2Authorizer *authorizer)
return g_weak_ref_get (&authorizer->priv->source);
}
+void
+e_gdata_oauth2_authorizer_set_credentials (EGDataOAuth2Authorizer *authorizer,
+ const ENamedParameters *credentials)
+{
+ g_return_if_fail (E_IS_GDATA_OAUTH2_AUTHORIZER (authorizer));
+
+ g_mutex_lock (&mutex);
+
+ e_named_parameters_free (authorizer->priv->credentials);
+ if (credentials)
+ authorizer->priv->credentials = e_named_parameters_new_clone (credentials);
+ else
+ authorizer->priv->credentials = NULL;
+
+ g_mutex_unlock (&mutex);
+}
+
+ENamedParameters *
+e_gdata_oauth2_authorizer_clone_credentials (EGDataOAuth2Authorizer *authorizer)
+{
+ ENamedParameters *credentials = NULL;
+
+ g_return_val_if_fail (E_IS_GDATA_OAUTH2_AUTHORIZER (authorizer), NULL);
+
+ g_mutex_lock (&mutex);
+
+ if (authorizer->priv->credentials)
+ credentials = e_named_parameters_new_clone (authorizer->priv->credentials);
+
+ g_mutex_unlock (&mutex);
+
+ return credentials;
+}
diff --git a/addressbook/backends/google/e-gdata-oauth2-authorizer.h
b/addressbook/backends/google/e-gdata-oauth2-authorizer.h
index ef908f6..0dfc582 100644
--- a/addressbook/backends/google/e-gdata-oauth2-authorizer.h
+++ b/addressbook/backends/google/e-gdata-oauth2-authorizer.h
@@ -62,6 +62,12 @@ EGDataOAuth2Authorizer *
(ESource *source);
ESource * e_gdata_oauth2_authorizer_ref_source
(EGDataOAuth2Authorizer *authorizer);
+void e_gdata_oauth2_authorizer_set_credentials
+ (EGDataOAuth2Authorizer *authorizer,
+ const ENamedParameters *credentials);
+ENamedParameters *
+ e_gdata_oauth2_authorizer_clone_credentials
+ (EGDataOAuth2Authorizer *authorizer);
G_END_DECLS
diff --git a/addressbook/libebook/e-book-client-view.c b/addressbook/libebook/e-book-client-view.c
index 0393617..be4df5c 100644
--- a/addressbook/libebook/e-book-client-view.c
+++ b/addressbook/libebook/e-book-client-view.c
@@ -647,6 +647,8 @@ book_client_view_complete_cb (EGdbusBookView *object,
g_weak_ref_init (&signal_closure->client_view, client_view);
e_gdbus_templates_decode_error (
in_error_strv, &signal_closure->error);
+ if (signal_closure->error)
+ g_dbus_error_strip_remote_error (signal_closure->error);
main_context = book_client_view_ref_main_context (client_view);
diff --git a/calendar/backends/caldav/e-cal-backend-caldav.c b/calendar/backends/caldav/e-cal-backend-caldav.c
index 51ec4ba..4d1a389 100644
--- a/calendar/backends/caldav/e-cal-backend-caldav.c
+++ b/calendar/backends/caldav/e-cal-backend-caldav.c
@@ -95,8 +95,7 @@ struct _ECalBackendCalDAVPrivate {
gchar *uri;
/* Authentication info */
- gchar *username; /* not NULL only as override */
- gchar *password;
+ ENamedParameters *credentials;
gboolean auth_required;
/* support for 'getctag' extension */
@@ -322,6 +321,92 @@ static void put_server_comp_to_cache (ECalBackendCalDAV *cbdav, icalcomponent *i
/* ************************************************************************* */
/* Misc. utility functions */
+static gboolean
+caldav_setup_bearer_auth (ECalBackendCalDAV *cbdav,
+ ESoupAuthBearer *bearer,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESource *source;
+ gchar *access_token = NULL;
+ gint expires_in_seconds = -1;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
+ g_return_val_if_fail (E_IS_SOUP_AUTH_BEARER (bearer), FALSE);
+
+ source = e_backend_get_source (E_BACKEND (cbdav));
+
+ success = e_util_get_source_oauth2_access_token_sync (source, cbdav->priv->credentials,
+ &access_token, &expires_in_seconds, cancellable, error);
+
+ if (success)
+ e_soup_auth_bearer_set_access_token (bearer, access_token, expires_in_seconds);
+
+ g_free (access_token);
+
+ return success;
+}
+
+static gboolean
+caldav_maybe_prepare_bearer_auth (ECalBackendCalDAV *cbdav,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESourceWebdav *extension;
+ ESource *source;
+ SoupSessionFeature *feature;
+ SoupAuth *soup_auth;
+ SoupURI *soup_uri;
+ gchar *auth_method = NULL;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
+
+ source = e_backend_get_source (E_BACKEND (cbdav));
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+ ESourceAuthentication *extension;
+
+ extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+ auth_method = e_source_authentication_dup_method (extension);
+ } else {
+ return TRUE;
+ }
+
+ if (g_strcmp0 (auth_method, "OAuth2") != 0 && g_strcmp0 (auth_method, "Google") != 0) {
+ g_free (auth_method);
+ return TRUE;
+ }
+
+ g_free (auth_method);
+
+ /* Preload the SoupAuthManager with a valid "Bearer" token
+ * when using OAuth 2.0. This avoids an extra unauthorized
+ * HTTP round-trip, which apparently Google doesn't like. */
+
+ feature = soup_session_get_feature (cbdav->priv->session, SOUP_TYPE_AUTH_MANAGER);
+
+ extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+ soup_uri = e_source_webdav_dup_soup_uri (extension);
+
+ soup_auth = g_object_new (
+ E_TYPE_SOUP_AUTH_BEARER,
+ SOUP_AUTH_HOST, soup_uri->host, NULL);
+
+ success = caldav_setup_bearer_auth (cbdav, E_SOUP_AUTH_BEARER (soup_auth), cancellable, error);
+ if (success) {
+ soup_auth_manager_use_auth (
+ SOUP_AUTH_MANAGER (feature),
+ soup_uri, soup_auth);
+ }
+
+ g_object_unref (soup_auth);
+ soup_uri_free (soup_uri);
+
+ return success;
+}
+
static void
update_slave_cmd (ECalBackendCalDAVPrivate *priv,
SlaveCommand slave_cmd)
@@ -994,20 +1079,9 @@ soup_authenticate_bearer (SoupSession *session,
SoupAuth *auth,
ECalBackendCalDAV *cbdav)
{
- ESource *source;
- gchar *access_token = NULL;
- gint expires_in_seconds = -1;
GError *local_error = NULL;
- source = e_backend_get_source (E_BACKEND (cbdav));
-
- e_source_get_oauth2_access_token_sync (
- source, NULL, &access_token,
- &expires_in_seconds, &local_error);
-
- e_soup_auth_bearer_set_access_token (
- E_SOUP_AUTH_BEARER (auth),
- access_token, expires_in_seconds);
+ caldav_setup_bearer_auth (cbdav, E_SOUP_AUTH_BEARER (auth), NULL, &local_error);
/* Stash the error to be picked up by caldav_credentials_required_sync().
* There's no way to explicitly propagate a GError directly
@@ -1026,8 +1100,6 @@ soup_authenticate_bearer (SoupSession *session,
g_mutex_unlock (&cbdav->priv->bearer_auth_error_lock);
}
-
- g_free (access_token);
}
static void
@@ -1063,14 +1135,15 @@ soup_authenticate (SoupSession *session,
auth_user = e_source_authentication_dup_user (auth_extension);
- username = cbdav->priv->username;
+ username = cbdav->priv->credentials ? e_named_parameters_get (cbdav->priv->credentials,
E_SOURCE_CREDENTIAL_USERNAME) : NULL;
if (!username || !*username)
username = auth_user;
- if (!username || !*username || !cbdav->priv->password)
+ if (!username || !*username || !cbdav->priv->credentials ||
+ !e_named_parameters_exists (cbdav->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD))
soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
else
- soup_auth_authenticate (auth, username, cbdav->priv->password);
+ soup_auth_authenticate (auth, username, e_named_parameters_get
(cbdav->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD));
g_free (auth_user);
}
@@ -1315,7 +1388,9 @@ caldav_credentials_required_sync (ECalBackendCalDAV *cbdav,
if (success) {
success = e_backend_credentials_required_sync (E_BACKEND (cbdav),
- (first_attempt || !cbdav->priv->password) ? E_SOURCE_CREDENTIALS_REASON_REQUIRED :
+ (first_attempt || !cbdav->priv->credentials ||
+ !e_named_parameters_exists (cbdav->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD))
+ ? E_SOURCE_CREDENTIALS_REASON_REQUIRED :
E_SOURCE_CREDENTIALS_REASON_REJECTED,
NULL, 0, NULL, cancellable, error);
}
@@ -2983,6 +3058,9 @@ open_calendar_wrapper (ECalBackendCalDAV *cbdav,
g_return_val_if_fail (cbdav != NULL, FALSE);
+ if (!caldav_maybe_prepare_bearer_auth (cbdav, cancellable, error))
+ return FALSE;
+
success = caldav_server_open_calendar (cbdav, &server_unreachable, out_certificate_pem,
out_certificate_errors, cancellable, &local_error);
if (first_attempt && g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed)) {
@@ -3033,6 +3111,7 @@ caldav_do_open (ECalBackendSync *backend,
{
ECalBackendCalDAV *cbdav;
ESourceWebdav *webdav_extension;
+ ESourceAuthentication *auth_extension;
ESource *source;
gboolean online;
@@ -3042,6 +3121,7 @@ caldav_do_open (ECalBackendSync *backend,
source = e_backend_get_source (E_BACKEND (cbdav));
webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+ auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
/* let it decide the 'getctag' extension availability again */
@@ -3065,11 +3145,14 @@ caldav_do_open (ECalBackendSync *backend,
cbdav->priv->is_google = FALSE;
if (online) {
- gchar *certificate_pem = NULL;
+ gchar *certificate_pem = NULL, *auth_method;
GTlsCertificateFlags certificate_errors = 0;
GError *local_error = NULL;
- if (!open_calendar_wrapper (cbdav, cancellable, &local_error, TRUE, NULL, &certificate_pem,
&certificate_errors) &&
+ auth_method = e_source_authentication_dup_method (auth_extension);
+
+ if ((g_strcmp0 (auth_method, "Google") == 0 ||
+ !open_calendar_wrapper (cbdav, cancellable, &local_error, TRUE, NULL, &certificate_pem,
&certificate_errors)) &&
!g_cancellable_is_cancelled (cancellable)) {
ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
GError *local_error2 = NULL;
@@ -3090,7 +3173,8 @@ caldav_do_open (ECalBackendSync *backend,
/* These errors are treated through the authentication */
g_clear_error (&local_error);
} else {
- g_propagate_error (perror, local_error);
+ if (local_error)
+ g_propagate_error (perror, local_error);
local_error = NULL;
}
g_clear_error (&local_error2);
@@ -3098,6 +3182,7 @@ caldav_do_open (ECalBackendSync *backend,
g_clear_error (&local_error);
g_free (certificate_pem);
+ g_free (auth_method);
} else {
e_cal_backend_set_writable (E_CAL_BACKEND (cbdav), FALSE);
}
@@ -5405,23 +5490,14 @@ caldav_authenticate_sync (EBackend *backend,
{
ECalBackendCalDAV *cbdav;
ESourceAuthenticationResult result;
- const gchar *username;
GError *local_error = NULL;
cbdav = E_CAL_BACKEND_CALDAV (backend);
g_mutex_lock (&cbdav->priv->busy_lock);
- g_free (cbdav->priv->username);
- cbdav->priv->username = NULL;
-
- g_free (cbdav->priv->password);
- cbdav->priv->password = g_strdup (e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD));
-
- username = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME);
- if (username && *username) {
- cbdav->priv->username = g_strdup (username);
- }
+ e_named_parameters_free (cbdav->priv->credentials);
+ cbdav->priv->credentials = e_named_parameters_new_clone (credentials);
open_calendar_wrapper (cbdav, cancellable, &local_error, FALSE, NULL, out_certificate_pem,
out_certificate_errors);
@@ -5432,8 +5508,11 @@ caldav_authenticate_sync (EBackend *backend,
g_cond_signal (&cbdav->priv->cond);
} else if (g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed) ||
g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationRequired)) {
+ const gchar *username;
gchar *auth_user = NULL;
+ username = e_named_parameters_get (cbdav->priv->credentials, E_SOURCE_CREDENTIAL_USERNAME);
+
if (!username || !*username) {
ESource *source;
ESourceAuthentication *auth_extension;
@@ -5500,8 +5579,8 @@ e_cal_backend_caldav_finalize (GObject *object)
g_cond_clear (&priv->slave_gone_cond);
g_free (priv->uri);
- g_free (priv->username);
- g_free (priv->password);
+ e_named_parameters_free (priv->credentials);
+ priv->credentials = NULL;
g_free (priv->schedule_outbox_url);
if (priv->ctag_to_store) {
@@ -5523,73 +5602,16 @@ caldav_backend_initable_init (GInitable *initable,
{
ECalBackendCalDAVPrivate *priv;
SoupSessionFeature *feature;
- ESource *source;
- const gchar *extension_name;
- gchar *auth_method = NULL;
gboolean success = TRUE;
priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (initable);
- feature = soup_session_get_feature (
- priv->session, SOUP_TYPE_AUTH_MANAGER);
+ feature = soup_session_get_feature (priv->session, SOUP_TYPE_AUTH_MANAGER);
/* Add the "Bearer" auth type to support OAuth 2.0. */
soup_session_feature_add_feature (feature, E_TYPE_SOUP_AUTH_BEARER);
g_mutex_init (&priv->bearer_auth_error_lock);
- /* Preload the SoupAuthManager with a valid "Bearer" token
- * when using OAuth 2.0. This avoids an extra unauthorized
- * HTTP round-trip, which apparently Google doesn't like. */
-
- source = e_backend_get_source (E_BACKEND (initable));
-
- extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
- if (e_source_has_extension (source, extension_name)) {
- ESourceAuthentication *extension;
-
- extension = e_source_get_extension (source, extension_name);
- auth_method = e_source_authentication_dup_method (extension);
- }
-
- e_backend_ensure_online_state_updated (E_BACKEND (initable), cancellable);
-
- if (g_strcmp0 (auth_method, "OAuth2") == 0 &&
- e_backend_get_online (E_BACKEND (initable))) {
- ESourceWebdav *extension;
- SoupAuth *soup_auth;
- SoupURI *soup_uri;
- gchar *access_token = NULL;
- gint expires_in_seconds = -1;
-
- extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
- extension = e_source_get_extension (source, extension_name);
- soup_uri = e_source_webdav_dup_soup_uri (extension);
-
- soup_auth = g_object_new (
- E_TYPE_SOUP_AUTH_BEARER,
- SOUP_AUTH_HOST, soup_uri->host, NULL);
-
- success = e_source_get_oauth2_access_token_sync (
- source, cancellable, &access_token,
- &expires_in_seconds, error);
-
- if (success) {
- e_soup_auth_bearer_set_access_token (
- E_SOUP_AUTH_BEARER (soup_auth),
- access_token, expires_in_seconds);
-
- soup_auth_manager_use_auth (
- SOUP_AUTH_MANAGER (feature),
- soup_uri, soup_auth);
- }
-
- g_free (access_token);
- g_object_unref (soup_auth);
- soup_uri_free (soup_uri);
- }
-
- g_free (auth_method);
-
return success;
}
diff --git a/calendar/backends/gtasks/e-cal-backend-gtasks.c b/calendar/backends/gtasks/e-cal-backend-gtasks.c
index 912d122..88cc408 100644
--- a/calendar/backends/gtasks/e-cal-backend-gtasks.c
+++ b/calendar/backends/gtasks/e-cal-backend-gtasks.c
@@ -168,7 +168,8 @@ ecb_gtasks_is_authorized (ECalBackend *backend)
{
ECalBackendGTasks *gtasks = E_CAL_BACKEND_GTASKS (backend);
- if (!gtasks->priv->service)
+ if (!gtasks->priv->service ||
+ !gtasks->priv->tasklist)
return FALSE;
return gdata_service_is_authorized (GDATA_SERVICE (gtasks->priv->service));
@@ -181,23 +182,24 @@ ecb_gtasks_prepare_tasklist (ECalBackendGTasks *gtasks,
{
ESourceResource *resource;
ESource *source;
+ GDataFeed *feed;
+ GDataQuery *query;
gchar *id;
g_return_if_fail (E_IS_CAL_BACKEND_GTASKS (gtasks));
- g_return_if_fail (ecb_gtasks_is_authorized (E_CAL_BACKEND (gtasks)));
+ g_return_if_fail (gtasks->priv->service != NULL);
+ g_return_if_fail (gdata_service_is_authorized (GDATA_SERVICE (gtasks->priv->service)));
source = e_backend_get_source (E_BACKEND (gtasks));
resource = e_source_get_extension (source, E_SOURCE_EXTENSION_RESOURCE);
id = e_source_resource_dup_identity (resource);
- /* If the tasklist ID is not set, then pick the first from the list, most likely the "Default List" */
- if (!id || !*id) {
- GDataFeed *feed;
- GDataQuery *query;
-
- query = gdata_query_new_with_limits (NULL, 0, 1);
- feed = gdata_tasks_service_query_all_tasklists (gtasks->priv->service, query, cancellable,
NULL, NULL, error);
- if (feed) {
+ query = gdata_query_new_with_limits (NULL, 0, 1);
+ /* This also verifies that the service can connect to the server with given credentials */
+ feed = gdata_tasks_service_query_all_tasklists (gtasks->priv->service, query, cancellable, NULL,
NULL, error);
+ if (feed) {
+ /* If the tasklist ID is not set, then pick the first from the list, most likely the "Default
List" */
+ if (!id || !*id) {
GList *entries;
entries = gdata_feed_get_entries (feed);
@@ -208,10 +210,10 @@ ecb_gtasks_prepare_tasklist (ECalBackendGTasks *gtasks,
id = g_strdup (gdata_entry_get_id (entry));
}
}
- g_clear_object (&feed);
}
- g_object_unref (query);
}
+ g_clear_object (&feed);
+ g_object_unref (query);
if (!id || !*id) {
/* But the tests for change will not work */
@@ -641,6 +643,7 @@ ecb_gtasks_time_to_refresh_data_cb (ESource *source,
static gboolean
ecb_gtasks_request_authorization (ECalBackend *backend,
+ const ENamedParameters *credentials,
GCancellable *cancellable,
GError **error)
{
@@ -651,21 +654,17 @@ ecb_gtasks_request_authorization (ECalBackend *backend,
if (!gtasks->priv->authorizer) {
ESource *source;
- ESourceAuthentication *extension;
EGDataOAuth2Authorizer *authorizer;
- const gchar *extension_name;
- gchar *method;
- extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
source = e_backend_get_source (E_BACKEND (backend));
- extension = e_source_get_extension (source, extension_name);
- method = e_source_authentication_dup_method (extension);
/* Only OAuth2 is supported with Google Tasks */
authorizer = e_gdata_oauth2_authorizer_new (source);
gtasks->priv->authorizer = GDATA_AUTHORIZER (authorizer);
+ }
- g_free (method);
+ if (E_IS_GDATA_OAUTH2_AUTHORIZER (gtasks->priv->authorizer)) {
+ e_gdata_oauth2_authorizer_set_credentials (E_GDATA_OAUTH2_AUTHORIZER
(gtasks->priv->authorizer), credentials);
}
if (!gtasks->priv->service) {
@@ -742,6 +741,71 @@ ecb_gtasks_get_backend_property (ECalBackend *backend,
}
static void
+ecb_gtasks_update_connection_sync (ECalBackendGTasks *gtasks,
+ const ENamedParameters *credentials,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ECalBackend *backend;
+ gboolean success;
+ GError *local_error = NULL;
+
+ g_return_if_fail (E_IS_CAL_BACKEND_GTASKS (gtasks));
+
+ backend = E_CAL_BACKEND (gtasks);
+
+ success = ecb_gtasks_request_authorization (backend, credentials, cancellable, &local_error);
+ if (success)
+ success = gdata_authorizer_refresh_authorization (gtasks->priv->authorizer, cancellable,
&local_error);
+
+ if (success) {
+ e_cal_backend_set_writable (backend, TRUE);
+
+ ecb_gtasks_prepare_tasklist (gtasks, cancellable, &local_error);
+ if (!local_error)
+ ecb_gtasks_start_update (gtasks);
+ } else {
+ e_cal_backend_set_writable (backend, FALSE);
+ }
+
+ if (local_error)
+ g_propagate_error (error, local_error);
+}
+
+static ESourceAuthenticationResult
+ecb_gtasks_authenticate_sync (EBackend *backend,
+ const ENamedParameters *credentials,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ECalBackendGTasks *gtasks;
+ ESourceAuthenticationResult result;
+ GError *local_error = NULL;
+
+ gtasks = E_CAL_BACKEND_GTASKS (backend);
+
+ ecb_gtasks_update_connection_sync (gtasks, credentials, cancellable, &local_error);
+
+ if (local_error == NULL) {
+ result = E_SOURCE_AUTHENTICATION_ACCEPTED;
+
+ } else if (g_error_matches (local_error, GDATA_SERVICE_ERROR,
GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED)) {
+ if (!e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD))
+ result = E_SOURCE_AUTHENTICATION_REQUIRED;
+ else
+ result = E_SOURCE_AUTHENTICATION_REJECTED;
+ g_clear_error (&local_error);
+ } else {
+ result = E_SOURCE_AUTHENTICATION_ERROR;
+ g_propagate_error (error, local_error);
+ }
+
+ return result;
+}
+
+static void
ecb_gtasks_open (ECalBackend *backend,
EDataCal *cal,
guint32 opid,
@@ -766,19 +830,27 @@ ecb_gtasks_open (ECalBackend *backend,
ecb_gtasks_take_cancellable (gtasks, g_cancellable_new ());
if (e_backend_get_online (E_BACKEND (backend))) {
- gboolean success;
+ ESource *source;
+ gchar *auth_method = NULL;
+
+ source = e_backend_get_source (E_BACKEND (backend));
- success = ecb_gtasks_request_authorization (backend, cancellable, &local_error);
- if (success)
- success = gdata_authorizer_refresh_authorization (gtasks->priv->authorizer,
cancellable, &local_error);
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+ ESourceAuthentication *auth_extension;
- if (success) {
- e_cal_backend_set_writable (backend, TRUE);
+ auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+ auth_method = e_source_authentication_dup_method (auth_extension);
+ }
- ecb_gtasks_prepare_tasklist (gtasks, cancellable, &local_error);
- if (!local_error)
- ecb_gtasks_start_update (gtasks);
+ if (g_strcmp0 (auth_method, "Google") == 0) {
+ e_backend_credentials_required_sync (
+ E_BACKEND (backend), E_SOURCE_CREDENTIALS_REASON_REQUIRED,
+ NULL, 0, NULL, cancellable, &local_error);
+ } else {
+ ecb_gtasks_update_connection_sync (gtasks, NULL, cancellable, &local_error);
}
+
+ g_free (auth_method);
}
e_data_cal_respond_open (cal, opid, local_error);
@@ -1435,32 +1507,35 @@ static void
e_cal_backend_gtasks_class_init (ECalBackendGTasksClass *class)
{
GObjectClass *object_class;
- ECalBackendClass *backend_class;
-
- object_class = (GObjectClass *) class;
- backend_class = (ECalBackendClass *) class;
+ EBackendClass *backend_class;
+ ECalBackendClass *cal_backend_class;
g_type_class_add_private (class, sizeof (ECalBackendGTasksPrivate));
+ object_class = (GObjectClass *) class;
object_class->constructed = ecb_gtasks_constructed;
object_class->dispose = ecb_gtasks_dispose;
object_class->finalize = ecb_gtasks_finalize;
- backend_class->get_backend_property = ecb_gtasks_get_backend_property;
- backend_class->open = ecb_gtasks_open;
- backend_class->refresh = ecb_gtasks_refresh;
- backend_class->get_object = ecb_gtasks_get_object;
- backend_class->get_object_list = ecb_gtasks_get_object_list;
- backend_class->get_free_busy = ecb_gtasks_get_free_busy;
- backend_class->create_objects = ecb_gtasks_create_objects;
- backend_class->modify_objects = ecb_gtasks_modify_objects;
- backend_class->remove_objects = ecb_gtasks_remove_objects;
- backend_class->receive_objects = ecb_gtasks_receive_objects;
- backend_class->send_objects = ecb_gtasks_send_objects;
- backend_class->get_attachment_uris = ecb_gtasks_get_attachment_uris;
- backend_class->discard_alarm = ecb_gtasks_discard_alarm;
- backend_class->start_view = ecb_gtasks_start_view;
- backend_class->stop_view = ecb_gtasks_stop_view;
- backend_class->add_timezone = ecb_gtasks_add_timezone;
- backend_class->shutdown = ecb_gtasks_shutdown;
+ backend_class = (EBackendClass *) class;
+ backend_class->authenticate_sync = ecb_gtasks_authenticate_sync;
+
+ cal_backend_class = (ECalBackendClass *) class;
+ cal_backend_class->get_backend_property = ecb_gtasks_get_backend_property;
+ cal_backend_class->open = ecb_gtasks_open;
+ cal_backend_class->refresh = ecb_gtasks_refresh;
+ cal_backend_class->get_object = ecb_gtasks_get_object;
+ cal_backend_class->get_object_list = ecb_gtasks_get_object_list;
+ cal_backend_class->get_free_busy = ecb_gtasks_get_free_busy;
+ cal_backend_class->create_objects = ecb_gtasks_create_objects;
+ cal_backend_class->modify_objects = ecb_gtasks_modify_objects;
+ cal_backend_class->remove_objects = ecb_gtasks_remove_objects;
+ cal_backend_class->receive_objects = ecb_gtasks_receive_objects;
+ cal_backend_class->send_objects = ecb_gtasks_send_objects;
+ cal_backend_class->get_attachment_uris = ecb_gtasks_get_attachment_uris;
+ cal_backend_class->discard_alarm = ecb_gtasks_discard_alarm;
+ cal_backend_class->start_view = ecb_gtasks_start_view;
+ cal_backend_class->stop_view = ecb_gtasks_stop_view;
+ cal_backend_class->add_timezone = ecb_gtasks_add_timezone;
+ cal_backend_class->shutdown = ecb_gtasks_shutdown;
}
diff --git a/calendar/backends/gtasks/e-gdata-oauth2-authorizer.c
b/calendar/backends/gtasks/e-gdata-oauth2-authorizer.c
index c1c5632..094b30b 100644
--- a/calendar/backends/gtasks/e-gdata-oauth2-authorizer.c
+++ b/calendar/backends/gtasks/e-gdata-oauth2-authorizer.c
@@ -27,6 +27,7 @@ struct _EGDataOAuth2AuthorizerPrivate {
/* These members are protected by the global mutex. */
gchar *access_token;
GHashTable *authorization_domains;
+ ENamedParameters *credentials;
};
enum {
@@ -137,6 +138,8 @@ gdata_oauth2_authorizer_finalize (GObject *object)
g_hash_table_destroy (priv->authorization_domains);
g_weak_ref_clear (&priv->source);
+ e_named_parameters_free (priv->credentials);
+
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_gdata_oauth2_authorizer_parent_class)->
finalize (object);
@@ -240,8 +243,8 @@ gdata_oauth2_authorizer_refresh_authorization (GDataAuthorizer *authorizer,
g_free (*ptr_access_token);
*ptr_access_token = NULL;
- success = e_source_get_oauth2_access_token_sync (
- source, cancellable, ptr_access_token, NULL, error);
+ success = e_util_get_source_oauth2_access_token_sync (source, oauth2_authorizer->priv->credentials,
+ ptr_access_token, NULL, cancellable, error);
g_mutex_unlock (&mutex);
@@ -323,3 +326,36 @@ e_gdata_oauth2_authorizer_ref_source (EGDataOAuth2Authorizer *authorizer)
return g_weak_ref_get (&authorizer->priv->source);
}
+void
+e_gdata_oauth2_authorizer_set_credentials (EGDataOAuth2Authorizer *authorizer,
+ const ENamedParameters *credentials)
+{
+ g_return_if_fail (E_IS_GDATA_OAUTH2_AUTHORIZER (authorizer));
+
+ g_mutex_lock (&mutex);
+
+ e_named_parameters_free (authorizer->priv->credentials);
+ if (credentials)
+ authorizer->priv->credentials = e_named_parameters_new_clone (credentials);
+ else
+ authorizer->priv->credentials = NULL;
+
+ g_mutex_unlock (&mutex);
+}
+
+ENamedParameters *
+e_gdata_oauth2_authorizer_clone_credentials (EGDataOAuth2Authorizer *authorizer)
+{
+ ENamedParameters *credentials = NULL;
+
+ g_return_val_if_fail (E_IS_GDATA_OAUTH2_AUTHORIZER (authorizer), NULL);
+
+ g_mutex_lock (&mutex);
+
+ if (authorizer->priv->credentials)
+ credentials = e_named_parameters_new_clone (authorizer->priv->credentials);
+
+ g_mutex_unlock (&mutex);
+
+ return credentials;
+}
diff --git a/calendar/backends/gtasks/e-gdata-oauth2-authorizer.h
b/calendar/backends/gtasks/e-gdata-oauth2-authorizer.h
index ef908f6..0dfc582 100644
--- a/calendar/backends/gtasks/e-gdata-oauth2-authorizer.h
+++ b/calendar/backends/gtasks/e-gdata-oauth2-authorizer.h
@@ -62,6 +62,12 @@ EGDataOAuth2Authorizer *
(ESource *source);
ESource * e_gdata_oauth2_authorizer_ref_source
(EGDataOAuth2Authorizer *authorizer);
+void e_gdata_oauth2_authorizer_set_credentials
+ (EGDataOAuth2Authorizer *authorizer,
+ const ENamedParameters *credentials);
+ENamedParameters *
+ e_gdata_oauth2_authorizer_clone_credentials
+ (EGDataOAuth2Authorizer *authorizer);
G_END_DECLS
diff --git a/camel/providers/imapx/camel-imapx-command.c b/camel/providers/imapx/camel-imapx-command.c
index 0677736..0388c7d 100644
--- a/camel/providers/imapx/camel-imapx-command.c
+++ b/camel/providers/imapx/camel-imapx-command.c
@@ -390,6 +390,8 @@ camel_imapx_command_add_part (CamelIMAPXCommand *ic,
/* we presume we'll need to get additional data only if we're not authenticated yet */
g_object_ref (ob);
mechanism = camel_sasl_get_mechanism (CAMEL_SASL (ob));
+ if (g_strcmp0 (mechanism, "Google") == 0)
+ mechanism = "XOAUTH2";
g_string_append (buffer, mechanism);
if (!camel_sasl_get_authenticated ((CamelSasl *) ob))
type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
diff --git a/camel/providers/imapx/camel-imapx-server.c b/camel/providers/imapx/camel-imapx-server.c
index e70fd95..0d5356d 100644
--- a/camel/providers/imapx/camel-imapx-server.c
+++ b/camel/providers/imapx/camel-imapx-server.c
@@ -2874,7 +2874,8 @@ camel_imapx_server_authenticate_sync (CamelIMAPXServer *is,
g_object_unref (settings);
if (mechanism != NULL) {
- if (is->priv->cinfo && !g_hash_table_lookup (is->priv->cinfo->auth_types, mechanism)) {
+ if (is->priv->cinfo && !g_hash_table_lookup (is->priv->cinfo->auth_types, mechanism) && (
+ !g_str_equal (mechanism, "Google") || !g_hash_table_lookup (is->priv->cinfo->auth_types,
"XOAUTH2"))) {
g_set_error (
error, CAMEL_SERVICE_ERROR,
CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
diff --git a/camel/providers/smtp/camel-smtp-transport.c b/camel/providers/smtp/camel-smtp-transport.c
index cba3c8b..4d5e746 100644
--- a/camel/providers/smtp/camel-smtp-transport.c
+++ b/camel/providers/smtp/camel-smtp-transport.c
@@ -526,7 +526,7 @@ smtp_transport_connect_sync (CamelService *service,
session = camel_service_ref_session (service);
- if (g_hash_table_lookup (transport->authtypes, mechanism)) {
+ if (g_hash_table_lookup (transport->authtypes, g_strcmp0 (mechanism, "Google") == 0 ?
"XOAUTH2" : mechanism)) {
success = camel_session_authenticate_sync (
session, service, mechanism,
cancellable, error);
@@ -633,7 +633,7 @@ smtp_transport_authenticate_sync (CamelService *service,
if (challenge) {
auth_challenge = TRUE;
cmdbuf = g_strdup_printf (
- "AUTH %s %s\r\n", mechanism, challenge);
+ "AUTH %s %s\r\n", g_strcmp0 (mechanism, "Google") == 0 ? "XOAUTH2" : mechanism,
challenge);
g_free (challenge);
} else if (local_error) {
d (fprintf (stderr, "[SMTP] SASL challenge failed: %s", local_error->message));
diff --git a/configure.ac b/configure.ac
index 9c459ba..a91443f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -58,6 +58,8 @@ m4_define([goa_minimum_version], [3.8])
m4_define([gweather_minimum_version], [3.10])
m4_define([libaccounts_glib_minimum_version], [1.4])
m4_define([libsignon_glib_minimum_version], [1.8])
+m4_define([webkitgtk_minimum_version], [2.4.9])
+m4_define([json_glib_minimum_version], [1.0.4])
AC_SUBST([BASE_VERSION],[base_version])
AC_SUBST([API_VERSION],[api_version])
@@ -471,6 +473,42 @@ if test "x$enable_gtk" = xyes; then
fi
AM_CONDITIONAL(HAVE_GTK, [test x$enable_gtk = xyes])
+dnl **********************************
+dnl Check for WebKitGTK+ and json-glib
+dnl **********************************
+AC_ARG_ENABLE([google-auth],
+ [AS_HELP_STRING([--enable-google-auth],
+ [enable Google authentication (default=yes)])],
+ [enable_google_auth=$enableval], [enable_google_auth=yes])
+AC_MSG_CHECKING([if Google authentication support is enabled])
+AC_MSG_RESULT([$enable_google_auth])
+if test "x$enable_google_auth" = "xyes"; then
+ PKG_CHECK_MODULES(
+ [GOOGLE_AUTH], [webkitgtk-3.0 >= webkitgtk_minimum_version
+ json-glib-1.0 >= json_glib_minimum_version],,
+ [AC_MSG_ERROR([
+
+ Libraries to support Google authentication prompts not found,
+ or versions not new enough.
+
+ If you want to disable Google authentication support,
+ please append --disable-google-auth to configure.
+
+ ])])
+
+ AC_DEFINE(ENABLE_GOOGLE_AUTH, 1, [Define to 1 if Google autentication support is enabled.])
+ AC_ARG_WITH(google-client-id, [AS_HELP_STRING([--with-google-client-id], [Google OAuth 2.0 client
id])], [], [])
+ if test "x$with_google_client_id" = "x"; then
+ with_google_client_id=590402290962-2i0b7rqma8b9nmtfrcp7fa06g6cf7g74.apps.googleusercontent.com
+ fi
+ AC_ARG_WITH(google-client-secret, [AS_HELP_STRING([--with-google-client-secret], [Google OAuth 2.0
client secret])], [], [])
+ if test "x$with_google_client_secret" = "x"; then
+ with_google_client_secret=mtfUe5W8Aal9DcgVipOY1T9G
+ fi
+ AC_DEFINE_UNQUOTED(GOOGLE_CLIENT_ID, ["$with_google_client_id"], [Define Google OAuth 2.0 Client ID
to use])
+ AC_DEFINE_UNQUOTED(GOOGLE_CLIENT_SECRET, ["$with_google_client_secret"], [Define Google OAuth 2.0
Client Secret to use])
+fi
+AM_CONDITIONAL(ENABLE_GOOGLE_AUTH, [test x$enable_google_auth = xyes])
dnl ******************************************
dnl Check whether to build examples/demos
@@ -1884,6 +1922,7 @@ echo "
Vala bindings: $enable_vala_bindings
GNOME Online Accounts $enable_goa
Ubuntu Online Accounts $enable_uoa
+ Google Authentication $enable_google_auth
Google Contacts $enable_google
Google Tasks $have_gdata_0_15_1
GTK+: $enable_gtk
diff --git a/libedataserver/Makefile.am b/libedataserver/Makefile.am
index f19fdfc..57870d7 100644
--- a/libedataserver/Makefile.am
+++ b/libedataserver/Makefile.am
@@ -38,6 +38,7 @@ SHARED_COMPILER_FLAGS = \
$(SOUP_CFLAGS) \
$(CODE_COVERAGE_CFLAGS) \
$(ICU_CFLAGS) \
+ $(GOOGLE_AUTH_CFLAGS) \
$(NULL)
libedataserver_1_2_la_CPPFLAGS = \
@@ -82,6 +83,7 @@ libedataserver_1_2_la_SOURCES = \
e-source-contacts.c \
e-source-credentials-provider.c \
e-source-credentials-provider-impl.c \
+ e-source-credentials-provider-impl-google.c \
e-source-credentials-provider-impl-password.c \
e-source-goa.c \
e-source-ldap.c \
@@ -129,6 +131,7 @@ libedataserver_1_2_la_LIBADD = \
$(SOCKET_LIBS) \
$(SOUP_LIBS) \
$(ICU_LIBS) \
+ $(GOOGLE_AUTH_LIBS) \
$(NULL)
libedataserver_1_2_la_LDFLAGS = \
@@ -173,6 +176,7 @@ libedataserverinclude_HEADERS = \
e-source-contacts.h \
e-source-credentials-provider.h \
e-source-credentials-provider-impl.h \
+ e-source-credentials-provider-impl-google.h \
e-source-credentials-provider-impl-password.h \
e-source-enums.h \
e-source-enumtypes.h \
diff --git a/libedataserver/e-data-server-util.c b/libedataserver/e-data-server-util.c
index 60ce101..7dc777c 100644
--- a/libedataserver/e-data-server-util.c
+++ b/libedataserver/e-data-server-util.c
@@ -33,6 +33,8 @@
#include <glib-object.h>
#include "e-source.h"
+#include "e-source-authentication.h"
+#include "e-source-credentials-provider-impl-google.h"
#include "e-source-enumtypes.h"
#include "e-source-registry.h"
#include "camel/camel.h"
@@ -2834,3 +2836,39 @@ e_util_get_source_full_name (ESourceRegistry *registry,
return g_string_free (fullname, FALSE);
}
+
+gboolean
+e_util_get_source_oauth2_access_token_sync (struct _ESource *source,
+ const ENamedParameters *credentials,
+ gchar **out_access_token,
+ gint *out_expires_in_seconds,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gchar *auth_method = NULL;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+ ESourceAuthentication *extension;
+
+ extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+ auth_method = e_source_authentication_dup_method (extension);
+ }
+
+ if (g_strcmp0 (auth_method, "OAuth2") == 0) {
+ success = e_source_get_oauth2_access_token_sync (
+ source, cancellable, out_access_token,
+ out_expires_in_seconds, error);
+ } else if (g_strcmp0 (auth_method, "Google") == 0) {
+ success = TRUE;
+
+ e_source_credentials_google_util_extract_from_credentials (
+ credentials, out_access_token, out_expires_in_seconds);
+ }
+
+ g_free (auth_method);
+
+ return success;
+}
diff --git a/libedataserver/e-data-server-util.h b/libedataserver/e-data-server-util.h
index 42115a2..6b26f12 100644
--- a/libedataserver/e-data-server-util.h
+++ b/libedataserver/e-data-server-util.h
@@ -273,7 +273,13 @@ void e_type_traverse (GType parent_type,
gchar * e_util_get_source_full_name (struct _ESourceRegistry *registry,
struct _ESource *source);
-
+gboolean e_util_get_source_oauth2_access_token_sync
+ (struct _ESource *source,
+ const ENamedParameters *credentials,
+ gchar **out_access_token,
+ gint *out_expires_in_seconds,
+ GCancellable *cancellable,
+ GError **error);
G_END_DECLS
#endif /* E_DATA_SERVER_UTIL_H */
diff --git a/libedataserver/e-source-credentials-provider-impl-google.c
b/libedataserver/e-source-credentials-provider-impl-google.c
new file mode 100644
index 0000000..5761a17
--- /dev/null
+++ b/libedataserver/e-source-credentials-provider-impl-google.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <libedataserver/libedataserver.h>
+
+#ifdef ENABLE_GOOGLE_AUTH
+#include <json-glib/json-glib.h>
+#endif
+
+#include "e-source-credentials-provider-impl-google.h"
+
+struct _ESourceCredentialsProviderImplGooglePrivate {
+ gboolean dummy;
+};
+
+G_DEFINE_TYPE (ESourceCredentialsProviderImplGoogle, e_source_credentials_provider_impl_google,
E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL)
+
+static gboolean
+e_source_credentials_provider_impl_google_can_process (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source)
+{
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ return e_source_credentials_google_util_generate_secret_uid (source, NULL);
+}
+
+static gboolean
+e_source_credentials_provider_impl_google_can_store (ESourceCredentialsProviderImpl *provider_impl)
+{
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE (provider_impl), FALSE);
+
+ return TRUE;
+}
+
+static gboolean
+e_source_credentials_provider_impl_google_can_prompt (ESourceCredentialsProviderImpl *provider_impl)
+{
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE (provider_impl), FALSE);
+
+ return TRUE;
+}
+
+static gboolean
+e_source_credentials_google_util_get_access_token_from_secret (const gchar *secret,
+ gchar **out_access_token)
+{
+ gchar *expires_after = NULL, *access_token = NULL;
+ gboolean success = FALSE;
+
+ if (!secret)
+ return FALSE;
+
+ if (!e_source_credentials_google_util_decode_from_secret (secret,
+ E_GOOGLE_SECRET_ACCESS_TOKEN, &access_token,
+ E_GOOGLE_SECRET_EXPIRES_AFTER, &expires_after,
+ NULL))
+ return FALSE;
+
+ if (access_token && expires_after &&
+ g_ascii_strtoll (expires_after, NULL, 10) > g_get_real_time () / G_USEC_PER_SEC) {
+ success = TRUE;
+
+ if (out_access_token) {
+ *out_access_token = access_token;
+ access_token = NULL;
+ }
+ }
+
+ g_free (expires_after);
+ g_free (access_token);
+
+ return success;
+}
+
+static gboolean
+e_source_credentials_provider_impl_google_lookup_sync (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ GCancellable *cancellable,
+ ENamedParameters **out_credentials,
+ GError **error)
+{
+ gchar *uid = NULL, *secret = NULL, *access_token = NULL;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (out_credentials != NULL, FALSE);
+
+ *out_credentials = NULL;
+
+ if (!e_source_credentials_google_is_supported ()) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Google authentication is not supported"));
+ return FALSE;
+ }
+
+ if (!e_source_credentials_google_util_generate_secret_uid (source, &uid)) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ /* Translators: The first %s is a display name of the source, the second is its UID.
*/
+ _("Source '%s' (%s) is not a valid Google source"),
+ e_source_get_display_name (source),
+ e_source_get_uid (source));
+ return FALSE;
+ }
+
+ if (!e_secret_store_lookup_sync (uid, &secret, cancellable, error)) {
+ g_free (uid);
+ return FALSE;
+ }
+
+ g_free (uid);
+
+ if (!secret) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("Google secret not found"));
+ return FALSE;
+ }
+
+ if (e_source_credentials_google_util_get_access_token_from_secret (secret, &access_token)) {
+ *out_credentials = e_named_parameters_new ();
+ e_named_parameters_set (*out_credentials, E_SOURCE_CREDENTIAL_GOOGLE_SECRET, secret);
+ e_named_parameters_set (*out_credentials, E_SOURCE_CREDENTIAL_PASSWORD, access_token);
+ }
+
+ e_util_safe_free_string (access_token);
+ e_util_safe_free_string (secret);
+
+ return *out_credentials != NULL;
+}
+
+static gboolean
+e_source_credentials_provider_impl_google_store_sync (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ const ENamedParameters *credentials,
+ gboolean permanently,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gchar *uid = NULL, *label;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (credentials != NULL, FALSE);
+ g_return_val_if_fail (e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_GOOGLE_SECRET) !=
NULL, FALSE);
+
+ if (!e_source_credentials_google_is_supported ()) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Google authentication is not supported"));
+ return FALSE;
+ }
+
+ if (!e_source_credentials_google_util_generate_secret_uid (source, &uid)) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ /* Translators: The first %s is a display name of the source, the second is its UID.
*/
+ _("Source '%s' (%s) is not a valid Google source"),
+ e_source_get_display_name (source),
+ e_source_get_uid (source));
+ return FALSE;
+ }
+
+ label = g_strdup_printf ("Evolution Data Source - %s", strstr (uid, "::") + 2);
+
+ success = e_secret_store_store_sync (uid,
+ e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_GOOGLE_SECRET),
+ label, permanently, cancellable, error);
+
+ g_free (label);
+ g_free (uid);
+
+ return success;
+}
+
+static gboolean
+e_source_credentials_provider_impl_google_delete_sync (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gchar *uid = NULL;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ if (!e_source_credentials_google_is_supported ()) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Google authentication is not supported"));
+ return FALSE;
+ }
+
+ if (!e_source_credentials_google_util_generate_secret_uid (source, &uid)) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ /* Translators: The first %s is a display name of the source, the second is its UID.
*/
+ _("Source '%s' (%s) is not a valid Google source"),
+ e_source_get_display_name (source),
+ e_source_get_uid (source));
+ return FALSE;
+ }
+
+ success = e_secret_store_delete_sync (uid, cancellable, error);
+
+ g_free (uid);
+
+ return success;
+}
+
+static void
+e_source_credentials_provider_impl_google_class_init (ESourceCredentialsProviderImplGoogleClass *klass)
+{
+ ESourceCredentialsProviderImplClass *impl_class;
+
+ g_type_class_add_private (klass, sizeof (ESourceCredentialsProviderImplGooglePrivate));
+
+ impl_class = E_SOURCE_CREDENTIALS_PROVIDER_IMPL_CLASS (klass);
+ impl_class->can_process = e_source_credentials_provider_impl_google_can_process;
+ impl_class->can_store = e_source_credentials_provider_impl_google_can_store;
+ impl_class->can_prompt = e_source_credentials_provider_impl_google_can_prompt;
+ impl_class->lookup_sync = e_source_credentials_provider_impl_google_lookup_sync;
+ impl_class->store_sync = e_source_credentials_provider_impl_google_store_sync;
+ impl_class->delete_sync = e_source_credentials_provider_impl_google_delete_sync;
+}
+
+static void
+e_source_credentials_provider_impl_google_init (ESourceCredentialsProviderImplGoogle *provider_impl)
+{
+ provider_impl->priv = G_TYPE_INSTANCE_GET_PRIVATE (provider_impl,
+ E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE, ESourceCredentialsProviderImplGooglePrivate);
+}
+
+gboolean
+e_source_credentials_google_is_supported (void)
+{
+#ifdef ENABLE_GOOGLE_AUTH
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+gboolean
+e_source_credentials_google_util_generate_secret_uid (ESource *source,
+ gchar **out_uid)
+{
+ ESourceAuthentication *authentication_extension;
+ gchar *auth_method, *user;
+ gboolean is_google_auth;
+
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ if (out_uid)
+ *out_uid = NULL;
+
+ if (!e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION))
+ return FALSE;
+
+ authentication_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+ auth_method = e_source_authentication_dup_method (authentication_extension);
+ is_google_auth = g_strcmp0 (auth_method, "Google") == 0;
+
+ g_free (auth_method);
+
+ if (!is_google_auth)
+ return FALSE;
+
+ user = e_source_authentication_dup_user (authentication_extension);
+ if (!user || !*user) {
+ g_free (user);
+ return FALSE;
+ }
+
+ if (out_uid)
+ *out_uid = g_strdup_printf ("OAuth2::Google[%s]", user);
+
+ g_free (user);
+
+ return TRUE;
+}
+
+gboolean
+e_source_credentials_google_util_encode_to_secret (gchar **out_secret,
+ const gchar *key1_name,
+ const gchar *value1,
+ ...)
+{
+#ifdef ENABLE_GOOGLE_AUTH
+ JsonBuilder *builder;
+ JsonNode *node;
+ const gchar *key, *value;
+ va_list va;
+
+ g_return_val_if_fail (out_secret != NULL, FALSE);
+ g_return_val_if_fail (key1_name != NULL, FALSE);
+ g_return_val_if_fail (value1 != NULL, FALSE);
+
+ *out_secret = NULL;
+
+ builder = json_builder_new ();
+
+ va_start (va, value1);
+ key = key1_name;
+ value = value1;
+
+ json_builder_begin_object (builder);
+
+ while (key && value) {
+ json_builder_set_member_name (builder, key);
+ json_builder_add_string_value (builder, value);
+
+ key = va_arg (va, const gchar *);
+ if (!key)
+ break;
+
+ value = va_arg (va, const gchar *);
+ g_warn_if_fail (value != NULL);
+ }
+
+ va_end (va);
+
+ json_builder_end_object (builder);
+ node = json_builder_get_root (builder);
+
+ g_object_unref (builder);
+
+ if (node) {
+ JsonGenerator *generator;
+
+ generator = json_generator_new ();
+ json_generator_set_root (generator, node);
+
+ *out_secret = json_generator_to_data (generator, NULL);
+
+ g_object_unref (generator);
+ json_node_free (node);
+ }
+
+ return *out_secret != NULL;
+#else
+ return FALSE;
+#endif
+}
+
+gboolean
+e_source_credentials_google_util_decode_from_secret (const gchar *secret,
+ const gchar *key1_name,
+ gchar **out_value1,
+ ...)
+{
+#ifdef ENABLE_GOOGLE_AUTH
+ JsonParser *parser;
+ JsonReader *reader;
+ const gchar *key;
+ gchar **out_value;
+ va_list va;
+ GError *error = NULL;
+
+ g_return_val_if_fail (key1_name != NULL, FALSE);
+ g_return_val_if_fail (out_value1 != NULL, FALSE);
+
+ if (!secret)
+ return FALSE;
+
+ parser = json_parser_new ();
+ if (!json_parser_load_from_data (parser, secret, -1, &error)) {
+ g_object_unref (parser);
+
+ g_debug ("%s: Failed to parse secret '%s': %s", G_STRFUNC, secret, error ? error->message :
"Unknown error");
+ g_clear_error (&error);
+
+ return FALSE;
+ }
+
+ reader = json_reader_new (json_parser_get_root (parser));
+ key = key1_name;
+ out_value = out_value1;
+
+ va_start (va, out_value1);
+
+ while (key && out_value) {
+ *out_value = NULL;
+
+ if (json_reader_read_member (reader, key)) {
+ *out_value = g_strdup (json_reader_get_string_value (reader));
+ if (!*out_value) {
+ const GError *reader_error = json_reader_get_error (reader);
+
+ if (g_error_matches (reader_error, JSON_READER_ERROR,
JSON_READER_ERROR_INVALID_TYPE)) {
+ gint64 iv64;
+
+ json_reader_end_member (reader);
+
+ iv64 = json_reader_get_int_value (reader);
+
+ if (!json_reader_get_error (reader))
+ *out_value = g_strdup_printf ("%" G_GINT64_FORMAT, iv64);
+ }
+ }
+ }
+
+ json_reader_end_member (reader);
+
+ key = va_arg (va, const gchar *);
+ if (!key)
+ break;
+
+ out_value = va_arg (va, gchar **);
+ g_warn_if_fail (out_value != NULL);
+ }
+
+ g_object_unref (reader);
+ g_object_unref (parser);
+ va_end (va);
+
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+gboolean
+e_source_credentials_google_util_extract_from_credentials (const ENamedParameters *credentials,
+ gchar **out_access_token,
+ gint *out_expires_in_seconds)
+{
+ const gchar *secret;
+ gchar *expires_after = NULL;
+ gint64 expires_after_tm, now;
+
+ if (!credentials ||
+ !e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_GOOGLE_SECRET) ||
+ !e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_PASSWORD))
+ return FALSE;
+
+ secret = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_GOOGLE_SECRET);
+
+ if (!e_source_credentials_google_util_decode_from_secret (secret, E_GOOGLE_SECRET_EXPIRES_AFTER,
&expires_after, NULL) ||
+ !expires_after)
+ return FALSE;
+
+ expires_after_tm = g_ascii_strtoll (expires_after, NULL, 10);
+ g_free (expires_after);
+
+ if (out_access_token)
+ *out_access_token = g_strdup (e_named_parameters_get (credentials,
E_SOURCE_CREDENTIAL_PASSWORD));
+
+ if (out_expires_in_seconds) {
+ now = g_get_real_time () / G_USEC_PER_SEC;
+ if (now < expires_after_tm)
+ now = expires_after_tm;
+
+ *out_expires_in_seconds = (gint) (expires_after_tm - now);
+ }
+
+ return TRUE;
+}
diff --git a/libedataserver/e-source-credentials-provider-impl-google.h
b/libedataserver/e-source-credentials-provider-impl-google.h
new file mode 100644
index 0000000..4181c69
--- /dev/null
+++ b/libedataserver/e-source-credentials-provider-impl-google.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
+#endif
+
+#ifndef E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE_H
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libedataserver/e-source.h>
+#include <libedataserver/e-source-credentials-provider-impl.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE \
+ (e_source_credentials_provider_impl_google_get_type ())
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE, ESourceCredentialsProviderImplGoogle))
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE, ESourceCredentialsProviderImplGoogleClass))
+#define E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE))
+#define E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE))
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE, ESourceCredentialsProviderImplGoogleClass))
+
+/* The key names used in crendetials ENamedParameters structure */
+#define E_SOURCE_CREDENTIAL_GOOGLE_SECRET "Google-secret"
+
+/* Secret key names, saved by the code; not the names returned by the Google server */
+#define E_GOOGLE_SECRET_REFRESH_TOKEN "refresh_token"
+#define E_GOOGLE_SECRET_ACCESS_TOKEN "access_token"
+#define E_GOOGLE_SECRET_EXPIRES_AFTER "expires_after"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceCredentialsProviderImplGoogle ESourceCredentialsProviderImplGoogle;
+typedef struct _ESourceCredentialsProviderImplGoogleClass ESourceCredentialsProviderImplGoogleClass;
+typedef struct _ESourceCredentialsProviderImplGooglePrivate ESourceCredentialsProviderImplGooglePrivate;
+
+/**
+ * ESourceCredentialsProviderImplGoogle:
+ *
+ * Google based credentials provider implementation.
+ *
+ * Since: 3.20
+ **/
+struct _ESourceCredentialsProviderImplGoogle {
+ ESourceCredentialsProviderImpl parent;
+ ESourceCredentialsProviderImplGooglePrivate *priv;
+};
+
+struct _ESourceCredentialsProviderImplGoogleClass {
+ ESourceCredentialsProviderImplClass parent_class;
+};
+
+GType e_source_credentials_provider_impl_google_get_type (void);
+
+gboolean e_source_credentials_google_is_supported (void);
+gboolean e_source_credentials_google_util_generate_secret_uid (ESource *source,
+ gchar **out_uid);
+gboolean e_source_credentials_google_util_encode_to_secret (gchar **out_secret,
+ const gchar *key1_name,
+ const gchar *value1,
+ ...) G_GNUC_NULL_TERMINATED;
+gboolean e_source_credentials_google_util_decode_from_secret (const gchar *secret,
+ const gchar *key1_name,
+ gchar **out_value1,
+ ...) G_GNUC_NULL_TERMINATED;
+gboolean e_source_credentials_google_util_extract_from_credentials
+ (const ENamedParameters *credentials,
+ gchar **out_access_token,
+ gint *out_expires_in_seconds);
+G_END_DECLS
+
+#endif /* E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE_H */
diff --git a/libedataserver/e-source-credentials-provider.c b/libedataserver/e-source-credentials-provider.c
index 2d3abfc..93a466e 100644
--- a/libedataserver/e-source-credentials-provider.c
+++ b/libedataserver/e-source-credentials-provider.c
@@ -35,6 +35,7 @@
/* built-in source credentials provider implementations */
#include "e-source-credentials-provider-impl-password.h"
+#include "e-source-credentials-provider-impl-google.h"
struct _ESourceCredentialsProviderPrivate {
GWeakRef registry; /* The property can hold both client and server-side registry */
@@ -201,6 +202,7 @@ e_source_credentials_provider_class_init (ESourceCredentialsProviderClass *class
/* Ensure built-in credential providers implementation types */
g_type_ensure (E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD);
+ g_type_ensure (E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_GOOGLE);
}
static void
diff --git a/libedataserver/e-webdav-discover.c b/libedataserver/e-webdav-discover.c
index 0fc13bf..5ff6b35 100644
--- a/libedataserver/e-webdav-discover.c
+++ b/libedataserver/e-webdav-discover.c
@@ -30,6 +30,7 @@
#include "e-soup-auth-bearer.h"
#include "e-soup-ssl-trust.h"
#include "e-source-authentication.h"
+#include "e-source-credentials-provider-impl-google.h"
#include "e-source-webdav.h"
#include "e-webdav-discover.h"
@@ -337,6 +338,31 @@ e_webdav_discover_get_xpath_string (xmlXPathContextPtr xp_ctx,
return string;
}
+static gboolean
+e_webdav_discover_setup_bearer_auth (ESource *source,
+ const ENamedParameters *credentials,
+ ESoupAuthBearer *bearer,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gchar *access_token = NULL;
+ gint expires_in_seconds = -1;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (credentials != NULL, FALSE);
+
+ success = e_util_get_source_oauth2_access_token_sync (source, credentials,
+ &access_token, &expires_in_seconds, cancellable, error);
+
+ if (success)
+ e_soup_auth_bearer_set_access_token (bearer, access_token, expires_in_seconds);
+
+ g_free (access_token);
+
+ return success;
+}
+
typedef struct _AuthenticateData {
ESource *source;
const ENamedParameters *credentials;
@@ -357,26 +383,16 @@ e_webdav_discover_authenticate_cb (SoupSession *session,
return;
if (E_IS_SOUP_AUTH_BEARER (auth)) {
- gchar *access_token = NULL;
- gint expires_in_seconds = -1;
GError *local_error = NULL;
- e_source_get_oauth2_access_token_sync (
- auth_data->source, NULL, &access_token,
- &expires_in_seconds, &local_error);
-
- e_soup_auth_bearer_set_access_token (
- E_SOUP_AUTH_BEARER (auth),
- access_token, expires_in_seconds);
+ e_webdav_discover_setup_bearer_auth (auth_data->source, auth_data->credentials,
+ E_SOUP_AUTH_BEARER (auth), NULL, &local_error);
if (local_error != NULL) {
soup_message_set_status_full (msg, SOUP_STATUS_FORBIDDEN, local_error->message);
g_error_free (local_error);
}
-
- g_free (access_token);
-
} else {
gchar *auth_user = NULL;
@@ -1792,28 +1808,20 @@ e_webdav_discover_sources_sync (ESource *source,
auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
auth_method = e_source_authentication_dup_method (auth_extension);
- if (g_strcmp0 (auth_method, "OAuth2") == 0) {
+ if (g_strcmp0 (auth_method, "OAuth2") == 0 || g_strcmp0 (auth_method, "Google") == 0) {
SoupAuth *soup_auth;
- gchar *access_token = NULL;
- gint expires_in_seconds = -1;
soup_auth = g_object_new (E_TYPE_SOUP_AUTH_BEARER, SOUP_AUTH_HOST, soup_uri->host,
NULL);
- success = e_source_get_oauth2_access_token_sync (
- source, cancellable, &access_token,
- &expires_in_seconds, error);
+ success = e_webdav_discover_setup_bearer_auth (source, credentials,
+ E_SOUP_AUTH_BEARER (soup_auth), cancellable, error);
if (success) {
- e_soup_auth_bearer_set_access_token (
- E_SOUP_AUTH_BEARER (soup_auth),
- access_token, expires_in_seconds);
-
soup_auth_manager_use_auth (
SOUP_AUTH_MANAGER (feature),
soup_uri, soup_auth);
}
- g_free (access_token);
g_object_unref (soup_auth);
}
diff --git a/libedataserver/libedataserver.h b/libedataserver/libedataserver.h
index 8410d81..4c6e053 100644
--- a/libedataserver/libedataserver.h
+++ b/libedataserver/libedataserver.h
@@ -54,6 +54,7 @@
#include <libedataserver/e-source-contacts.h>
#include <libedataserver/e-source-credentials-provider.h>
#include <libedataserver/e-source-credentials-provider-impl.h>
+#include <libedataserver/e-source-credentials-provider-impl-google.h>
#include <libedataserver/e-source-credentials-provider-impl-password.h>
#include <libedataserver/e-source-enums.h>
#include <libedataserver/e-source-enumtypes.h>
diff --git a/libedataserverui/Makefile.am b/libedataserverui/Makefile.am
index 51578fd..21beed4 100644
--- a/libedataserverui/Makefile.am
+++ b/libedataserverui/Makefile.am
@@ -16,6 +16,7 @@ libedataserverui_1_2_la_CPPFLAGS = \
$(GCR_BASE_CFLAGS) \
$(GCR_CFLAGS) \
$(GTK_CFLAGS) \
+ $(GOOGLE_AUTH_CFLAGS) \
$(NULL)
libedataserverui_1_2_la_LIBADD = \
@@ -30,6 +31,7 @@ libedataserverui_1_2_la_LIBADD = \
$(GCR_BASE_LIBS) \
$(GCR_LIBS) \
$(GTK_LIBS) \
+ $(GOOGLE_AUTH_LIBS) \
$(NULL)
libedataserverui_1_2_la_LDFLAGS = \
@@ -44,6 +46,7 @@ libedataserveruiinclude_HEADERS = \
e-cell-renderer-color.h \
e-credentials-prompter.h \
e-credentials-prompter-impl.h \
+ e-credentials-prompter-impl-google.h \
e-credentials-prompter-impl-password.h \
e-trust-prompt.h \
e-webdav-discover-widget.h \
@@ -54,6 +57,7 @@ libedataserverui_1_2_la_SOURCES = \
e-cell-renderer-color.c \
e-credentials-prompter.c \
e-credentials-prompter-impl.c \
+ e-credentials-prompter-impl-google.c \
e-credentials-prompter-impl-password.c \
e-trust-prompt.c \
e-webdav-discover-widget.c \
diff --git a/libedataserverui/e-credentials-prompter-impl-google.c
b/libedataserverui/e-credentials-prompter-impl-google.c
new file mode 100644
index 0000000..4fadef5
--- /dev/null
+++ b/libedataserverui/e-credentials-prompter-impl-google.c
@@ -0,0 +1,1127 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <gtk/gtk.h>
+
+#include <libedataserver/libedataserver.h>
+
+#include "e-credentials-prompter.h"
+#include "e-credentials-prompter-impl-google.h"
+
+#ifdef ENABLE_GOOGLE_AUTH
+#include <webkit/webkit.h>
+
+/* https://developers.google.com/identity/protocols/OAuth2InstalledApp */
+#define GOOGLE_AUTH_URI "https://accounts.google.com/o/oauth2/auth"
+#define GOOGLE_TOKEN_URI "https://www.googleapis.com/oauth2/v3/token"
+#define GOOGLE_REDIRECT_URI "urn:ietf:wg:oauth:2.0:oob"
+
+static const gchar *GOOGLE_SCOPE =
+ /* GMail IMAP and SMTP access */
+ "https://mail.google.com/ "
+ /* Google Calendar API (CalDAV and GData) */
+ "https://www.googleapis.com/auth/calendar "
+ /* Google Contacts API (GData) */
+ "https://www.google.com/m8/feeds/ "
+ /* Google Contacts API (CardDAV) - undocumented */
+ "https://www.googleapis.com/auth/carddav "
+ /* Google Tasks - undocumented */
+ "https://www.googleapis.com/auth/tasks";
+#endif /* ENABLE_GOOGLE_AUTH */
+
+struct _ECredentialsPrompterImplGooglePrivate {
+ GMutex property_lock;
+
+ gpointer prompt_id;
+ ESource *auth_source;
+ ESource *cred_source;
+ gchar *error_text;
+ ENamedParameters *credentials;
+
+ GtkDialog *dialog;
+#ifdef ENABLE_GOOGLE_AUTH
+ WebKitWebView *web_view;
+#endif
+ gulong show_dialog_idle_id;
+
+ GCancellable *cancellable;
+};
+
+G_DEFINE_TYPE (ECredentialsPrompterImplGoogle, e_credentials_prompter_impl_google,
E_TYPE_CREDENTIALS_PROMPTER_IMPL)
+
+#ifdef ENABLE_GOOGLE_AUTH
+static gchar *
+cpi_google_create_auth_uri (ESource *source)
+{
+ GHashTable *form;
+ SoupURI *soup_uri;
+ gchar *uri, *user = NULL;
+
+ g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+ soup_uri = soup_uri_new (GOOGLE_AUTH_URI);
+ form = g_hash_table_new (g_str_hash, g_str_equal);
+
+ #define add_to_form(name, value) g_hash_table_insert (form, (gpointer) name, (gpointer) value)
+
+ add_to_form ("response_type", "code");
+ add_to_form ("client_id", GOOGLE_CLIENT_ID);
+ add_to_form ("redirect_uri", GOOGLE_REDIRECT_URI);
+ add_to_form ("scope", GOOGLE_SCOPE);
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+ ESourceAuthentication *auth_extension;
+
+ auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+ user = e_source_authentication_dup_user (auth_extension);
+
+ if (user && *user)
+ add_to_form ("login_hint", user);
+ }
+
+ add_to_form ("include_granted_scopes", "false");
+
+ #undef add_to_form
+
+ soup_uri_set_query_from_form (soup_uri, form);
+
+ uri = soup_uri_to_string (soup_uri, FALSE);
+
+ soup_uri_free (soup_uri);
+ g_hash_table_destroy (form);
+ g_free (user);
+
+ return uri;
+}
+
+static gchar *
+cpi_google_create_token_post_data (const gchar *authorization_code)
+{
+ g_return_val_if_fail (authorization_code != NULL, NULL);
+
+ return soup_form_encode (
+ "code", authorization_code,
+ "client_id", GOOGLE_CLIENT_ID,
+ "client_secret", GOOGLE_CLIENT_SECRET,
+ "redirect_uri", GOOGLE_REDIRECT_URI,
+ "grant_type", "authorization_code",
+ NULL);
+}
+
+static gchar *
+cpi_google_create_refresh_token_post_data (const gchar *refresh_token)
+{
+ g_return_val_if_fail (refresh_token != NULL, NULL);
+
+ return soup_form_encode (
+ "refresh_token", refresh_token,
+ "client_id", GOOGLE_CLIENT_ID,
+ "client_secret", GOOGLE_CLIENT_SECRET,
+ "grant_type", "refresh_token",
+ NULL);
+}
+
+static void
+cpi_google_setup_proxy_resolver (SoupSession *session,
+ ESourceRegistry *registry,
+ ESource *cred_source)
+{
+ ESourceAuthentication *auth_extension;
+ ESource *source = NULL;
+ gchar *uid;
+
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+ g_return_if_fail (E_IS_SOURCE (cred_source));
+
+ if (!e_source_has_extension (cred_source, E_SOURCE_EXTENSION_AUTHENTICATION))
+ return;
+
+ auth_extension = e_source_get_extension (cred_source, E_SOURCE_EXTENSION_AUTHENTICATION);
+ uid = e_source_authentication_dup_proxy_uid (auth_extension);
+ if (uid != NULL) {
+ source = e_source_registry_ref_source (registry, uid);
+
+ g_free (uid);
+ }
+
+ if (source != NULL) {
+ GProxyResolver *proxy_resolver;
+
+ proxy_resolver = G_PROXY_RESOLVER (source);
+ if (g_proxy_resolver_is_supported (proxy_resolver))
+ g_object_set (session, SOUP_SESSION_PROXY_RESOLVER, proxy_resolver, NULL);
+
+ g_object_unref (source);
+ }
+}
+
+static void
+cpi_google_abort_session_cb (GCancellable *cancellable,
+ SoupSession *session)
+{
+ soup_session_abort (session);
+}
+
+static guint
+cpi_google_post_data_sync (const gchar *uri,
+ const gchar *post_data,
+ ESourceRegistry *registry,
+ ESource *cred_source,
+ GCancellable *cancellable,
+ gchar **out_response_data)
+{
+ SoupSession *session;
+ SoupMessage *message;
+ guint status_code = SOUP_STATUS_CANCELLED;
+
+ g_return_val_if_fail (uri != NULL, SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (post_data != NULL, SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (E_IS_SOURCE (cred_source), SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (out_response_data != NULL, SOUP_STATUS_MALFORMED);
+
+ *out_response_data = NULL;
+
+ message = soup_message_new (SOUP_METHOD_POST, uri);
+ g_return_val_if_fail (message != NULL, SOUP_STATUS_MALFORMED);
+
+ soup_message_set_request (message, "application/x-www-form-urlencoded",
+ SOUP_MEMORY_TEMPORARY, post_data, strlen (post_data));
+
+ e_soup_ssl_trust_connect (message, cred_source);
+
+ session = soup_session_new ();
+ g_object_set (
+ session,
+ SOUP_SESSION_TIMEOUT, 90,
+ SOUP_SESSION_SSL_STRICT, TRUE,
+ SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
+ NULL);
+
+ cpi_google_setup_proxy_resolver (session, registry, cred_source);
+
+ soup_message_headers_append (message->request_headers, "Connection", "close");
+
+ if (!g_cancellable_is_cancelled (cancellable)) {
+ gulong cancel_handler_id = 0;
+
+ if (cancellable)
+ cancel_handler_id = g_cancellable_connect (cancellable, G_CALLBACK
(cpi_google_abort_session_cb), session, NULL);
+
+ status_code = soup_session_send_message (session, message);
+
+ if (cancel_handler_id)
+ g_cancellable_disconnect (cancellable, cancel_handler_id);
+ }
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (status_code)) {
+ if (message->response_body) {
+ *out_response_data = g_strndup (message->response_body->data,
message->response_body->length);
+ } else {
+ status_code = SOUP_STATUS_MALFORMED;
+ }
+ }
+
+ g_object_unref (message);
+ g_object_unref (session);
+
+ return status_code;
+}
+
+static gboolean
+cpi_google_update_prompter_credentials (GWeakRef *prompter_google_wr,
+ const gchar *refresh_token,
+ const gchar *access_token,
+ const gchar *expires_in,
+ GCancellable *cancellable)
+{
+ ECredentialsPrompterImplGoogle *prompter_google;
+ gint64 expires_after_tm;
+ gchar *expires_after;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (prompter_google_wr != NULL, FALSE);
+
+ if (!refresh_token || !access_token || !expires_in)
+ return FALSE;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return FALSE;
+
+ expires_after_tm = g_get_real_time () / G_USEC_PER_SEC;
+ expires_after_tm += g_ascii_strtoll (expires_in, NULL, 10);
+ expires_after = g_strdup_printf ("%" G_GINT64_FORMAT, expires_after_tm);
+
+ prompter_google = g_weak_ref_get (prompter_google_wr);
+ if (prompter_google && !g_cancellable_is_cancelled (cancellable)) {
+ gchar *secret = NULL;
+
+ if (e_source_credentials_google_util_encode_to_secret (&secret,
+ E_GOOGLE_SECRET_REFRESH_TOKEN, refresh_token,
+ E_GOOGLE_SECRET_ACCESS_TOKEN, access_token,
+ E_GOOGLE_SECRET_EXPIRES_AFTER, expires_after, NULL)) {
+ e_named_parameters_set (prompter_google->priv->credentials,
+ E_SOURCE_CREDENTIAL_GOOGLE_SECRET, secret);
+ e_named_parameters_set (prompter_google->priv->credentials,
+ E_SOURCE_CREDENTIAL_PASSWORD, access_token);
+
+ success = TRUE;
+ }
+
+ g_object_unref (prompter_google);
+ g_free (secret);
+ }
+
+ g_free (expires_after);
+
+ return success;
+}
+
+static void
+e_credentials_prompter_impl_google_show_html (WebKitWebView *web_view,
+ const gchar *title,
+ const gchar *body_text)
+{
+ gchar *html;
+
+ g_return_if_fail (WEBKIT_IS_WEB_VIEW (web_view));
+ g_return_if_fail (title != NULL);
+ g_return_if_fail (body_text != NULL);
+
+ html = g_strdup_printf (
+ "<html>"
+ "<head><title>%s</title></head>"
+ "<body><div style=\"font-size:12pt; font-family:Helvetica,Arial;\">%s</div></body>"
+ "</html>",
+ title,
+ body_text);
+ webkit_web_view_load_string (web_view, html, "text/html", "UTF-8", "none-local://");
+ g_free (html);
+}
+
+static gboolean
+e_credentials_prompter_impl_google_finish_dialog_idle_cb (gpointer user_data)
+{
+ ECredentialsPrompterImplGoogle *prompter_google = user_data;
+
+ if (g_source_is_destroyed (g_main_current_source ()))
+ return FALSE;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_google), FALSE);
+
+ if (g_source_get_id (g_main_current_source ()) == prompter_google->priv->show_dialog_idle_id) {
+ prompter_google->priv->show_dialog_idle_id = 0;
+
+ g_warn_if_fail (prompter_google->priv->dialog != NULL);
+
+ if (e_named_parameters_exists (prompter_google->priv->credentials,
E_SOURCE_CREDENTIAL_PASSWORD)) {
+ gtk_dialog_response (prompter_google->priv->dialog, GTK_RESPONSE_OK);
+ } else if (prompter_google->priv->error_text) {
+ e_credentials_prompter_impl_google_show_html (prompter_google->priv->web_view,
+ "Finished with error", prompter_google->priv->error_text);
+ }
+ }
+
+ return FALSE;
+}
+
+typedef struct {
+ GWeakRef *prompter_google; /* ECredentialsPrompterImplGoogle * */
+ GCancellable *cancellable;
+ ESource *cred_source;
+ ESourceRegistry *registry;
+ gchar *authorization_code;
+} AccessTokenThreadData;
+
+static void
+access_token_thread_data_free (gpointer user_data)
+{
+ AccessTokenThreadData *td = user_data;
+
+ if (td) {
+ e_weak_ref_free (td->prompter_google);
+ g_clear_object (&td->cancellable);
+ g_clear_object (&td->cred_source);
+ g_clear_object (&td->registry);
+ g_free (td->authorization_code);
+ g_free (td);
+ }
+}
+
+static gpointer
+cpi_google_get_access_token_thread (gpointer user_data)
+{
+ AccessTokenThreadData *td = user_data;
+ ECredentialsPrompterImplGoogle *prompter_google;
+ GCancellable *cancellable;
+ gchar *post_data, *response_json = NULL;
+ guint soup_status;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (td != NULL, NULL);
+
+ cancellable = td->cancellable;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ goto exit;
+
+
+ post_data = cpi_google_create_token_post_data (td->authorization_code);
+ g_warn_if_fail (post_data != NULL);
+ if (!post_data)
+ goto exit;
+
+ soup_status = cpi_google_post_data_sync (GOOGLE_TOKEN_URI, post_data,
+ td->registry, td->cred_source, cancellable, &response_json);
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (soup_status) && response_json) {
+ gchar *access_token = NULL, *refresh_token = NULL, *expires_in = NULL, *token_type = NULL;
+
+ if (e_source_credentials_google_util_decode_from_secret (response_json,
+ "access_token", &access_token,
+ "refresh_token", &refresh_token,
+ "expires_in", &expires_in,
+ "token_type", &token_type,
+ NULL) && access_token && refresh_token && expires_in && token_type) {
+
+ g_warn_if_fail (g_str_equal (token_type, "Bearer"));
+
+ if (!cpi_google_update_prompter_credentials (td->prompter_google,
+ refresh_token, access_token, expires_in, cancellable)) {
+ soup_status = SOUP_STATUS_MALFORMED;
+ }
+ } else {
+ soup_status = SOUP_STATUS_MALFORMED;
+ }
+
+ g_free (access_token);
+ g_free (refresh_token);
+ g_free (expires_in);
+ g_free (token_type);
+ }
+
+ g_free (response_json);
+ g_free (post_data);
+
+ exit:
+ prompter_google = g_weak_ref_get (td->prompter_google);
+ if (prompter_google && !g_cancellable_is_cancelled (cancellable)) {
+ if (!success) {
+ g_free (prompter_google->priv->error_text);
+ prompter_google->priv->error_text = NULL;
+
+ prompter_google->priv->error_text = g_strdup_printf (
+ _("Failed to obtain access token from address '%s'. Error code %d (%s)"),
+ GOOGLE_TOKEN_URI, soup_status, soup_status_get_phrase (soup_status));
+ }
+
+ prompter_google->priv->show_dialog_idle_id = g_idle_add (
+ e_credentials_prompter_impl_google_finish_dialog_idle_cb,
+ prompter_google);
+ }
+
+ g_clear_object (&prompter_google);
+
+ access_token_thread_data_free (td);
+
+ return NULL;
+}
+
+static void
+cpi_google_document_load_finished_cb (WebKitWebView *web_view,
+ WebKitWebFrame *frame,
+ ECredentialsPrompterImplGoogle *prompter_google)
+{
+ const gchar *title;
+
+ g_return_if_fail (WEBKIT_IS_WEB_VIEW (web_view));
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_google));
+
+ if (frame != webkit_web_view_get_main_frame (web_view))
+ return;
+
+ title = webkit_web_frame_get_title (frame);
+ if (!title)
+ return;
+
+ if (g_ascii_strncasecmp (title, "Denied ", 7) == 0) {
+ g_cancellable_cancel (prompter_google->priv->cancellable);
+ gtk_dialog_response (prompter_google->priv->dialog, GTK_RESPONSE_CANCEL);
+ return;
+ }
+
+ if (g_ascii_strncasecmp (title, "Success code=", 13) == 0) {
+ ECredentialsPrompter *prompter;
+ ECredentialsPrompterImpl *prompter_impl;
+ AccessTokenThreadData *td;
+ GThread *thread;
+
+ e_credentials_prompter_impl_google_show_html (web_view,
+ "Checking returned code", _("Requesting access token, please wait..."));
+
+ gtk_widget_set_sensitive (GTK_WIDGET (web_view), FALSE);
+
+ e_named_parameters_set (prompter_google->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD,
NULL);
+
+ prompter_impl = E_CREDENTIALS_PROMPTER_IMPL (prompter_google);
+ prompter = e_credentials_prompter_impl_get_credentials_prompter (prompter_impl);
+
+ td = g_new0 (AccessTokenThreadData, 1);
+ td->prompter_google = e_weak_ref_new (prompter_google);
+ td->cancellable = g_object_ref (prompter_google->priv->cancellable);
+ td->cred_source = g_object_ref (prompter_google->priv->cred_source);
+ td->registry = g_object_ref (e_credentials_prompter_get_registry (prompter));
+ td->authorization_code = g_strdup (title + 13);
+
+ thread = g_thread_new (G_STRFUNC, cpi_google_get_access_token_thread, td);
+ g_thread_unref (thread);
+
+ return;
+ }
+}
+
+static void
+cpi_google_notify_progress_cb (WebKitWebView *web_view,
+ GParamSpec *param,
+ GtkProgressBar *progress_bar)
+{
+ gboolean visible;
+ gdouble progress;
+
+ g_return_if_fail (GTK_IS_PROGRESS_BAR (progress_bar));
+
+ progress = webkit_web_view_get_progress (web_view);
+ visible = progress > 1e-9 && progress < 1 - 1e-9;
+
+ gtk_progress_bar_set_fraction (progress_bar, visible ? progress : 0.0);
+}
+
+static void
+credentials_prompter_impl_google_get_prompt_strings (ESourceRegistry *registry,
+ ESource *source,
+ gchar **prompt_title,
+ GString **prompt_description)
+{
+ GString *description;
+ const gchar *message;
+ gchar *display_name;
+
+ /* Known types */
+ enum {
+ TYPE_UNKNOWN,
+ TYPE_AMBIGUOUS,
+ TYPE_ADDRESS_BOOK,
+ TYPE_CALENDAR,
+ TYPE_MAIL_ACCOUNT,
+ TYPE_MAIL_TRANSPORT,
+ TYPE_MEMO_LIST,
+ TYPE_TASK_LIST
+ } type = TYPE_UNKNOWN;
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
+ type = TYPE_ADDRESS_BOOK;
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) {
+ if (type == TYPE_UNKNOWN)
+ type = TYPE_CALENDAR;
+ else
+ type = TYPE_AMBIGUOUS;
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
+ if (type == TYPE_UNKNOWN)
+ type = TYPE_MAIL_ACCOUNT;
+ else
+ type = TYPE_AMBIGUOUS;
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_TRANSPORT)) {
+ if (type == TYPE_UNKNOWN)
+ type = TYPE_MAIL_TRANSPORT;
+ else
+ type = TYPE_AMBIGUOUS;
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) {
+ if (type == TYPE_UNKNOWN)
+ type = TYPE_MEMO_LIST;
+ else
+ type = TYPE_AMBIGUOUS;
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) {
+ if (type == TYPE_UNKNOWN)
+ type = TYPE_TASK_LIST;
+ else
+ type = TYPE_AMBIGUOUS;
+ }
+
+ switch (type) {
+ case TYPE_ADDRESS_BOOK:
+ message = _("Google Address book authentication request");
+ break;
+ case TYPE_CALENDAR:
+ message = _("Google Calendar authentication request");
+ break;
+ case TYPE_MEMO_LIST:
+ message = _("Google Memo List authentication request");
+ break;
+ case TYPE_TASK_LIST:
+ message = _("Google Task List authentication request");
+ break;
+ case TYPE_MAIL_ACCOUNT:
+ case TYPE_MAIL_TRANSPORT:
+ message = _("Google Mail authentication request");
+ break;
+ default: /* generic account prompt */
+ message = _("Google account authentication request");
+ break;
+ }
+
+ display_name = e_util_get_source_full_name (registry, source);
+ description = g_string_sized_new (256);
+
+ g_string_append_printf (description, "<big><b>%s</b></big>\n\n", message);
+ switch (type) {
+ case TYPE_ADDRESS_BOOK:
+ g_string_append_printf (description,
+ _("Login to your Google account and accept conditions in order to access your
address book \"%s\"."), display_name);
+ break;
+ case TYPE_CALENDAR:
+ g_string_append_printf (description,
+ _("Login to your Google account and accept conditions in order to access your
calendar \"%s\"."), display_name);
+ break;
+ case TYPE_MAIL_ACCOUNT:
+ g_string_append_printf (description,
+ _("Login to your Google account and accept conditions in order to access your
mail account \"%s\"."), display_name);
+ break;
+ case TYPE_MAIL_TRANSPORT:
+ g_string_append_printf (description,
+ _("Login to your Google account and accept conditions in order to access your
mail transport \"%s\"."), display_name);
+ break;
+ case TYPE_MEMO_LIST:
+ g_string_append_printf (description,
+ _("Login to your Google account and accept conditions in order to access your
memo list \"%s\"."), display_name);
+ break;
+ case TYPE_TASK_LIST:
+ g_string_append_printf (description,
+ _("Login to your Google account and accept conditions in order to access your
task list \"%s\"."), display_name);
+ break;
+ default: /* generic account prompt */
+ g_string_append_printf (description,
+ _("Login to your Google account and accept conditions in order to access your
account \"%s\"."), display_name);
+ break;
+ }
+
+ *prompt_title = g_strdup (message);
+ *prompt_description = description;
+
+ g_free (display_name);
+}
+#endif /* ENABLE_GOOGLE_AUTH */
+
+static gboolean
+e_credentials_prompter_impl_google_show_dialog (ECredentialsPrompterImplGoogle *prompter_google)
+{
+#ifdef ENABLE_GOOGLE_AUTH
+ GtkWidget *dialog, *content_area, *widget, *progress_bar, *vbox;
+ GtkGrid *grid;
+ GtkScrolledWindow *scrolled_window;
+ GtkWindow *dialog_parent;
+ ECredentialsPrompter *prompter;
+ gchar *title, *uri;
+ GString *info_markup;
+ gint row = 0;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_google), FALSE);
+ g_return_val_if_fail (prompter_google->priv->prompt_id != NULL, FALSE);
+ g_return_val_if_fail (prompter_google->priv->dialog == NULL, FALSE);
+
+ prompter = e_credentials_prompter_impl_get_credentials_prompter (E_CREDENTIALS_PROMPTER_IMPL
(prompter_google));
+ g_return_val_if_fail (prompter != NULL, FALSE);
+
+ dialog_parent = e_credentials_prompter_get_dialog_parent (prompter);
+
+ credentials_prompter_impl_google_get_prompt_strings (e_credentials_prompter_get_registry (prompter),
+ prompter_google->priv->auth_source, &title, &info_markup);
+ if (prompter_google->priv->error_text && *prompter_google->priv->error_text) {
+ gchar *escaped = g_markup_printf_escaped ("%s", prompter_google->priv->error_text);
+
+ g_string_append_printf (info_markup, "\n\n%s", escaped);
+ g_free (escaped);
+ }
+
+ dialog = gtk_dialog_new_with_buttons (title, dialog_parent, GTK_DIALOG_MODAL |
GTK_DIALOG_DESTROY_WITH_PARENT,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ NULL);
+
+ gtk_window_set_default_size (GTK_WINDOW (dialog), 320, 480);
+
+ prompter_google->priv->dialog = GTK_DIALOG (dialog);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+ if (dialog_parent)
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), dialog_parent);
+ gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 12);
+
+ content_area = gtk_dialog_get_content_area (prompter_google->priv->dialog);
+
+ /* Override GtkDialog defaults */
+ gtk_box_set_spacing (GTK_BOX (content_area), 12);
+ gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_column_spacing (grid, 12);
+ gtk_grid_set_row_spacing (grid, 6);
+
+ gtk_box_pack_start (GTK_BOX (content_area), GTK_WIDGET (grid), FALSE, TRUE, 0);
+
+ /* Info Label */
+ widget = gtk_label_new (NULL);
+ gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+ gtk_label_set_markup (GTK_LABEL (widget), info_markup->str);
+ g_object_set (
+ G_OBJECT (widget),
+ "hexpand", TRUE,
+ "vexpand", FALSE,
+ "halign", GTK_ALIGN_FILL,
+ "valign", GTK_ALIGN_CENTER,
+ "width-chars", 60,
+ "max-width-chars", 80,
+ "xalign", 0.0,
+ NULL);
+
+ gtk_grid_attach (grid, widget, 0, row, 1, 1);
+ row++;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 1);
+ g_object_set (
+ G_OBJECT (vbox),
+ "hexpand", TRUE,
+ "vexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ "valign", GTK_ALIGN_FILL,
+ NULL);
+
+ gtk_grid_attach (grid, vbox, 0, row, 1, 1);
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ g_object_set (
+ G_OBJECT (widget),
+ "hexpand", TRUE,
+ "vexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ "valign", GTK_ALIGN_FILL,
+ "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+ "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
+
+ scrolled_window = GTK_SCROLLED_WINDOW (widget);
+
+ widget = webkit_web_view_new ();
+ g_object_set (
+ G_OBJECT (widget),
+ "hexpand", TRUE,
+ "vexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ "valign", GTK_ALIGN_FILL,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
+
+ prompter_google->priv->web_view = WEBKIT_WEB_VIEW (widget);
+
+ progress_bar = gtk_progress_bar_new ();
+ g_object_set (
+ G_OBJECT (progress_bar),
+ "hexpand", TRUE,
+ "vexpand", FALSE,
+ "halign", GTK_ALIGN_FILL,
+ "valign", GTK_ALIGN_START,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ "fraction", 0.0,
+ NULL);
+ gtk_style_context_add_class (gtk_widget_get_style_context (progress_bar), GTK_STYLE_CLASS_OSD);
+
+ gtk_box_pack_start (GTK_BOX (vbox), progress_bar, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (GTK_WIDGET (grid));
+
+ uri = cpi_google_create_auth_uri (prompter_google->priv->cred_source);
+ if (!uri) {
+ success = FALSE;
+ } else {
+ WebKitWebView *web_view = prompter_google->priv->web_view;
+ gulong load_finished_handler_id, progress_handler_id;
+
+ load_finished_handler_id = g_signal_connect (web_view, "document-load-finished",
+ G_CALLBACK (cpi_google_document_load_finished_cb), prompter_google);
+ progress_handler_id = g_signal_connect (web_view, "notify::progress",
+ G_CALLBACK (cpi_google_notify_progress_cb), progress_bar);
+
+ webkit_web_view_load_uri (web_view, uri);
+
+ success = gtk_dialog_run (prompter_google->priv->dialog) == GTK_RESPONSE_OK;
+
+ if (load_finished_handler_id)
+ g_signal_handler_disconnect (web_view, load_finished_handler_id);
+ if (progress_handler_id)
+ g_signal_handler_disconnect (web_view, progress_handler_id);
+ }
+
+ if (prompter_google->priv->cancellable)
+ g_cancellable_cancel (prompter_google->priv->cancellable);
+
+ prompter_google->priv->web_view = NULL;
+ prompter_google->priv->dialog = NULL;
+ gtk_widget_destroy (dialog);
+
+ g_string_free (info_markup, TRUE);
+ g_free (title);
+
+ return success;
+#else /* ENABLE_GOOGLE_AUTH */
+ return FALSE;
+#endif /* ENABLE_GOOGLE_AUTH */
+}
+
+static void
+e_credentials_prompter_impl_google_free_prompt_data (ECredentialsPrompterImplGoogle *prompter_google)
+{
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_google));
+
+ prompter_google->priv->prompt_id = NULL;
+
+ g_clear_object (&prompter_google->priv->auth_source);
+ g_clear_object (&prompter_google->priv->cred_source);
+
+ g_free (prompter_google->priv->error_text);
+ prompter_google->priv->error_text = NULL;
+
+ e_named_parameters_free (prompter_google->priv->credentials);
+ prompter_google->priv->credentials = NULL;
+}
+
+static gboolean
+e_credentials_prompter_impl_google_manage_dialog_idle_cb (gpointer user_data)
+{
+ ECredentialsPrompterImplGoogle *prompter_google = user_data;
+
+ if (g_source_is_destroyed (g_main_current_source ()))
+ return FALSE;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_google), FALSE);
+
+ if (g_source_get_id (g_main_current_source ()) == prompter_google->priv->show_dialog_idle_id) {
+ gboolean success;
+
+ prompter_google->priv->show_dialog_idle_id = 0;
+
+ g_warn_if_fail (prompter_google->priv->dialog == NULL);
+
+ if (e_named_parameters_exists (prompter_google->priv->credentials,
E_SOURCE_CREDENTIAL_PASSWORD))
+ success = TRUE;
+ else
+ success = e_credentials_prompter_impl_google_show_dialog (prompter_google);
+
+ e_credentials_prompter_impl_prompt_finish (
+ E_CREDENTIALS_PROMPTER_IMPL (prompter_google),
+ prompter_google->priv->prompt_id,
+ success ? prompter_google->priv->credentials : NULL);
+
+ e_credentials_prompter_impl_google_free_prompt_data (prompter_google);
+ }
+
+ return FALSE;
+}
+
+#ifdef ENABLE_GOOGLE_AUTH
+typedef struct {
+ GWeakRef *prompter_google; /* ECredentialsPrompterImplGoogle * */
+ GCancellable *cancellable;
+ ESource *cred_source;
+ ESourceRegistry *registry;
+ gchar *secret;
+} CheckExistingThreadData;
+
+static void
+check_existing_thread_data_free (gpointer user_data)
+{
+ CheckExistingThreadData *td = user_data;
+
+ if (td) {
+ e_weak_ref_free (td->prompter_google);
+ g_clear_object (&td->cancellable);
+ g_clear_object (&td->cred_source);
+ g_clear_object (&td->registry);
+ g_free (td->secret);
+ g_free (td);
+ }
+}
+
+static gpointer
+cpi_google_check_existing_token_thread (gpointer user_data)
+{
+ CheckExistingThreadData *td = user_data;
+ ECredentialsPrompterImplGoogle *prompter_google;
+ GCancellable *cancellable;
+ gchar *refresh_token = NULL;
+
+ g_return_val_if_fail (td != NULL, NULL);
+
+ cancellable = td->cancellable;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ goto exit;
+
+ if (!e_source_credentials_google_util_decode_from_secret (td->secret, E_GOOGLE_SECRET_REFRESH_TOKEN,
&refresh_token, NULL))
+ goto exit;
+
+ if (refresh_token) {
+ gchar *post_data, *response_json = NULL;
+ guint soup_status;
+
+ post_data = cpi_google_create_refresh_token_post_data (refresh_token);
+ g_warn_if_fail (post_data != NULL);
+ if (!post_data)
+ goto exit;
+
+ soup_status = cpi_google_post_data_sync (GOOGLE_TOKEN_URI, post_data,
+ td->registry, td->cred_source, cancellable, &response_json);
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (soup_status) && response_json) {
+ gchar *access_token = NULL, *expires_in = NULL;
+
+ if (e_source_credentials_google_util_decode_from_secret (response_json,
+ "access_token", &access_token,
+ "expires_in", &expires_in,
+ NULL) && access_token && expires_in) {
+ cpi_google_update_prompter_credentials (td->prompter_google,
+ refresh_token, access_token, expires_in, cancellable);
+ }
+
+ g_free (access_token);
+ g_free (expires_in);
+ }
+
+ g_free (response_json);
+ g_free (post_data);
+ }
+
+ exit:
+ prompter_google = g_weak_ref_get (td->prompter_google);
+ if (prompter_google && !g_cancellable_is_cancelled (cancellable)) {
+ prompter_google->priv->show_dialog_idle_id = g_idle_add (
+ e_credentials_prompter_impl_google_manage_dialog_idle_cb,
+ prompter_google);
+ }
+
+ g_clear_object (&prompter_google);
+ g_free (refresh_token);
+
+ check_existing_thread_data_free (td);
+
+ return NULL;
+}
+#endif /* ENABLE_GOOGLE_AUTH */
+
+static void
+e_credentials_prompter_impl_google_process_prompt (ECredentialsPrompterImpl *prompter_impl,
+ gpointer prompt_id,
+ ESource *auth_source,
+ ESource *cred_source,
+ const gchar *error_text,
+ const ENamedParameters *credentials)
+{
+ ECredentialsPrompterImplGoogle *prompter_google;
+
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_impl));
+
+ prompter_google = E_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_impl);
+ g_return_if_fail (prompter_google->priv->prompt_id == NULL);
+ g_return_if_fail (prompter_google->priv->show_dialog_idle_id == 0);
+
+ prompter_google->priv->prompt_id = prompt_id;
+ prompter_google->priv->auth_source = g_object_ref (auth_source);
+ prompter_google->priv->cred_source = g_object_ref (cred_source);
+ prompter_google->priv->error_text = g_strdup (error_text);
+ prompter_google->priv->credentials = e_named_parameters_new_clone (credentials);
+ prompter_google->priv->cancellable = g_cancellable_new ();
+
+#ifdef ENABLE_GOOGLE_AUTH
+ e_named_parameters_set (prompter_google->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD, NULL);
+
+ /* Read stored secret directly from the secret store, if not provided, because
+ e_source_credentials_provider_impl_google_lookup_sync() returns the secret
+ only if the stored access token is not expired.
+ */
+ if (!e_named_parameters_exists (prompter_google->priv->credentials,
E_SOURCE_CREDENTIAL_GOOGLE_SECRET)) {
+ gchar *secret_uid = NULL;
+
+ if (e_source_credentials_google_util_generate_secret_uid (cred_source, &secret_uid)) {
+ gchar *secret = NULL;
+
+ if (e_secret_store_lookup_sync (secret_uid, &secret,
prompter_google->priv->cancellable, NULL)) {
+ e_named_parameters_set (prompter_google->priv->credentials,
+ E_SOURCE_CREDENTIAL_GOOGLE_SECRET, secret);
+ }
+
+ g_free (secret);
+ }
+
+ g_free (secret_uid);
+ }
+
+ if (e_named_parameters_exists (prompter_google->priv->credentials,
E_SOURCE_CREDENTIAL_GOOGLE_SECRET)) {
+ ECredentialsPrompter *prompter;
+ CheckExistingThreadData *td;
+ GThread *thread;
+
+ prompter = e_credentials_prompter_impl_get_credentials_prompter (prompter_impl);
+
+ td = g_new0 (CheckExistingThreadData, 1);
+ td->prompter_google = e_weak_ref_new (prompter_google);
+ td->cancellable = g_object_ref (prompter_google->priv->cancellable);
+ td->secret = g_strdup (e_named_parameters_get (prompter_google->priv->credentials,
E_SOURCE_CREDENTIAL_GOOGLE_SECRET));
+ td->cred_source = g_object_ref (cred_source);
+ td->registry = g_object_ref (e_credentials_prompter_get_registry (prompter));
+
+ thread = g_thread_new (G_STRFUNC, cpi_google_check_existing_token_thread, td);
+ g_thread_unref (thread);
+ } else {
+#endif /* ENABLE_GOOGLE_AUTH */
+ prompter_google->priv->show_dialog_idle_id = g_idle_add (
+ e_credentials_prompter_impl_google_manage_dialog_idle_cb,
+ prompter_google);
+#ifdef ENABLE_GOOGLE_AUTH
+ }
+#endif /* ENABLE_GOOGLE_AUTH */
+}
+
+static void
+e_credentials_prompter_impl_google_cancel_prompt (ECredentialsPrompterImpl *prompter_impl,
+ gpointer prompt_id)
+{
+ ECredentialsPrompterImplGoogle *prompter_google;
+
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_impl));
+
+ prompter_google = E_CREDENTIALS_PROMPTER_IMPL_GOOGLE (prompter_impl);
+ g_return_if_fail (prompter_google->priv->prompt_id == prompt_id);
+
+ if (prompter_google->priv->cancellable)
+ g_cancellable_cancel (prompter_google->priv->cancellable);
+
+ /* This also closes the dialog. */
+ if (prompter_google->priv->dialog)
+ gtk_dialog_response (prompter_google->priv->dialog, GTK_RESPONSE_CANCEL);
+}
+
+static void
+e_credentials_prompter_impl_google_dispose (GObject *object)
+{
+ ECredentialsPrompterImplGoogle *prompter_google = E_CREDENTIALS_PROMPTER_IMPL_GOOGLE (object);
+
+ if (prompter_google->priv->show_dialog_idle_id) {
+ g_source_remove (prompter_google->priv->show_dialog_idle_id);
+ prompter_google->priv->show_dialog_idle_id = 0;
+ }
+
+ if (prompter_google->priv->cancellable) {
+ g_cancellable_cancel (prompter_google->priv->cancellable);
+ g_clear_object (&prompter_google->priv->cancellable);
+ }
+
+ g_warn_if_fail (prompter_google->priv->prompt_id == NULL);
+ g_warn_if_fail (prompter_google->priv->dialog == NULL);
+
+ e_credentials_prompter_impl_google_free_prompt_data (prompter_google);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_credentials_prompter_impl_google_parent_class)->dispose (object);
+}
+
+static void
+e_credentials_prompter_impl_google_finalize (GObject *object)
+{
+ ECredentialsPrompterImplGoogle *prompter_google = E_CREDENTIALS_PROMPTER_IMPL_GOOGLE (object);
+
+ g_mutex_clear (&prompter_google->priv->property_lock);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_credentials_prompter_impl_google_parent_class)->finalize (object);
+}
+
+static void
+e_credentials_prompter_impl_google_class_init (ECredentialsPrompterImplGoogleClass *class)
+{
+ static const gchar *authentication_methods[] = {
+ "Google",
+ NULL
+ };
+
+ GObjectClass *object_class;
+ ECredentialsPrompterImplClass *prompter_impl_class;
+
+ g_type_class_add_private (class, sizeof (ECredentialsPrompterImplGooglePrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = e_credentials_prompter_impl_google_dispose;
+ object_class->finalize = e_credentials_prompter_impl_google_finalize;
+
+ prompter_impl_class = E_CREDENTIALS_PROMPTER_IMPL_CLASS (class);
+ prompter_impl_class->authentication_methods = (const gchar * const *) authentication_methods;
+ prompter_impl_class->process_prompt = e_credentials_prompter_impl_google_process_prompt;
+ prompter_impl_class->cancel_prompt = e_credentials_prompter_impl_google_cancel_prompt;
+}
+
+static void
+e_credentials_prompter_impl_google_init (ECredentialsPrompterImplGoogle *prompter_google)
+{
+ prompter_google->priv = G_TYPE_INSTANCE_GET_PRIVATE (prompter_google,
+ E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE, ECredentialsPrompterImplGooglePrivate);
+
+ g_mutex_init (&prompter_google->priv->property_lock);
+}
+
+/**
+ * e_credentials_prompter_impl_google_new:
+ *
+ * Creates a new instance of an #ECredentialsPrompterImplGoogle.
+ *
+ * Returns: (transfer full): a newly created #ECredentialsPrompterImplGoogle,
+ * which should be freed with g_object_unref() when no longer needed.
+ *
+ * Since: 3.20
+ **/
+ECredentialsPrompterImpl *
+e_credentials_prompter_impl_google_new (void)
+{
+ return g_object_new (E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE, NULL);
+}
diff --git a/libedataserverui/e-credentials-prompter-impl-google.h
b/libedataserverui/e-credentials-prompter-impl-google.h
new file mode 100644
index 0000000..c4f3aea
--- /dev/null
+++ b/libedataserverui/e-credentials-prompter-impl-google.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBEDATASERVERUI_H_INSIDE__) && !defined (LIBEDATASERVERUI_COMPILATION)
+#error "Only <libedataserverui/libedataserverui.h> should be included directly."
+#endif
+
+#ifndef E_CREDENTIALS_PROMPTER_IMPL_GOOGLE_H
+#define E_CREDENTIALS_PROMPTER_IMPL_GOOGLE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libedataserverui/e-credentials-prompter-impl.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE \
+ (e_credentials_prompter_impl_google_get_type ())
+#define E_CREDENTIALS_PROMPTER_IMPL_GOOGLE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE, ECredentialsPrompterImplGoogle))
+#define E_CREDENTIALS_PROMPTER_IMPL_GOOGLE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE, ECredentialsPrompterImplGoogleClass))
+#define E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE))
+#define E_IS_CREDENTIALS_PROMPTER_IMPL_GOOGLE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE))
+#define E_CREDENTIALS_PROMPTER_IMPL_GOOGLE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE, ECredentialsPrompterImplGoogleClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECredentialsPrompterImplGoogle ECredentialsPrompterImplGoogle;
+typedef struct _ECredentialsPrompterImplGoogleClass ECredentialsPrompterImplGoogleClass;
+typedef struct _ECredentialsPrompterImplGooglePrivate ECredentialsPrompterImplGooglePrivate;
+
+/**
+ * ECredentialsPrompterImplGoogle:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.20
+ **/
+struct _ECredentialsPrompterImplGoogle {
+ ECredentialsPrompterImpl parent;
+ ECredentialsPrompterImplGooglePrivate *priv;
+};
+
+struct _ECredentialsPrompterImplGoogleClass {
+ ECredentialsPrompterImplClass parent_class;
+};
+
+GType e_credentials_prompter_impl_google_get_type (void) G_GNUC_CONST;
+ECredentialsPrompterImpl *
+ e_credentials_prompter_impl_google_new (void);
+
+G_END_DECLS
+
+#endif /* E_CREDENTIALS_PROMPTER_IMPL_GOOGLE_H */
diff --git a/libedataserverui/e-credentials-prompter-impl-password.c
b/libedataserverui/e-credentials-prompter-impl-password.c
index 582d167..512f32f 100644
--- a/libedataserverui/e-credentials-prompter-impl-password.c
+++ b/libedataserverui/e-credentials-prompter-impl-password.c
@@ -53,7 +53,8 @@ password_dialog_map_event_cb (GtkWidget *dialog,
}
static void
-credentials_prompter_impl_password_get_prompt_strings (ESource *source,
+credentials_prompter_impl_password_get_prompt_strings (ESourceRegistry *registry,
+ ESource *source,
gchar **prompt_title,
GString **prompt_description)
{
@@ -80,7 +81,7 @@ credentials_prompter_impl_password_get_prompt_strings (ESource *source,
* if the result is ambiguous, just refer to the data source as
* an "account". */
- display_name = e_source_dup_display_name (source);
+ display_name = e_util_get_source_full_name (registry, source);
if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
ESourceAuthentication *extension;
@@ -217,7 +218,9 @@ e_credentials_prompter_impl_password_show_dialog (ECredentialsPrompterImplPasswo
dialog_parent = e_credentials_prompter_get_dialog_parent (prompter);
- credentials_prompter_impl_password_get_prompt_strings (prompter_password->priv->auth_source, &title,
&info_markup);
+ credentials_prompter_impl_password_get_prompt_strings (
+ e_credentials_prompter_get_registry (prompter),
+ prompter_password->priv->auth_source, &title, &info_markup);
if (prompter_password->priv->error_text && *prompter_password->priv->error_text) {
gchar *escaped = g_markup_printf_escaped ("%s", prompter_password->priv->error_text);
diff --git a/libedataserverui/e-credentials-prompter.c b/libedataserverui/e-credentials-prompter.c
index 3c3a072..40c39a7 100644
--- a/libedataserverui/e-credentials-prompter.c
+++ b/libedataserverui/e-credentials-prompter.c
@@ -29,6 +29,7 @@
/* built-in credentials prompter implementations */
#include "e-credentials-prompter-impl-password.h"
+#include "e-credentials-prompter-impl-google.h"
typedef struct _ProcessPromptData {
GWeakRef *prompter;
@@ -1032,6 +1033,7 @@ e_credentials_prompter_class_init (ECredentialsPrompterClass *class)
/* Ensure built-in credential providers implementation types */
g_type_ensure (E_TYPE_CREDENTIALS_PROMPTER_IMPL_PASSWORD);
+ g_type_ensure (E_TYPE_CREDENTIALS_PROMPTER_IMPL_GOOGLE);
}
static void
diff --git a/libedataserverui/libedataserverui.h b/libedataserverui/libedataserverui.h
index 3223492..062eb09 100644
--- a/libedataserverui/libedataserverui.h
+++ b/libedataserverui/libedataserverui.h
@@ -23,6 +23,7 @@
#include <libedataserverui/e-cell-renderer-color.h>
#include <libedataserverui/e-credentials-prompter.h>
#include <libedataserverui/e-credentials-prompter-impl.h>
+#include <libedataserverui/e-credentials-prompter-impl-google.h>
#include <libedataserverui/e-credentials-prompter-impl-password.h>
#include <libedataserverui/e-trust-prompt.h>
#include <libedataserverui/e-webdav-discover-widget.h>
diff --git a/modules/google-backend/module-google-backend.c b/modules/google-backend/module-google-backend.c
index 6492b77..8ab4b2f 100644
--- a/modules/google-backend/module-google-backend.c
+++ b/modules/google-backend/module-google-backend.c
@@ -101,6 +101,90 @@ G_DEFINE_DYNAMIC_TYPE (
e_google_backend_factory,
E_TYPE_COLLECTION_BACKEND_FACTORY)
+static gboolean
+google_backend_can_use_google_auth (ESource *source)
+{
+ ESourceRegistryServer *registry;
+ gboolean res;
+
+ g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
+
+ if (!e_source_credentials_google_is_supported ())
+ return FALSE;
+
+ registry = e_server_side_source_get_server (E_SERVER_SIDE_SOURCE (source));
+ g_object_ref (source);
+
+ while (source && e_source_get_parent (source)) {
+ ESource *adept_source;
+
+ adept_source = e_source_registry_server_ref_source (registry, e_source_get_parent (source));
+ if (adept_source) {
+ g_object_unref (source);
+ source = adept_source;
+ } else {
+ break;
+ }
+ }
+
+ res = !e_source_has_extension (source, E_SOURCE_EXTENSION_GOA) &&
+ !e_source_has_extension (source, E_SOURCE_EXTENSION_UOA);
+
+ g_object_unref (source);
+
+ return res;
+}
+
+static gboolean
+host_ends_with (const gchar *host,
+ const gchar *ends_with)
+{
+ gint host_len, ends_with_len;
+
+ if (!host || !ends_with)
+ return FALSE;
+
+ host_len = strlen (host);
+ ends_with_len = strlen (ends_with);
+
+ if (host_len <= ends_with_len)
+ return FALSE;
+
+ return g_ascii_strcasecmp (host + host_len - ends_with_len, ends_with) == 0;
+}
+
+static void
+google_backend_mail_update_auth_method (ESource *source)
+{
+ EOAuth2Support *oauth2_support;
+ const gchar *method;
+
+ oauth2_support = e_server_side_source_ref_oauth2_support (E_SERVER_SIDE_SOURCE (source));
+
+ if (oauth2_support != NULL) {
+ method = "XOAUTH2";
+ } else if (google_backend_can_use_google_auth (source)) {
+ method = "Google";
+ } else {
+ method = NULL;
+ }
+
+ if (method) {
+ ESourceAuthentication *auth_extension;
+ gchar *host;
+
+ auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+ host = e_source_authentication_dup_host (auth_extension);
+
+ if (host && (host_ends_with (host, ".gmail.com") || host_ends_with (host, ".googlemail.com")))
+ e_source_authentication_set_method (auth_extension, method);
+
+ g_free (host);
+ }
+
+ g_clear_object (&oauth2_support);
+}
+
static void
google_backend_calendar_update_auth_method (ESource *source)
{
@@ -110,11 +194,10 @@ google_backend_calendar_update_auth_method (ESource *source)
oauth2_support = e_server_side_source_ref_oauth2_support (E_SERVER_SIDE_SOURCE (source));
- /* The host name and WebDAV resource path depend on the
- * authentication method used, so update those here too. */
-
if (oauth2_support != NULL) {
method = "OAuth2";
+ } else if (google_backend_can_use_google_auth (source)) {
+ method = "Google";
} else {
method = "plain/password";
}
@@ -138,7 +221,15 @@ google_backend_contacts_update_auth_method (ESource *source)
extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
extension = e_source_get_extension (source, extension_name);
- method = (oauth2_support != NULL) ? "OAuth2" : "ClientLogin";
+
+ if (oauth2_support != NULL)
+ method = "OAuth2";
+ else /* if (google_backend_can_use_google_auth (source)) */
+ method = "Google";
+ /* "ClientLogin" for Contacts is not supported anymore, thus
+ force "Google" method regardless the evolution-data-server
+ was compiled with it or not. */
+
e_source_authentication_set_method (extension, method);
g_clear_object (&oauth2_support);
@@ -325,6 +416,7 @@ google_backend_authenticate_sync (EBackend *backend,
GError **error)
{
ECollectionBackend *collection = E_COLLECTION_BACKEND (backend);
+ ESourceAuthentication *auth_extension = NULL;
ESourceCollection *collection_extension;
ESourceGoa *goa_extension = NULL;
ESource *source;
@@ -343,6 +435,8 @@ google_backend_authenticate_sync (EBackend *backend,
collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
if (e_source_has_extension (source, E_SOURCE_EXTENSION_GOA))
goa_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_GOA);
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION))
+ auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
g_return_val_if_fail (e_source_collection_get_calendar_enabled (collection_extension) ||
e_source_collection_get_contacts_enabled (collection_extension),
E_SOURCE_AUTHENTICATION_ERROR);
@@ -362,11 +456,20 @@ google_backend_authenticate_sync (EBackend *backend,
google_backend_calendar_update_auth_method (source);
- if (goa_extension)
+ if (goa_extension) {
calendar_url = e_source_goa_get_calendar_url (goa_extension);
- else
+ } else {
calendar_url = "https://www.google.com/calendar/dav/";
+ if (auth_extension) {
+ gchar *method;
+
+ method = e_source_authentication_dup_method (auth_extension);
+ if (g_strcmp0 (method, "Google") == 0)
+ calendar_url = "https://apidata.googleusercontent.com/caldav/v2/";
+ }
+ }
+
if (e_source_collection_get_calendar_enabled (collection_extension) && calendar_url &&
e_webdav_discover_sources_sync (source, calendar_url, E_WEBDAV_DISCOVER_SUPPORTS_NONE,
credentials, out_certificate_pem, out_certificate_errors,
@@ -452,8 +555,9 @@ google_backend_add_tasks (ECollectionBackend *backend)
collection_source = e_backend_get_source (E_BACKEND (backend));
- /* Tasks require OAuth2, which is supported only through GOA */
- if (!e_source_has_extension (collection_source, E_SOURCE_EXTENSION_GOA))
+ /* Tasks require OAuth2 */
+ if (!e_source_has_extension (collection_source, E_SOURCE_EXTENSION_GOA) &&
+ !e_source_credentials_google_is_supported ())
return;
resource_id = GOOGLE_TASKS_RESOURCE_ID;
@@ -477,7 +581,10 @@ google_backend_add_tasks (ECollectionBackend *backend)
extension = e_source_get_extension (source, extension_name);
e_source_authentication_set_host (E_SOURCE_AUTHENTICATION (extension), "www.google.com");
- e_source_authentication_set_method (E_SOURCE_AUTHENTICATION (extension), "OAuth2");
+ if (google_backend_can_use_google_auth (collection_source))
+ e_source_authentication_set_method (E_SOURCE_AUTHENTICATION (extension), "Google");
+ else
+ e_source_authentication_set_method (E_SOURCE_AUTHENTICATION (extension), "OAuth2");
e_binding_bind_property (
collection_extension, "identity",
@@ -589,15 +696,6 @@ google_backend_populate (ECollectionBackend *backend)
ESource *source = link->data;
have_tasks = have_tasks || e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
- if (have_tasks) {
- source = e_backend_get_source (E_BACKEND (backend));
-
- /* Tasks require OAuth2, which is supported only through GOA */
- if (!e_source_has_extension (source, E_SOURCE_EXTENSION_GOA)) {
- e_source_remove_sync (source, NULL, NULL);
- have_tasks = FALSE;
- }
- }
}
g_list_free_full (list, (GDestroyNotify) g_object_unref);
@@ -699,6 +797,15 @@ google_backend_child_added (ECollectionBackend *backend,
e_source_authentication_set_user (
auth_child_extension,
collection_identity);
+
+ if (e_source_has_extension (child_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT) ||
+ e_source_has_extension (child_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT)) {
+ google_backend_mail_update_auth_method (child_source);
+ g_signal_connect (
+ child_source, "notify::oauth2-support",
+ G_CALLBACK (google_backend_mail_update_auth_method),
+ NULL);
+ }
}
/* Keep the calendar authentication method up-to-date.
diff --git a/modules/secret-monitor/module-secret-monitor.c b/modules/secret-monitor/module-secret-monitor.c
index de94992..07d12b0 100644
--- a/modules/secret-monitor/module-secret-monitor.c
+++ b/modules/secret-monitor/module-secret-monitor.c
@@ -131,6 +131,10 @@ secret_monitor_scan_secrets_thread (gpointer user_data)
if (uid == NULL)
continue;
+ /* These are special keys, not referencing any real ESource */
+ if (g_str_has_prefix (uid, "OAuth2::"))
+ continue;
+
source = e_source_registry_server_ref_source (server, uid);
/* If we find a matching ESource, update the SecretItem's
diff --git a/po/POTFILES.in b/po/POTFILES.in
index ea550a5..a01d86c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -186,6 +186,7 @@ libedataserver/e-categories.c
libedataserver/e-client.c
libedataserver/e-source.c
libedataserver/e-source-credentials-provider-impl.c
+libedataserver/e-source-credentials-provider-impl-google.c
libedataserver/e-source-credentials-provider-impl-password.c
libedataserver/e-source-mail-signature.c
libedataserver/e-source-proxy.c
@@ -194,6 +195,7 @@ libedataserver/e-source-webdav.c
libedataserver/e-time-utils.c
libedataserver/e-webdav-discover.c
libedataserverui/e-credentials-prompter.c
+libedataserverui/e-credentials-prompter-impl-google.c
libedataserverui/e-credentials-prompter-impl-password.c
libedataserverui/e-trust-prompt.c
libedataserverui/e-webdav-discover-widget.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]