diff -up evolution-data-server-3.8.5/calendar/backends/caldav/e-cal-backend-caldav.c.google-caldav-v2 evolution-data-server-3.8.5/calendar/backends/caldav/e-cal-backend-caldav.c --- evolution-data-server-3.8.5/calendar/backends/caldav/e-cal-backend-caldav.c.google-caldav-v2 2013-07-23 07:58:13.000000000 -0400 +++ evolution-data-server-3.8.5/calendar/backends/caldav/e-cal-backend-caldav.c 2013-08-16 06:46:04.137343301 -0400 @@ -958,6 +958,32 @@ parse_propfind_response (SoupMessage *me /* Authentication helpers for libsoup */ static void +soup_authenticate_bearer (SoupSession *session, + SoupMessage *message, + SoupAuth *auth, + ESource *source) +{ + gchar *access_token = NULL; + gint expires_in_seconds = -1; + GError *local_error = NULL; + + 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); + + if (local_error != NULL) { + g_warning ("%s: %s", G_STRFUNC, local_error->message); + g_error_free (local_error); + } + + g_free (access_token); +} + +static void soup_authenticate (SoupSession *session, SoupMessage *msg, SoupAuth *auth, @@ -975,8 +1001,14 @@ soup_authenticate (SoupSession *session, extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; auth_extension = e_source_get_extension (source, extension_name); + if (retrying) + return; + + if (E_IS_SOUP_AUTH_BEARER (auth)) { + soup_authenticate_bearer (session, msg, auth, source); + /* do not send same password twice, but keep it for later use */ - if (!retrying && cbdav->priv->password != NULL) { + } else if (cbdav->priv->password != NULL) { gchar *user; user = e_source_authentication_dup_user (auth_extension); @@ -5210,6 +5242,8 @@ cal_backend_caldav_constructed (GObject static void e_cal_backend_caldav_init (ECalBackendCalDAV *cbdav) { + SoupSessionFeature *feature; + cbdav->priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav); cbdav->priv->session = soup_session_sync_new (); g_object_set ( @@ -5219,6 +5253,16 @@ e_cal_backend_caldav_init (ECalBackendCa SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE, NULL); + /* XXX SoupAuthManager is public API as of libsoup 2.42, but + * this isn't worth bumping our libsoup requirement over. + * So get the SoupAuthManager GType by its type name. */ + feature = soup_session_get_feature ( + cbdav->priv->session, + g_type_from_name ("SoupAuthManager")); + + /* Add the "Bearer" auth type to support OAuth 2.0. */ + soup_session_feature_add_feature (feature, E_TYPE_SOUP_AUTH_BEARER); + cbdav->priv->proxy = e_proxy_new (); e_proxy_setup_proxy (cbdav->priv->proxy); g_signal_connect (cbdav->priv->proxy, "changed", G_CALLBACK (proxy_settings_changed), cbdav->priv); diff -up evolution-data-server-3.8.5/docs/reference/libebackend/libebackend-docs.xml.google-caldav-v2 evolution-data-server-3.8.5/docs/reference/libebackend/libebackend-docs.xml --- evolution-data-server-3.8.5/docs/reference/libebackend/libebackend-docs.xml.google-caldav-v2 2013-03-17 08:46:02.000000000 -0400 +++ evolution-data-server-3.8.5/docs/reference/libebackend/libebackend-docs.xml 2013-08-16 06:46:04.131343499 -0400 @@ -34,6 +34,7 @@ Miscellaneous Utilities + diff -up evolution-data-server-3.8.5/docs/reference/libebackend/libebackend-sections.txt.google-caldav-v2 evolution-data-server-3.8.5/docs/reference/libebackend/libebackend-sections.txt --- evolution-data-server-3.8.5/docs/reference/libebackend/libebackend-sections.txt.google-caldav-v2 2013-08-11 07:26:26.000000000 -0400 +++ evolution-data-server-3.8.5/docs/reference/libebackend/libebackend-sections.txt 2013-08-16 06:46:04.132343461 -0400 @@ -397,6 +397,24 @@ EServerSideSourcePrivate
+e-soup-auth-bearer +ESoupAuthBearer +ESoupAuthBearer +e_soup_auth_bearer_set_access_token + +E_SOUP_AUTH_BEARER +E_IS_SOUP_AUTH_BEARER +E_TYPE_SOUP_AUTH_BEARER +E_SOUP_AUTH_BEARER_CLASS +E_IS_SOUP_AUTH_BEARER_CLASS +E_SOUP_AUTH_BEARER_GET_CLASS +ESoupAuthBearerClass +e_soup_auth_bearer_get_type + +ESoupAuthBearerPrivate +
+ +
e-source-registry-server ESourceRegistryServer E_SOURCE_REGISTRY_SERVER_OBJECT_PATH diff -up evolution-data-server-3.8.5/docs/reference/libebackend/libebackend.types.google-caldav-v2 evolution-data-server-3.8.5/docs/reference/libebackend/libebackend.types --- evolution-data-server-3.8.5/docs/reference/libebackend/libebackend.types.google-caldav-v2 2013-08-11 07:26:26.000000000 -0400 +++ evolution-data-server-3.8.5/docs/reference/libebackend/libebackend.types 2013-08-16 06:46:04.134343391 -0400 @@ -15,6 +15,7 @@ e_module_get_type e_oauth2_support_get_type e_offline_listener_get_type e_server_side_source_get_type +e_soup_auth_bearer_get_type e_source_registry_server_get_type e_user_prompter_get_type e_user_prompter_server_get_type diff -up evolution-data-server-3.8.5/libebackend/e-soup-auth-bearer.c.google-caldav-v2 evolution-data-server-3.8.5/libebackend/e-soup-auth-bearer.c --- evolution-data-server-3.8.5/libebackend/e-soup-auth-bearer.c.google-caldav-v2 2013-08-16 06:46:04.135343359 -0400 +++ evolution-data-server-3.8.5/libebackend/e-soup-auth-bearer.c 2013-08-16 06:46:04.135343359 -0400 @@ -0,0 +1,196 @@ +/* + * e-soup-auth-bearer.c + * + * This program 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; either + * version 2 of the License, or (at your option) version 3. + * + * This program 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 the program; if not, see + * + */ + +/** + * SECTION: e-soup-auth-bearer + * @include: libebackend/libebackend.h + * @short_description: OAuth 2.0 support for libsoup + * + * #ESoupAuthBearer adds libsoup support for the use of bearer tokens in + * HTTP requests to access OAuth 2.0 protected resources, as defined in + * RFC 6750. + * + * An #EBackend should integrate #ESoupAuthBearer first by adding it as a + * feature to a #SoupSession's #SoupAuthManager, then from a #SoupSession + * #SoupSession::authenticate handler call e_source_get_oauth2_access_token() + * and pass the results to e_soup_auth_bearer_set_access_token(). + **/ + +#include "e-soup-auth-bearer.h" + +#include + +#define E_SOUP_AUTH_BEARER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOUP_AUTH_BEARER, ESoupAuthBearerPrivate)) + +#define AUTH_STRENGTH 1 + +#define EXPIRY_INVALID ((time_t) -1) + +struct _ESoupAuthBearerPrivate { + gchar *access_token; + time_t expiry; +}; + +G_DEFINE_TYPE ( + ESoupAuthBearer, + e_soup_auth_bearer, + SOUP_TYPE_AUTH) + +static gboolean +e_soup_auth_bearer_is_expired (ESoupAuthBearer *bearer) +{ + gboolean expired = FALSE; + + if (bearer->priv->expiry != EXPIRY_INVALID) + expired = (bearer->priv->expiry < time (NULL)); + + return expired; +} + +static void +e_soup_auth_bearer_finalize (GObject *object) +{ + ESoupAuthBearerPrivate *priv; + + priv = E_SOUP_AUTH_BEARER_GET_PRIVATE (object); + + g_free (priv->access_token); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_soup_auth_bearer_parent_class)->finalize (object); +} + +static gboolean +e_soup_auth_bearer_update (SoupAuth *auth, + SoupMessage *message, + GHashTable *auth_header) +{ + /* XXX Not sure what to do here. Discard the access token? */ + + return TRUE; +} + +static GSList * +e_soup_auth_bearer_get_protection_space (SoupAuth *auth, + SoupURI *source_uri) +{ + /* XXX Not sure what to do here. Need to return something. */ + + return g_slist_prepend (NULL, g_strdup ("")); +} + +static gboolean +e_soup_auth_bearer_is_authenticated (SoupAuth *auth) +{ + ESoupAuthBearer *bearer; + gboolean authenticated = FALSE; + + bearer = E_SOUP_AUTH_BEARER (auth); + + if (!e_soup_auth_bearer_is_expired (bearer)) + authenticated = (bearer->priv->access_token != NULL); + + return authenticated; +} + +static gchar * +e_soup_auth_bearer_get_authorization (SoupAuth *auth, + SoupMessage *message) +{ + ESoupAuthBearer *bearer; + + bearer = E_SOUP_AUTH_BEARER (auth); + + return g_strdup_printf ("Bearer %s", bearer->priv->access_token); +} + +static void +e_soup_auth_bearer_class_init (ESoupAuthBearerClass *class) +{ + GObjectClass *object_class; + SoupAuthClass *auth_class; + + g_type_class_add_private (class, sizeof (ESoupAuthBearerPrivate)); + + /* Keep the "e" prefix on private methods + * so we don't step on libsoup's namespace. */ + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = e_soup_auth_bearer_finalize; + + auth_class = SOUP_AUTH_CLASS (class); + auth_class->scheme_name = "Bearer"; + auth_class->strength = AUTH_STRENGTH; + auth_class->update = e_soup_auth_bearer_update; + auth_class->get_protection_space = e_soup_auth_bearer_get_protection_space; + auth_class->is_authenticated = e_soup_auth_bearer_is_authenticated; + auth_class->get_authorization = e_soup_auth_bearer_get_authorization; +} + +static void +e_soup_auth_bearer_init (ESoupAuthBearer *bearer) +{ + bearer->priv = E_SOUP_AUTH_BEARER_GET_PRIVATE (bearer); + bearer->priv->expiry = EXPIRY_INVALID; +} + +/** + * e_soup_auth_bearer_set_access_token: + * @bearer: an #ESoupAuthBearer + * @access_token: an OAuth 2.0 access token + * @expires_in_seconds: expiry for @access_token, or 0 if unknown + * + * This function is analogous to soup_auth_authenticate() for "Basic" HTTP + * authentication, except it takes an OAuth 2.0 access token instead of a + * username and password. + * + * If @expires_in_seconds is greater than zero, soup_auth_is_authenticated() + * will return %FALSE after the given number of seconds have elapsed. + * + * Since: 3.10 + **/ +void +e_soup_auth_bearer_set_access_token (ESoupAuthBearer *bearer, + const gchar *access_token, + gint expires_in_seconds) +{ + gboolean was_authenticated; + gboolean now_authenticated; + + g_return_if_fail (E_IS_SOUP_AUTH_BEARER (bearer)); + + was_authenticated = soup_auth_is_authenticated (SOUP_AUTH (bearer)); + + g_free (bearer->priv->access_token); + bearer->priv->access_token = g_strdup (access_token); + + if (expires_in_seconds > 0) + bearer->priv->expiry = time (NULL) + expires_in_seconds; + else + bearer->priv->expiry = EXPIRY_INVALID; + + now_authenticated = soup_auth_is_authenticated (SOUP_AUTH (bearer)); + + if (was_authenticated != now_authenticated) + g_object_notify ( + G_OBJECT (bearer), + SOUP_AUTH_IS_AUTHENTICATED); +} + diff -up evolution-data-server-3.8.5/libebackend/e-soup-auth-bearer.h.google-caldav-v2 evolution-data-server-3.8.5/libebackend/e-soup-auth-bearer.h --- evolution-data-server-3.8.5/libebackend/e-soup-auth-bearer.h.google-caldav-v2 2013-08-16 06:46:04.135343359 -0400 +++ evolution-data-server-3.8.5/libebackend/e-soup-auth-bearer.h 2013-08-16 06:46:04.135343359 -0400 @@ -0,0 +1,79 @@ +/* + * e-soup-auth-bearer.h + * + * This program 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; either + * version 2 of the License, or (at your option) version 3. + * + * This program 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 the program; if not, see + * + */ + +#if !defined (__LIBEBACKEND_H_INSIDE__) && !defined (LIBEBACKEND_COMPILATION) +#error "Only should be included directly." +#endif + +#ifndef E_SOUP_AUTH_BEARER_H +#define E_SOUP_AUTH_BEARER_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOUP_AUTH_BEARER \ + (e_soup_auth_bearer_get_type ()) +#define E_SOUP_AUTH_BEARER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOUP_AUTH_BEARER, ESoupAuthBearer)) +#define E_SOUP_AUTH_BEARER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOUP_AUTH_BEARER, ESoupAuthBearerClass)) +#define E_IS_SOUP_AUTH_BEARER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOUP_AUTH_BEARER)) +#define E_IS_SOUP_AUTH_BEARER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOUP_AUTH_BEARER)) +#define E_SOUP_AUTH_BEARER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOUP_AUTH_BEARER, ESoupAuthBearerClass)) + +G_BEGIN_DECLS + +typedef struct _ESoupAuthBearer ESoupAuthBearer; +typedef struct _ESoupAuthBearerClass ESoupAuthBearerClass; +typedef struct _ESoupAuthBearerPrivate ESoupAuthBearerPrivate; + +/** + * ESoupAuthBearer: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.10 + **/ +struct _ESoupAuthBearer { + SoupAuth parent; + ESoupAuthBearerPrivate *priv; +}; + +struct _ESoupAuthBearerClass { + SoupAuthClass parent_class; +}; + +GType e_soup_auth_bearer_get_type (void) G_GNUC_CONST; +void e_soup_auth_bearer_set_access_token + (ESoupAuthBearer *bearer, + const gchar *access_token, + gint expires_in_seconds); + +G_END_DECLS + +#endif /* E_SOUP_AUTH_BEARER_H */ + diff -up evolution-data-server-3.8.5/libebackend/libebackend.h.google-caldav-v2 evolution-data-server-3.8.5/libebackend/libebackend.h --- evolution-data-server-3.8.5/libebackend/libebackend.h.google-caldav-v2 2013-07-23 07:57:40.000000000 -0400 +++ evolution-data-server-3.8.5/libebackend/libebackend.h 2013-08-16 06:46:04.135343359 -0400 @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include diff -up evolution-data-server-3.8.5/libebackend/Makefile.am.google-caldav-v2 evolution-data-server-3.8.5/libebackend/Makefile.am --- evolution-data-server-3.8.5/libebackend/Makefile.am.google-caldav-v2 2013-04-18 11:33:59.000000000 -0400 +++ evolution-data-server-3.8.5/libebackend/Makefile.am 2013-08-16 06:46:04.134343391 -0400 @@ -35,6 +35,7 @@ libebackend_1_2_la_CPPFLAGS = \ $(E_BACKEND_CFLAGS) \ $(GCR_BASE_CFLAGS) \ $(GIO_UNIX_CFLAGS) \ + $(SOUP_CFLAGS) \ $(CODE_COVERAGE_CFLAGS) \ $(NULL) @@ -56,6 +57,7 @@ libebackend_1_2_la_SOURCES = \ e-db3-utils.c \ e-module.c \ e-server-side-source.c \ + e-soup-auth-bearer.c \ e-source-registry-server.c \ e-sqlite3-vfs.c \ e-user-prompter.c \ @@ -71,6 +73,7 @@ libebackend_1_2_la_LIBADD = \ $(SQLITE3_LIBS) \ $(GCR_BASE_LIBS) \ $(GIO_UNIX_LIBS) \ + $(SOUP_LIBS) \ $(DB_LIBS) libebackend_1_2_la_LDFLAGS = \ @@ -100,6 +103,7 @@ libebackendinclude_HEADERS = \ e-dbhash.h \ e-module.h \ e-server-side-source.h \ + e-soup-auth-bearer.h \ e-source-registry-server.h \ e-sqlite3-vfs.h \ e-user-prompter.h \ diff -up evolution-data-server-3.8.5/libedataserver/e-source-webdav.c.google-caldav-v2 evolution-data-server-3.8.5/libedataserver/e-source-webdav.c --- evolution-data-server-3.8.5/libedataserver/e-source-webdav.c.google-caldav-v2 2013-07-23 07:57:32.000000000 -0400 +++ evolution-data-server-3.8.5/libedataserver/e-source-webdav.c 2013-08-16 06:46:04.130343539 -0400 @@ -108,15 +108,39 @@ source_webdav_user_to_method (GBinding * GValue *target_value, gpointer user_data) { + GObject *target_object; + ESourceAuthentication *extension; const gchar *user; + gchar *method; + gboolean success = TRUE; + + target_object = g_binding_get_target (binding); + extension = E_SOURCE_AUTHENTICATION (target_object); + method = e_source_authentication_dup_method (extension); + g_return_val_if_fail (method != NULL, FALSE); + + /* Be careful not to stomp on a custom method name. + * Only change it under the following conditions: + * + * 1) If "user" is empty, set "method" to "none". + * 2) If "user" is not empty and "method" is "none", + * set "method" to "plain/password" (corresponds + * to HTTP Basic authentication). + * 3) Otherwise preserve the current "method" value. + */ user = g_value_get_string (source_value); - if (user == NULL || *user == '\0') + if (user == NULL || *user == '\0') { g_value_set_string (target_value, "none"); - else + } else if (g_str_equal (method, "none")) { g_value_set_string (target_value, "plain/password"); + } else { + success = FALSE; + } + + g_free (method); - return TRUE; + return success; } static void diff -up evolution-data-server-3.8.5/modules/google-backend/module-google-backend.c.google-caldav-v2 evolution-data-server-3.8.5/modules/google-backend/module-google-backend.c --- evolution-data-server-3.8.5/modules/google-backend/module-google-backend.c.google-caldav-v2 2013-07-23 07:57:38.000000000 -0400 +++ evolution-data-server-3.8.5/modules/google-backend/module-google-backend.c 2013-08-16 06:46:04.371341485 -0400 @@ -45,10 +45,16 @@ /* Calendar Configuration Details */ #define GOOGLE_CALENDAR_BACKEND_NAME "caldav" -#define GOOGLE_CALENDAR_HOST "www.google.com" -#define GOOGLE_CALENDAR_CALDAV_PATH "/calendar/dav/%s/events" #define GOOGLE_CALENDAR_RESOURCE_ID "Calendar" +/* CalDAV v1 Configuration Details */ +#define GOOGLE_CALDAV_V1_HOST "www.google.com" +#define GOOGLE_CALDAV_V1_PATH "/calendar/dav/%s/events" + +/* CalDAV v2 Configuration Details */ +#define GOOGLE_CALDAV_V2_HOST "apidata.googleusercontent.com" +#define GOOGLE_CALDAV_V2_PATH "/caldav/v2/%s/events" + /* Contacts Configuration Details */ #define GOOGLE_CONTACTS_BACKEND_NAME "google" #define GOOGLE_CONTACTS_HOST "www.google.com" @@ -95,24 +101,68 @@ G_DEFINE_DYNAMIC_TYPE ( E_TYPE_COLLECTION_BACKEND_FACTORY) static void -google_backend_contacts_update_auth_method (ESource *source) +google_backend_calendar_update_auth_method (ESource *source) { EOAuth2Support *oauth2_support; - ESourceAuthentication *extension; + ESourceAuthentication *auth_extension; + ESourceWebdav *webdav_extension; const gchar *extension_name; - - extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; - extension = e_source_get_extension (source, extension_name); + const gchar *host; + const gchar *method; + const gchar *path_format; + gchar *path; + gchar *user; 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) { - e_source_authentication_set_method (extension, "OAuth2"); - g_object_unref (oauth2_support); - return; + method = "OAuth2"; + host = GOOGLE_CALDAV_V2_HOST; + path_format = GOOGLE_CALDAV_V2_PATH; + } else { + method = "plain/password"; + host = GOOGLE_CALDAV_V1_HOST; + path_format = GOOGLE_CALDAV_V1_PATH; } - e_source_authentication_set_method (extension, "ClientLogin"); + extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; + auth_extension = e_source_get_extension (source, extension_name); + e_source_authentication_set_host (auth_extension, host); + e_source_authentication_set_method (auth_extension, method); + + extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND; + webdav_extension = e_source_get_extension (source, extension_name); + + user = e_source_authentication_dup_user (auth_extension); + path = g_strdup_printf (path_format, (user != NULL) ? user : ""); + e_source_webdav_set_resource_path (webdav_extension, path); + g_free (path); + g_free (user); + + g_clear_object (&oauth2_support); +} + +static void +google_backend_contacts_update_auth_method (ESource *source) +{ + EOAuth2Support *oauth2_support; + ESourceAuthentication *extension; + const gchar *extension_name; + const gchar *method; + + oauth2_support = e_server_side_source_ref_oauth2_support ( + E_SERVER_SIDE_SOURCE (source)); + + extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; + extension = e_source_get_extension (source, extension_name); + method = (oauth2_support != NULL) ? "OAuth2" : "ClientLogin"; + e_source_authentication_set_method (extension, method); + + g_clear_object (&oauth2_support); } static void @@ -125,14 +175,16 @@ google_backend_add_calendar (ECollection ESourceCollection *collection_extension; const gchar *backend_name; const gchar *extension_name; - const gchar *identity; const gchar *resource_id; - gchar *path; /* FIXME As a future enhancement, we should query Google * for a list of user calendars and add them to the * collection with matching display names and colors. */ + /* NOTE: Host name and WebDAV resource path are set in + * google_backend_calendar_update_auth_method(), + * since they depend on the auth method used. */ + collection_source = e_backend_get_source (E_BACKEND (backend)); resource_id = GOOGLE_CALENDAR_RESOURCE_ID; @@ -155,15 +207,15 @@ google_backend_add_calendar (ECollection extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; extension = e_source_get_extension (source, extension_name); - e_source_authentication_set_host ( - E_SOURCE_AUTHENTICATION (extension), - GOOGLE_CALENDAR_HOST); - g_object_bind_property ( collection_extension, "identity", extension, "user", G_BINDING_SYNC_CREATE); + /* Make sure the WebDAV resource path is up-to-date, since + * it's built from the "user" property that we just set. */ + google_backend_calendar_update_auth_method (source); + extension_name = E_SOURCE_EXTENSION_SECURITY; extension = e_source_get_extension (source, extension_name); @@ -178,19 +230,11 @@ google_backend_add_calendar (ECollection g_get_current_time (&today_tv); today = g_time_val_to_iso8601 (&today_tv); - e_source_alarms_set_last_notified (E_SOURCE_ALARMS (extension), today); + e_source_alarms_set_last_notified ( + E_SOURCE_ALARMS (extension), today); g_free (today); } - extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND; - extension = e_source_get_extension (source, extension_name); - - identity = e_source_collection_get_identity (collection_extension); - path = g_strdup_printf (GOOGLE_CALENDAR_CALDAV_PATH, identity); - e_source_webdav_set_resource_path ( - E_SOURCE_WEBDAV (extension), path); - g_free (path); - server = e_collection_backend_ref_server (backend); e_source_registry_server_add_source (server, source); g_object_unref (server); @@ -337,6 +381,20 @@ google_backend_child_added (ECollectionB collection_identity); } + /* Keep the calendar authentication method up-to-date. + * + * XXX Not using a property binding here in case I end up adding + * other "support" interfaces which influence authentication. + * Many-to-one property bindinds tend not to work so well. */ + extension_name = E_SOURCE_EXTENSION_CALENDAR; + if (e_source_has_extension (child_source, extension_name)) { + google_backend_calendar_update_auth_method (child_source); + g_signal_connect ( + child_source, "notify::oauth2-support", + G_CALLBACK (google_backend_calendar_update_auth_method), + NULL); + } + /* Keep the contacts authentication method up-to-date. * * XXX Not using a property binding here in case I end up adding