[evolution-data-server] Introduce EWebDAVCollectionBackend to be used for WebDAV-discovered collections



commit 884adf3eca8a94dbcf3101b45c81b8218274e0f0
Author: Milan Crha <mcrha redhat com>
Date:   Fri Jul 21 12:56:24 2017 +0200

    Introduce EWebDAVCollectionBackend to be used for WebDAV-discovered collections
    
    Any server providing CalDAV/CardDAV interfaces to access the calendars,
    memo lists, task lists and address books may derive from this new object
    and avoid code duplication.

 src/libebackend/CMakeLists.txt                     |    2 +
 src/libebackend/e-webdav-collection-backend.c      |  544 ++++++++++++++++++++
 src/libebackend/e-webdav-collection-backend.h      |   98 ++++
 src/libebackend/libebackend.h                      |    1 +
 src/modules/google-backend/module-google-backend.c |  292 ++---------
 .../owncloud-backend/module-owncloud-backend.c     |  372 +-------------
 src/modules/yahoo-backend/module-yahoo-backend.c   |  215 +-------
 7 files changed, 729 insertions(+), 795 deletions(-)
---
diff --git a/src/libebackend/CMakeLists.txt b/src/libebackend/CMakeLists.txt
index 844bcbc..fbf2023 100644
--- a/src/libebackend/CMakeLists.txt
+++ b/src/libebackend/CMakeLists.txt
@@ -29,6 +29,7 @@ set(SOURCES
        e-user-prompter.c
        e-user-prompter-server.c
        e-user-prompter-server-extension.c
+       e-webdav-collection-backend.c
        ${CMAKE_CURRENT_BINARY_DIR}/e-backend-enumtypes.c
 )
 
@@ -54,6 +55,7 @@ set(HEADERS
        e-user-prompter.h
        e-user-prompter-server.h
        e-user-prompter-server-extension.h
+       e-webdav-collection-backend.h
        ${CMAKE_CURRENT_BINARY_DIR}/e-backend-enumtypes.h
 )
 
diff --git a/src/libebackend/e-webdav-collection-backend.c b/src/libebackend/e-webdav-collection-backend.c
new file mode 100644
index 0000000..0073bdd
--- /dev/null
+++ b/src/libebackend/e-webdav-collection-backend.c
@@ -0,0 +1,544 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 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/>.
+ *
+ */
+
+#include "evolution-data-server-config.h"
+
+#include <libebackend/libebackend.h>
+#include <libedataserver/libedataserver.h>
+
+#include "e-webdav-collection-backend.h"
+
+struct _EWebDAVCollectionBackendPrivate {
+       gboolean dummy;
+};
+
+G_DEFINE_TYPE (EWebDAVCollectionBackend, e_webdav_collection_backend, E_TYPE_COLLECTION_BACKEND)
+
+static void
+webdav_collection_add_uid_to_hashtable (gpointer source,
+                                       gpointer known_sources)
+{
+       ESourceResource *resource;
+       gchar *uid, *rid;
+
+       if (!e_source_has_extension (source, E_SOURCE_EXTENSION_RESOURCE))
+               return;
+
+       resource = e_source_get_extension (source, E_SOURCE_EXTENSION_RESOURCE);
+
+       uid = e_source_dup_uid (source);
+       if (!uid || !*uid) {
+               g_free (uid);
+               return;
+       }
+
+       rid = e_source_resource_dup_identity (resource);
+       if (!rid || !*rid) {
+               g_free (rid);
+               g_free (uid);
+               return;
+       }
+
+       g_hash_table_insert (known_sources, rid, uid);
+}
+
+typedef struct _RemoveSourcesData {
+       ESourceRegistryServer *server;
+       EWebDAVCollectionBackend *webdav_backend;
+} RemoveSourcesData;
+
+static void
+webdav_collection_remove_unknown_sources_cb (gpointer resource_id,
+                                            gpointer uid,
+                                            gpointer user_data)
+{
+       RemoveSourcesData *rsd = user_data;
+       ESource *source;
+
+       g_return_if_fail (rsd != NULL);
+
+       source = e_source_registry_server_ref_source (rsd->server, uid);
+
+       if (source) {
+               if (!e_webdav_collection_backend_is_custom_source (rsd->webdav_backend, source))
+                       e_source_remove_sync (source, NULL, NULL);
+
+               g_object_unref (source);
+       }
+}
+
+static void
+webdav_collection_add_found_source (ECollectionBackend *collection,
+                                   EWebDAVDiscoverSupports source_type,
+                                   SoupURI *uri,
+                                   const gchar *display_name,
+                                   const gchar *color,
+                                   GHashTable *known_sources)
+{
+       ESourceRegistryServer *server;
+       ESourceBackend *backend;
+       ESource *source = NULL;
+       const gchar *backend_name = NULL;
+       const gchar *provider = NULL;
+       const gchar *identity_prefix = NULL;
+       const gchar *source_uid;
+       gboolean is_new;
+       gchar *url;
+       gchar *identity;
+
+       g_return_if_fail (collection != NULL);
+       g_return_if_fail (uri != NULL);
+       g_return_if_fail (display_name != NULL);
+       g_return_if_fail (known_sources != NULL);
+
+       switch (source_type) {
+       case E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS:
+               backend_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
+               provider = "webdav";
+               identity_prefix = "contacts";
+               break;
+       case E_WEBDAV_DISCOVER_SUPPORTS_EVENTS:
+               backend_name = E_SOURCE_EXTENSION_CALENDAR;
+               provider = "caldav";
+               identity_prefix = "events";
+               break;
+       case E_WEBDAV_DISCOVER_SUPPORTS_MEMOS:
+               backend_name = E_SOURCE_EXTENSION_MEMO_LIST;
+               provider = "caldav";
+               identity_prefix = "memos";
+               break;
+       case E_WEBDAV_DISCOVER_SUPPORTS_TASKS:
+               backend_name = E_SOURCE_EXTENSION_TASK_LIST;
+               provider = "caldav";
+               identity_prefix = "tasks";
+               break;
+       default:
+               g_warn_if_reached ();
+               return;
+       }
+
+       g_return_if_fail (backend_name != NULL);
+
+       server = e_collection_backend_ref_server (collection);
+       if (!server)
+               return;
+
+       url = soup_uri_to_string (uri, FALSE);
+       identity = g_strconcat (identity_prefix, "::", url, NULL);
+       source_uid = g_hash_table_lookup (known_sources, identity);
+       is_new = !source_uid;
+       if (is_new) {
+               source = e_collection_backend_new_child (collection, identity);
+               g_warn_if_fail (source != NULL);
+       } else {
+               source = e_source_registry_server_ref_source (server, source_uid);
+               g_warn_if_fail (source != NULL);
+
+               g_hash_table_remove (known_sources, identity);
+       }
+
+       if (source) {
+               ESource *master_source;
+               ESourceCollection *collection_extension;
+               ESourceAuthentication *child_auth;
+               ESourceResource *resource;
+               ESourceWebdav *master_webdav, *child_webdav;
+
+               master_source = e_backend_get_source (E_BACKEND (collection));
+               master_webdav = e_source_get_extension (master_source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+               collection_extension = e_source_get_extension (master_source, E_SOURCE_EXTENSION_COLLECTION);
+               child_auth = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+               child_webdav = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+               resource = e_source_get_extension (source, E_SOURCE_EXTENSION_RESOURCE);
+
+               e_source_authentication_set_user (child_auth, e_source_collection_get_identity 
(collection_extension));
+               e_source_webdav_set_soup_uri (child_webdav, uri);
+               e_source_resource_set_identity (resource, identity);
+
+               if (is_new) {
+                       /* inherit ssl trust options */
+                       e_source_webdav_set_ssl_trust (child_webdav, e_source_webdav_get_ssl_trust 
(master_webdav));
+               }
+       }
+
+       g_free (identity);
+       g_free (url);
+
+       /* these properties are synchronized always */
+       if (source) {
+               gint rr, gg, bb;
+
+               backend = e_source_get_extension (source, backend_name);
+               e_source_backend_set_backend_name (backend, provider);
+
+               e_source_set_display_name (source, display_name);
+               e_source_set_enabled (source, TRUE);
+
+               /* Also check whether the color format is as expected; it cannot
+                  be used gdk_rgba_parse here, because it required gdk/gtk. */
+               if (is_new && source_type != E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS && color &&
+                   sscanf (color, "#%02x%02x%02x", &rr, &gg, &bb) == 3) {
+                       gchar *safe_color;
+
+                       /* In case an #RRGGBBAA is returned */
+                       safe_color = g_strdup_printf ("#%02x%02x%02x", rr, gg, bb);
+
+                       e_source_selectable_set_color (E_SOURCE_SELECTABLE (backend), safe_color);
+
+                       g_free (safe_color);
+               }
+
+               if (is_new)
+                       e_source_registry_server_add_source (server, source);
+
+               g_object_unref (source);
+       }
+
+       g_object_unref (server);
+}
+
+static void
+webdav_collection_process_discovered_sources (ECollectionBackend *collection,
+                                             GSList *discovered_sources,
+                                             GHashTable *known_sources,
+                                             const EWebDAVDiscoverSupports *source_types,
+                                             gint n_source_types)
+{
+       GSList *link;
+       gint ii;
+
+       for (link = discovered_sources; link; link = g_slist_next (link)) {
+               EWebDAVDiscoveredSource *discovered_source = link->data;
+               SoupURI *soup_uri;
+
+               if (!discovered_source || !discovered_source->href || !discovered_source->display_name)
+                       continue;
+
+               soup_uri = soup_uri_new (discovered_source->href);
+               if (!soup_uri)
+                       continue;
+
+               for (ii = 0; ii < n_source_types; ii++) {
+                       if ((discovered_source->supports & source_types[ii]) == source_types[ii])
+                               webdav_collection_add_found_source (collection, source_types[ii], soup_uri,
+                                       discovered_source->display_name, discovered_source->color, 
known_sources);
+               }
+
+               soup_uri_free (soup_uri);
+       }
+}
+
+static gchar *
+webdav_collection_backend_get_resource_id (EWebDAVCollectionBackend *webdav_backend,
+                                          ESource *source)
+{
+       g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_RESOURCE)) {
+               ESourceResource *resource;
+
+               resource = e_source_get_extension (source, E_SOURCE_EXTENSION_RESOURCE);
+               return e_source_resource_dup_identity (resource);
+       }
+
+       return NULL;
+}
+
+static gboolean
+webdav_collection_backend_is_custom_source (EWebDAVCollectionBackend *webdav_backend,
+                                           ESource *source)
+{
+       g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+       return FALSE;
+}
+
+static void
+webdav_collection_backend_populate (ECollectionBackend *collection)
+{
+       EWebDAVCollectionBackend *webdav_backend = E_WEBDAV_COLLECTION_BACKEND (collection);
+       ESourceRegistryServer *server;
+       ESourceCollection *collection_extension;
+       ESource *source;
+       GList *list, *liter;
+
+       /* Chain up to parent's method. */
+       E_COLLECTION_BACKEND_CLASS (e_webdav_collection_backend_parent_class)->populate (collection);
+
+       server = e_collection_backend_ref_server (collection);
+       list = e_collection_backend_claim_all_resources (collection);
+
+       for (liter = list; liter; liter = g_list_next (liter)) {
+               ESource *source = liter->data;
+               gchar *resource_id;
+
+               resource_id = webdav_collection_backend_get_resource_id (webdav_backend, source);
+               if (resource_id) {
+                       ESource *child;
+
+                       child = e_collection_backend_new_child (collection, resource_id);
+                       if (child) {
+                               e_source_registry_server_add_source (server, source);
+                               g_object_unref (child);
+                       }
+
+                       g_free (resource_id);
+               }
+
+       }
+
+       g_list_free_full (list, g_object_unref);
+       g_object_unref (server);
+
+       source = e_backend_get_source (E_BACKEND (collection));
+       collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
+
+       if (e_source_get_enabled (source) && (
+           e_source_collection_get_calendar_enabled (collection_extension) ||
+           e_source_collection_get_contacts_enabled (collection_extension))) {
+               e_backend_schedule_credentials_required (E_BACKEND (collection),
+                       E_SOURCE_CREDENTIALS_REASON_REQUIRED, NULL, 0, NULL, NULL, G_STRFUNC);
+       }
+}
+
+static void
+e_webdav_collection_backend_class_init (EWebDAVCollectionBackendClass *klass)
+{
+       ECollectionBackendClass *collection_backend_class;
+
+       g_type_class_add_private (klass, sizeof (EWebDAVCollectionBackendPrivate));
+
+       klass->get_resource_id = webdav_collection_backend_get_resource_id;
+       klass->is_custom_source = webdav_collection_backend_is_custom_source;
+
+       collection_backend_class = E_COLLECTION_BACKEND_CLASS (klass);
+       collection_backend_class->populate = webdav_collection_backend_populate;
+}
+
+static void
+e_webdav_collection_backend_init (EWebDAVCollectionBackend *webdav_backend)
+{
+       webdav_backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (webdav_backend, E_TYPE_WEBDAV_COLLECTION_BACKEND, 
EWebDAVCollectionBackendPrivate);
+}
+
+/**
+ * e_webdav_collection_backend_get_resource_id:
+ * @webdav_backend: an #EWebDAVCollectionBackend
+ * @source: an #ESource
+ *
+ * Verifies that the @source is expected here and returns its resource ID,
+ * which is used in call to e_collection_backend_new_child(). It returns %NULL,
+ * when the @source is not part of the backend and should be removed instead.
+ * The default implementation allows all sources, which has %ESourceResource
+ * extension defined.
+ *
+ * Returns: (transfer full) (nullable): a resource ID corresponding to @source,
+ *    or %NULL, when the @source should be removed.
+ *
+ * Since: 3.26
+ **/
+gchar *
+e_webdav_collection_backend_get_resource_id (EWebDAVCollectionBackend *webdav_backend,
+                                            ESource *source)
+{
+       EWebDAVCollectionBackendClass *klass;
+
+       g_return_val_if_fail (E_IS_WEBDAV_COLLECTION_BACKEND (webdav_backend), NULL);
+       g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+       klass = E_WEBDAV_COLLECTION_BACKEND_GET_CLASS (webdav_backend);
+       g_return_val_if_fail (klass != NULL, NULL);
+       g_return_val_if_fail (klass->get_resource_id != NULL, NULL);
+
+       return klass->get_resource_id (webdav_backend, source);
+}
+
+/**
+ * e_webdav_collection_backend_is_custom_source:
+ * @webdav_backend: an #EWebDAVCollectionBackend
+ * @source: an #ESource
+ *
+ * Returns: %TRUE, when the @source is a custom source, thus it
+ *    should not be removed as an obsolete source; %FALSE to not
+ *    force to keep it. It still can be left, when it's one of
+ *    the WebDAV-discovered sources.
+ *
+ * Since: 3.26
+ **/
+gboolean
+e_webdav_collection_backend_is_custom_source (EWebDAVCollectionBackend *webdav_backend,
+                                             ESource *source)
+{
+       EWebDAVCollectionBackendClass *klass;
+
+       g_return_val_if_fail (E_IS_WEBDAV_COLLECTION_BACKEND (webdav_backend), FALSE);
+       g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+       klass = E_WEBDAV_COLLECTION_BACKEND_GET_CLASS (webdav_backend);
+       g_return_val_if_fail (klass != NULL, FALSE);
+
+       if (!klass->is_custom_source)
+               return FALSE;
+
+       return klass->is_custom_source (webdav_backend, source);
+}
+
+/**
+ * e_webdav_collection_backend_discover_sync:
+ * @webdav_backend: an #EWebDAVCollectionBackend
+ * @calendar_url: (nullable): a URL to search calendars at, or %NULL
+ * @contacts_url: (nullable): a URL to search contacts at, or %NULL
+ * @credentials: credentials to use when running the discovery
+ * @out_certificate_pem: (out) (nullable): optional return location
+ *   for a server SSL certificate in PEM format, when the operation failed
+ *   with an SSL error
+ * @out_certificate_errors: (out) (nullable): optional #GTlsCertificateFlags,
+ *   with certificate error flags when the operation failed with SSL error
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * This function is usually called in EBackend::authenticate_sync() implementation
+ * of the descendant, causing discovery of CalDAV and CardDAV sources on given URLs.
+ * If either of @calendar_url and @contacts_url is %NULL, that that part is skipped.
+ * The @calendar_url covers all calendars, memo lists and task lists.
+ *
+ * The function also takes care of e_collection_backend_authenticate_children() on success.
+ *
+ * Returns: an #ESourceAuthenticationResult describing whether discovery on given
+ *    addresses succeeded.
+ *
+ * Since: 3.26
+ **/
+ESourceAuthenticationResult
+e_webdav_collection_backend_discover_sync (EWebDAVCollectionBackend *webdav_backend,
+                                          const gchar *calendar_url,
+                                          const gchar *contacts_url,
+                                          const ENamedParameters *credentials,
+                                          gchar **out_certificate_pem,
+                                          GTlsCertificateFlags *out_certificate_errors,
+                                          GCancellable *cancellable,
+                                          GError **error)
+{
+       ECollectionBackend *collection;
+       ESourceCollection *collection_extension;
+       ESource *source;
+       ESourceAuthenticationResult result;
+       GHashTable *known_sources; /* resource-id ~> source's UID */
+       GList *sources;
+       GSList *discovered_sources = NULL;
+       ENamedParameters *credentials_copy = NULL;
+       gboolean any_success = FALSE;
+       GError *local_error = NULL;
+
+       g_return_val_if_fail (E_IS_WEBDAV_COLLECTION_BACKEND (webdav_backend), E_SOURCE_AUTHENTICATION_ERROR);
+
+       collection = E_COLLECTION_BACKEND (webdav_backend);
+       source = e_backend_get_source (E_BACKEND (webdav_backend));
+       collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
+
+       if (!e_source_collection_get_calendar_enabled (collection_extension) &&
+           !e_source_collection_get_contacts_enabled (collection_extension))
+               return E_SOURCE_AUTHENTICATION_ACCEPTED;
+
+       if (credentials && !e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME)) {
+               credentials_copy = e_named_parameters_new_clone (credentials);
+               e_named_parameters_set (credentials_copy, E_SOURCE_CREDENTIAL_USERNAME, 
e_source_collection_get_identity (collection_extension));
+               credentials = credentials_copy;
+       }
+
+       known_sources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+       sources = e_collection_backend_list_calendar_sources (collection);
+       g_list_foreach (sources, webdav_collection_add_uid_to_hashtable, known_sources);
+       g_list_free_full (sources, g_object_unref);
+
+       sources = e_collection_backend_list_contacts_sources (collection);
+       g_list_foreach (sources, webdav_collection_add_uid_to_hashtable, known_sources);
+       g_list_free_full (sources, g_object_unref);
+
+       if (e_source_collection_get_calendar_enabled (collection_extension) && calendar_url &&
+           e_webdav_discover_sources_sync (source, calendar_url,
+               E_WEBDAV_DISCOVER_SUPPORTS_EVENTS | E_WEBDAV_DISCOVER_SUPPORTS_MEMOS | 
E_WEBDAV_DISCOVER_SUPPORTS_TASKS,
+               credentials, out_certificate_pem, out_certificate_errors,
+               &discovered_sources, NULL, cancellable, &local_error)) {
+               EWebDAVDiscoverSupports source_types[] = {
+                       E_WEBDAV_DISCOVER_SUPPORTS_EVENTS,
+                       E_WEBDAV_DISCOVER_SUPPORTS_MEMOS,
+                       E_WEBDAV_DISCOVER_SUPPORTS_TASKS
+               };
+
+               webdav_collection_process_discovered_sources (collection, discovered_sources, known_sources, 
source_types, G_N_ELEMENTS (source_types));
+
+               e_webdav_discover_free_discovered_sources (discovered_sources);
+               discovered_sources = NULL;
+               any_success = TRUE;
+       }
+
+       if (!local_error && e_source_collection_get_contacts_enabled (collection_extension) && contacts_url &&
+           e_webdav_discover_sources_sync (source, contacts_url, E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS,
+               credentials, out_certificate_pem, out_certificate_errors,
+               &discovered_sources, NULL, cancellable, &local_error)) {
+               EWebDAVDiscoverSupports source_types[] = {
+                       E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS
+               };
+
+               webdav_collection_process_discovered_sources (collection, discovered_sources, known_sources, 
source_types, G_N_ELEMENTS (source_types));
+
+               e_webdav_discover_free_discovered_sources (discovered_sources);
+               discovered_sources = NULL;
+               any_success = TRUE;
+       }
+
+       if (any_success) {
+               ESourceRegistryServer *server;
+
+               server = e_collection_backend_ref_server (collection);
+
+               if (server) {
+                       RemoveSourcesData rsd;
+
+                       rsd.server = server;
+                       rsd.webdav_backend = webdav_backend;
+
+                       g_hash_table_foreach (known_sources, webdav_collection_remove_unknown_sources_cb, 
&rsd);
+                       g_object_unref (server);
+               }
+
+               g_clear_error (&local_error);
+       }
+
+       if (local_error == NULL) {
+               result = E_SOURCE_AUTHENTICATION_ACCEPTED;
+               e_collection_backend_authenticate_children (collection, credentials);
+       } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) ||
+                  g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_FORBIDDEN)) {
+               result = E_SOURCE_AUTHENTICATION_REJECTED;
+               g_clear_error (&local_error);
+       } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
+               result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
+               g_propagate_error (error, local_error);
+       } else {
+               result = E_SOURCE_AUTHENTICATION_ERROR;
+               g_propagate_error (error, local_error);
+       }
+
+       g_hash_table_destroy (known_sources);
+       e_named_parameters_free (credentials_copy);
+
+       return result;
+}
diff --git a/src/libebackend/e-webdav-collection-backend.h b/src/libebackend/e-webdav-collection-backend.h
new file mode 100644
index 0000000..949e234
--- /dev/null
+++ b/src/libebackend/e-webdav-collection-backend.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 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 (__LIBEBACKEND_H_INSIDE__) && !defined (LIBEBACKEND_COMPILATION)
+#error "Only <libebackend/libebackend.h> should be included directly."
+#endif
+
+#ifndef E_WEBDAV_COLLECTION_BACKEND_H
+#define E_WEBDAV_COLLECTION_BACKEND_H
+
+#include <libebackend/e-collection-backend.h>
+#include <libedataserver/libedataserver.h>
+
+/* Standard GObject macros */
+#define E_TYPE_WEBDAV_COLLECTION_BACKEND \
+       (e_webdav_collection_backend_get_type ())
+#define E_WEBDAV_COLLECTION_BACKEND(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_WEBDAV_COLLECTION_BACKEND, EWebDAVCollectionBackend))
+#define E_WEBDAV_COLLECTION_BACKEND_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_WEBDAV_COLLECTION_BACKEND, EWebDAVCollectionBackendClass))
+#define E_IS_WEBDAV_COLLECTION_BACKEND(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_WEBDAV_COLLECTION_BACKEND))
+#define E_IS_WEBDAV_COLLECTION_BACKEND_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_WEBDAV_COLLECTION_BACKEND))
+#define E_WEBDAV_COLLECTION_BACKEND_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_WEBDAV_COLLECTION_BACKEND, EWebDAVCollectionBackendClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EWebDAVCollectionBackend EWebDAVCollectionBackend;
+typedef struct _EWebDAVCollectionBackendClass EWebDAVCollectionBackendClass;
+typedef struct _EWebDAVCollectionBackendPrivate EWebDAVCollectionBackendPrivate;
+
+/**
+ * EWebDAVCollectionBackend:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.26
+ **/
+struct _EWebDAVCollectionBackend {
+       /*< private >*/
+       ECollectionBackend parent;
+       EWebDAVCollectionBackendPrivate *priv;
+};
+
+struct _EWebDAVCollectionBackendClass {
+       ECollectionBackendClass parent_class;
+
+       gchar *         (* get_resource_id)     (EWebDAVCollectionBackend *webdav_backend,
+                                                ESource *source);
+       gboolean        (* is_custom_source)    (EWebDAVCollectionBackend *webdav_backend,
+                                                ESource *source);
+};
+
+GType          e_webdav_collection_backend_get_type    (void);
+
+gchar *                e_webdav_collection_backend_get_resource_id
+                                               (EWebDAVCollectionBackend *webdav_backend,
+                                                ESource *source);
+gboolean       e_webdav_collection_backend_is_custom_source
+                                               (EWebDAVCollectionBackend *webdav_backend,
+                                                ESource *source);
+ESourceAuthenticationResult
+               e_webdav_collection_backend_discover_sync
+                                               (EWebDAVCollectionBackend *webdav_backend,
+                                                const gchar *calendar_url,
+                                                const gchar *contacts_url,
+                                                const ENamedParameters *credentials,
+                                                gchar **out_certificate_pem,
+                                                GTlsCertificateFlags *out_certificate_errors,
+                                                GCancellable *cancellable,
+                                                GError **error);
+
+G_END_DECLS
+
+#endif /* E_WEBDAV_COLLECTION_BACKEND_H */
diff --git a/src/libebackend/libebackend.h b/src/libebackend/libebackend.h
index d1559fd..9a7c8c2 100644
--- a/src/libebackend/libebackend.h
+++ b/src/libebackend/libebackend.h
@@ -42,6 +42,7 @@
 #include <libebackend/e-user-prompter.h>
 #include <libebackend/e-user-prompter-server.h>
 #include <libebackend/e-user-prompter-server-extension.h>
+#include <libebackend/e-webdav-collection-backend.h>
 
 #undef __LIBEBACKEND_H_INSIDE__
 
diff --git a/src/modules/google-backend/module-google-backend.c 
b/src/modules/google-backend/module-google-backend.c
index 0f4887f..c33e869 100644
--- a/src/modules/google-backend/module-google-backend.c
+++ b/src/modules/google-backend/module-google-backend.c
@@ -72,11 +72,11 @@ typedef struct _EGoogleBackendFactory EGoogleBackendFactory;
 typedef struct _EGoogleBackendFactoryClass EGoogleBackendFactoryClass;
 
 struct _EGoogleBackend {
-       ECollectionBackend parent;
+       EWebDAVCollectionBackend parent;
 };
 
 struct _EGoogleBackendClass {
-       ECollectionBackendClass parent_class;
+       EWebDAVCollectionBackendClass parent_class;
 };
 
 struct _EGoogleBackendFactory {
@@ -98,7 +98,7 @@ GType e_google_backend_factory_get_type (void);
 G_DEFINE_DYNAMIC_TYPE (
        EGoogleBackend,
        e_google_backend,
-       E_TYPE_COLLECTION_BACKEND)
+       E_TYPE_WEBDAV_COLLECTION_BACKEND)
 
 G_DEFINE_DYNAMIC_TYPE (
        EGoogleBackendFactory,
@@ -272,13 +272,14 @@ google_backend_contacts_update_auth_method_cb (ESource *child_source,
 }
 
 static void
-google_add_uid_to_hashtable (gpointer source,
-                            gpointer known_sources)
+google_add_task_list_uid_to_hashtable (gpointer source,
+                                      gpointer known_sources)
 {
        ESourceResource *resource;
        gchar *uid, *rid;
 
-       if (!e_source_has_extension (source, E_SOURCE_EXTENSION_RESOURCE))
+       if (!e_source_has_extension (source, E_SOURCE_EXTENSION_RESOURCE) ||
+           !e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
                return;
 
        resource = e_source_get_extension (source, E_SOURCE_EXTENSION_RESOURCE);
@@ -315,138 +316,6 @@ google_remove_unknown_sources_cb (gpointer resource_id,
        }
 }
 
-static void
-google_add_found_source (ECollectionBackend *collection,
-                        EWebDAVDiscoverSupports source_type,
-                        SoupURI *uri,
-                        const gchar *display_name,
-                        const gchar *color,
-                        GHashTable *known_sources)
-{
-       ESourceRegistryServer *server;
-       ESourceBackend *backend;
-       ESource *source = NULL;
-       const gchar *backend_name = NULL;
-       const gchar *provider = NULL;
-       const gchar *identity_prefix = NULL;
-       const gchar *source_uid;
-       gboolean is_new;
-       gchar *url;
-       gchar *identity;
-
-       g_return_if_fail (collection != NULL);
-       g_return_if_fail (uri != NULL);
-       g_return_if_fail (display_name != NULL);
-       g_return_if_fail (known_sources != NULL);
-
-       switch (source_type) {
-       case E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS:
-               backend_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
-               provider = "webdav";
-               identity_prefix = "contacts";
-               break;
-       case E_WEBDAV_DISCOVER_SUPPORTS_EVENTS:
-               backend_name = E_SOURCE_EXTENSION_CALENDAR;
-               provider = "caldav";
-               identity_prefix = "events";
-               break;
-       case E_WEBDAV_DISCOVER_SUPPORTS_MEMOS:
-               backend_name = E_SOURCE_EXTENSION_MEMO_LIST;
-               provider = "caldav";
-               identity_prefix = "memos";
-               break;
-       case E_WEBDAV_DISCOVER_SUPPORTS_TASKS:
-               backend_name = E_SOURCE_EXTENSION_TASK_LIST;
-               provider = "caldav";
-               identity_prefix = "tasks";
-               break;
-       default:
-               g_warn_if_reached ();
-               return;
-       }
-
-       g_return_if_fail (backend_name != NULL);
-
-       server = e_collection_backend_ref_server (collection);
-       if (!server)
-               return;
-
-       url = soup_uri_to_string (uri, FALSE);
-       identity = g_strconcat (identity_prefix, "::", url, NULL);
-       source_uid = g_hash_table_lookup (known_sources, identity);
-       is_new = !source_uid;
-       if (is_new) {
-               source = e_collection_backend_new_child (collection, identity);
-               g_warn_if_fail (source != NULL);
-       } else {
-               source = e_source_registry_server_ref_source (server, source_uid);
-               g_warn_if_fail (source != NULL);
-
-               g_hash_table_remove (known_sources, identity);
-       }
-
-       if (source) {
-               ESource *master_source;
-               ESourceCollection *collection_extension;
-               ESourceAuthentication *child_auth;
-               ESourceResource *resource;
-               ESourceWebdav *master_webdav, *child_webdav;
-
-               master_source = e_backend_get_source (E_BACKEND (collection));
-               master_webdav = e_source_get_extension (master_source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
-               collection_extension = e_source_get_extension (master_source, E_SOURCE_EXTENSION_COLLECTION);
-               child_auth = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
-               child_webdav = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
-               resource = e_source_get_extension (source, E_SOURCE_EXTENSION_RESOURCE);
-
-               google_backend_calendar_update_auth_method (source, master_source);
-
-               e_source_authentication_set_user (child_auth, e_source_collection_get_identity 
(collection_extension));
-               e_source_webdav_set_soup_uri (child_webdav, uri);
-               e_source_resource_set_identity (resource, identity);
-
-               if (is_new) {
-                       /* inherit ssl trust options */
-                       e_source_webdav_set_ssl_trust (child_webdav, e_source_webdav_get_ssl_trust 
(master_webdav));
-               }
-       }
-
-       g_free (identity);
-       g_free (url);
-
-       /* these properties are synchronized always */
-       if (source) {
-               gint rr, gg, bb;
-
-               backend = e_source_get_extension (source, backend_name);
-               e_source_backend_set_backend_name (backend, provider);
-
-               e_source_set_display_name (source, display_name);
-               e_source_set_enabled (source, TRUE);
-
-               /* Also check whether the color format is as expected; it cannot
-                  be used gdk_rgba_parse here, because it required gdk/gtk. */
-               if (is_new && source_type != E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS && color &&
-                   sscanf (color, "#%02x%02x%02x", &rr, &gg, &bb) == 3) {
-                       gchar *safe_color;
-
-                       /* In case an #RRGGBBAA is returned */
-                       safe_color = g_strdup_printf ("#%02x%02x%02x", rr, gg, bb);
-
-                       e_source_selectable_set_color (E_SOURCE_SELECTABLE (backend), safe_color);
-
-                       g_free (safe_color);
-               }
-
-               if (is_new)
-                       e_source_registry_server_add_source (server, source);
-
-               g_object_unref (source);
-       }
-
-       g_object_unref (server);
-}
-
 #if GDATA_CHECK_VERSION(0,15,1)
 static void
 google_add_task_list (ECollectionBackend *collection,
@@ -541,13 +410,11 @@ google_backend_authenticate_sync (EBackend *backend,
        ESourceCollection *collection_extension;
        ESourceGoa *goa_extension = NULL;
        ESource *source;
-       ESourceAuthenticationResult result;
+       ESourceAuthenticationResult result = E_SOURCE_AUTHENTICATION_ERROR;
        GHashTable *known_sources;
        GList *sources;
-       GSList *discovered_sources = NULL;
        ENamedParameters *credentials_copy = NULL;
        const gchar *calendar_url;
-       gboolean any_success = FALSE;
        GError *local_error = NULL;
 
        g_return_val_if_fail (collection != NULL, E_SOURCE_AUTHENTICATION_ERROR);
@@ -572,7 +439,7 @@ google_backend_authenticate_sync (EBackend *backend,
        known_sources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
        sources = e_collection_backend_list_calendar_sources (collection);
-       g_list_foreach (sources, google_add_uid_to_hashtable, known_sources);
+       g_list_foreach (sources, google_add_task_list_uid_to_hashtable, known_sources);
        g_list_free_full (sources, g_object_unref);
 
        /* When the WebDAV extension is created, the auth method can be reset, thus ensure
@@ -596,57 +463,9 @@ google_backend_authenticate_sync (EBackend *backend,
                }
        }
 
-       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,
-               &discovered_sources, NULL, cancellable, &local_error)) {
-               EWebDAVDiscoverSupports source_types[] = {
-                       E_WEBDAV_DISCOVER_SUPPORTS_EVENTS,
-                       E_WEBDAV_DISCOVER_SUPPORTS_MEMOS,
-                       E_WEBDAV_DISCOVER_SUPPORTS_TASKS
-               };
-               GSList *link;
-               gint ii;
-
-               for (link = discovered_sources; link; link = g_slist_next (link)) {
-                       EWebDAVDiscoveredSource *discovered_source = link->data;
-                       SoupURI *soup_uri;
-
-                       if (!discovered_source || !discovered_source->href || 
!discovered_source->display_name)
-                               continue;
-
-                       soup_uri = soup_uri_new (discovered_source->href);
-                       if (!soup_uri)
-                               continue;
-
-                       for (ii = 0; ii < G_N_ELEMENTS (source_types); ii++) {
-                               if ((discovered_source->supports & source_types[ii]) == source_types[ii])
-                                       google_add_found_source (collection, source_types[ii], soup_uri,
-                                               discovered_source->display_name, discovered_source->color, 
known_sources);
-                       }
-
-                       soup_uri_free (soup_uri);
-               }
-
-               any_success = TRUE;
-       }
-
-       if (any_success)
-               g_clear_error (&local_error);
-
-       if (local_error == NULL) {
-               result = E_SOURCE_AUTHENTICATION_ACCEPTED;
-               e_collection_backend_authenticate_children (collection, credentials);
-       } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) ||
-                  g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_FORBIDDEN)) {
-               result = E_SOURCE_AUTHENTICATION_REJECTED;
-               g_clear_error (&local_error);
-       } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
-               result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
-               g_propagate_error (error, local_error);
-       } else {
-               result = E_SOURCE_AUTHENTICATION_ERROR;
-               g_propagate_error (error, local_error);
+       if (e_source_collection_get_calendar_enabled (collection_extension) && calendar_url) {
+               result = e_webdav_collection_backend_discover_sync (E_WEBDAV_COLLECTION_BACKEND (backend), 
calendar_url, NULL,
+                       credentials, out_certificate_pem, out_certificate_errors, cancellable, error);
        }
 
 #if GDATA_CHECK_VERSION(0,15,1)
@@ -700,7 +519,7 @@ google_backend_authenticate_sync (EBackend *backend,
        }
 #endif /* GDATA_CHECK_VERSION(0,15,1) */
 
-       if (any_success) {
+       if (result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
                ESourceRegistryServer *server;
 
                server = e_collection_backend_ref_server (collection);
@@ -768,76 +587,66 @@ google_backend_add_contacts (ECollectionBackend *backend)
        g_object_unref (source);
 }
 
+static gchar *
+google_backend_get_resource_id (EWebDAVCollectionBackend *webdav_backend,
+                               ESource *source)
+{
+       g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK))
+               return g_strdup (GOOGLE_CONTACTS_RESOURCE_ID);
+
+       /* Chain up to parent's method. */
+       return E_WEBDAV_COLLECTION_BACKEND_CLASS (e_google_backend_parent_class)->get_resource_id 
(webdav_backend, source);
+}
+
+static gboolean
+google_backend_is_custom_source (EWebDAVCollectionBackend *webdav_backend,
+                                ESource *source)
+{
+       g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK) ||
+           e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
+               return TRUE;
+
+       /* Chain up to parent's method. */
+       return E_WEBDAV_COLLECTION_BACKEND_CLASS (e_google_backend_parent_class)->is_custom_source 
(webdav_backend, source);
+}
+
 static void
 google_backend_populate (ECollectionBackend *backend)
 {
-       GList *list, *link;
-       ESourceRegistryServer *server;
        ESourceCollection *collection_extension;
        ESource *source;
 
-       server = e_collection_backend_ref_server (backend);
-       list = e_collection_backend_claim_all_resources (backend);
-       for (link = list; link; link = g_list_next (link)) {
-               ESource *source = link->data;
-               ESource *child = NULL;
-
-               if (e_source_has_extension (source, E_SOURCE_EXTENSION_RESOURCE)) {
-                       ESourceResource *resource;
-
-                       resource = e_source_get_extension (source, E_SOURCE_EXTENSION_RESOURCE);
-                       child = e_collection_backend_new_child (backend, e_source_resource_get_identity 
(resource));
-               } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
-                       child = e_collection_backend_new_child (backend, GOOGLE_CONTACTS_RESOURCE_ID);
-               }
-
-               if (child) {
-                       e_source_registry_server_add_source (server, source);
-                       g_object_unref (child);
-               }
-       }
-
-       g_list_free_full (list, g_object_unref);
-       g_object_unref (server);
+       /* Chain up to parent's method. */
+       E_COLLECTION_BACKEND_CLASS (e_google_backend_parent_class)->populate (backend);
 
        source = e_backend_get_source (E_BACKEND (backend));
        collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
 
        if (e_source_collection_get_contacts_enabled (collection_extension)) {
+               GList *list;
+
                list = e_collection_backend_list_contacts_sources (backend);
                if (list == NULL)
                        google_backend_add_contacts (backend);
                g_list_free_full (list, (GDestroyNotify) g_object_unref);
        }
 
-       /* Chain up to parent's populate() method. */
-       E_COLLECTION_BACKEND_CLASS (e_google_backend_parent_class)->populate (backend);
-
-       if (e_source_get_enabled (source) && e_source_collection_get_calendar_enabled (collection_extension)) 
{
-               e_backend_schedule_credentials_required (E_BACKEND (backend),
-                       E_SOURCE_CREDENTIALS_REASON_REQUIRED, NULL, 0, NULL, NULL, G_STRFUNC);
-       }
 }
 
 static gchar *
 google_backend_dup_resource_id (ECollectionBackend *backend,
                                 ESource *child_source)
 {
-       const gchar *extension_name;
-
-       /* XXX This is trivial for now since we only
-        *     add one calendar and one address book. */
-
-       extension_name = E_SOURCE_EXTENSION_CALENDAR;
-       if (e_source_has_extension (child_source, extension_name))
-               return E_COLLECTION_BACKEND_CLASS (e_google_backend_parent_class)->dup_resource_id (backend, 
child_source);
-
-       extension_name = E_SOURCE_EXTENSION_TASK_LIST;
-       if (e_source_has_extension (child_source, extension_name))
+       if (e_source_has_extension (child_source, E_SOURCE_EXTENSION_CALENDAR) ||
+           e_source_has_extension (child_source, E_SOURCE_EXTENSION_MEMO_LIST) ||
+           e_source_has_extension (child_source, E_SOURCE_EXTENSION_TASK_LIST))
                return E_COLLECTION_BACKEND_CLASS (e_google_backend_parent_class)->dup_resource_id (backend, 
child_source);
 
-       extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
-       if (e_source_has_extension (child_source, extension_name))
+       if (e_source_has_extension (child_source, E_SOURCE_EXTENSION_ADDRESS_BOOK))
                return g_strdup (GOOGLE_CONTACTS_RESOURCE_ID);
 
        return NULL;
@@ -996,6 +805,7 @@ e_google_backend_class_init (EGoogleBackendClass *class)
 {
        EBackendClass *backend_class;
        ECollectionBackendClass *collection_backend_class;
+       EWebDAVCollectionBackendClass *webdav_collection_backend_class;
 
        backend_class = E_BACKEND_CLASS (class);
        backend_class->authenticate_sync = google_backend_authenticate_sync;
@@ -1006,6 +816,10 @@ e_google_backend_class_init (EGoogleBackendClass *class)
        collection_backend_class->dup_resource_id = google_backend_dup_resource_id;
        collection_backend_class->child_added = google_backend_child_added;
        collection_backend_class->child_removed = google_backend_child_removed;
+
+       webdav_collection_backend_class = E_WEBDAV_COLLECTION_BACKEND_CLASS (class);
+       webdav_collection_backend_class->get_resource_id = google_backend_get_resource_id;
+       webdav_collection_backend_class->is_custom_source = google_backend_is_custom_source;
 }
 
 static void
diff --git a/src/modules/owncloud-backend/module-owncloud-backend.c 
b/src/modules/owncloud-backend/module-owncloud-backend.c
index 27b6980..0768959 100644
--- a/src/modules/owncloud-backend/module-owncloud-backend.c
+++ b/src/modules/owncloud-backend/module-owncloud-backend.c
@@ -37,11 +37,11 @@ typedef struct _EOwncloudBackendFactory EOwncloudBackendFactory;
 typedef struct _EOwncloudBackendFactoryClass EOwncloudBackendFactoryClass;
 
 struct _EOwncloudBackend {
-       ECollectionBackend parent;
+       EWebDAVCollectionBackend parent;
 };
 
 struct _EOwncloudBackendClass {
-       ECollectionBackendClass parent_class;
+       EWebDAVCollectionBackendClass parent_class;
 };
 
 struct _EOwncloudBackendFactory {
@@ -63,218 +63,13 @@ GType e_owncloud_backend_factory_get_type (void);
 G_DEFINE_DYNAMIC_TYPE (
        EOwncloudBackend,
        e_owncloud_backend,
-       E_TYPE_COLLECTION_BACKEND)
+       E_TYPE_WEBDAV_COLLECTION_BACKEND)
 
 G_DEFINE_DYNAMIC_TYPE (
        EOwncloudBackendFactory,
        e_owncloud_backend_factory,
        E_TYPE_COLLECTION_BACKEND_FACTORY)
 
-static void
-owncloud_add_uid_to_hashtable (gpointer source,
-                              gpointer known_sources)
-{
-       ESourceResource *resource;
-       gchar *uid, *rid;
-
-       if (!e_source_has_extension (source, E_SOURCE_EXTENSION_RESOURCE))
-               return;
-
-       resource = e_source_get_extension (source, E_SOURCE_EXTENSION_RESOURCE);
-
-       uid = e_source_dup_uid (source);
-       if (!uid || !*uid) {
-               g_free (uid);
-               return;
-       }
-
-       rid = e_source_resource_dup_identity (resource);
-       if (!rid || !*rid) {
-               g_free (rid);
-               g_free (uid);
-               return;
-       }
-
-       g_hash_table_insert (known_sources, rid, uid);
-}
-
-static void
-owncloud_remove_unknown_sources_cb (gpointer resource_id,
-                                    gpointer uid,
-                                    gpointer user_data)
-{
-       ESourceRegistryServer *server = user_data;
-       ESource *source;
-
-       source = e_source_registry_server_ref_source (server, uid);
-
-       if (source) {
-               e_source_remove_sync (source, NULL, NULL);
-               g_object_unref (source);
-       }
-}
-
-static void
-owncloud_add_found_source (ECollectionBackend *collection,
-                          EWebDAVDiscoverSupports source_type,
-                          SoupURI *uri,
-                          const gchar *display_name,
-                          const gchar *color,
-                          GHashTable *known_sources)
-{
-       ESourceRegistryServer *server;
-       ESourceBackend *backend;
-       ESource *source = NULL;
-       const gchar *backend_name = NULL;
-       const gchar *provider = NULL;
-       const gchar *identity_prefix = NULL;
-       const gchar *source_uid;
-       gboolean is_new;
-       gchar *url;
-       gchar *identity;
-
-       g_return_if_fail (collection != NULL);
-       g_return_if_fail (uri != NULL);
-       g_return_if_fail (display_name != NULL);
-       g_return_if_fail (known_sources != NULL);
-
-       switch (source_type) {
-       case E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS:
-               backend_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
-               provider = "webdav";
-               identity_prefix = "contacts";
-               break;
-       case E_WEBDAV_DISCOVER_SUPPORTS_EVENTS:
-               backend_name = E_SOURCE_EXTENSION_CALENDAR;
-               provider = "caldav";
-               identity_prefix = "events";
-               break;
-       case E_WEBDAV_DISCOVER_SUPPORTS_MEMOS:
-               backend_name = E_SOURCE_EXTENSION_MEMO_LIST;
-               provider = "caldav";
-               identity_prefix = "memos";
-               break;
-       case E_WEBDAV_DISCOVER_SUPPORTS_TASKS:
-               backend_name = E_SOURCE_EXTENSION_TASK_LIST;
-               provider = "caldav";
-               identity_prefix = "tasks";
-               break;
-       default:
-               g_warn_if_reached ();
-               return;
-       }
-
-       g_return_if_fail (backend_name != NULL);
-
-       server = e_collection_backend_ref_server (collection);
-       if (!server)
-               return;
-
-       url = soup_uri_to_string (uri, FALSE);
-       identity = g_strconcat (identity_prefix, "::", url, NULL);
-       source_uid = g_hash_table_lookup (known_sources, identity);
-       is_new = !source_uid;
-       if (is_new) {
-               source = e_collection_backend_new_child (collection, identity);
-               g_warn_if_fail (source != NULL);
-       } else {
-               source = e_source_registry_server_ref_source (server, source_uid);
-               g_warn_if_fail (source != NULL);
-
-               g_hash_table_remove (known_sources, identity);
-       }
-
-       if (source) {
-               ESource *master_source;
-               ESourceCollection *collection_extension;
-               ESourceAuthentication *child_auth;
-               ESourceResource *resource;
-               ESourceWebdav *master_webdav, *child_webdav;
-
-               master_source = e_backend_get_source (E_BACKEND (collection));
-               master_webdav = e_source_get_extension (master_source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
-               collection_extension = e_source_get_extension (master_source, E_SOURCE_EXTENSION_COLLECTION);
-               child_auth = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
-               child_webdav = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
-               resource = e_source_get_extension (source, E_SOURCE_EXTENSION_RESOURCE);
-
-               e_source_authentication_set_user (child_auth, e_source_collection_get_identity 
(collection_extension));
-               e_source_webdav_set_soup_uri (child_webdav, uri);
-               e_source_resource_set_identity (resource, identity);
-
-               if (is_new) {
-                       /* inherit ssl trust options */
-                       e_source_webdav_set_ssl_trust (child_webdav, e_source_webdav_get_ssl_trust 
(master_webdav));
-               }
-       }
-
-       g_free (identity);
-       g_free (url);
-
-       /* these properties are synchronized always */
-       if (source) {
-               gint rr, gg, bb;
-
-               backend = e_source_get_extension (source, backend_name);
-               e_source_backend_set_backend_name (backend, provider);
-
-               e_source_set_display_name (source, display_name);
-               e_source_set_enabled (source, TRUE);
-
-               /* Also check whether the color format is as expected; it cannot
-                  be used gdk_rgba_parse here, because it required gdk/gtk. */
-               if (is_new && source_type != E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS && color &&
-                   sscanf (color, "#%02x%02x%02x", &rr, &gg, &bb) == 3) {
-                       gchar *safe_color;
-
-                       /* In case an #RRGGBBAA is returned */
-                       safe_color = g_strdup_printf ("#%02x%02x%02x", rr, gg, bb);
-
-                       e_source_selectable_set_color (E_SOURCE_SELECTABLE (backend), safe_color);
-
-                       g_free (safe_color);
-               }
-
-               if (is_new)
-                       e_source_registry_server_add_source (server, source);
-
-               g_object_unref (source);
-       }
-
-       g_object_unref (server);
-}
-
-static void
-owncloud_process_discovered_sources (ECollectionBackend *collection,
-                                    GSList *discovered_sources,
-                                    GHashTable *known_sources,
-                                    const EWebDAVDiscoverSupports *source_types,
-                                    gint n_source_types)
-{
-       GSList *link;
-       gint ii;
-
-       for (link = discovered_sources; link; link = g_slist_next (link)) {
-               EWebDAVDiscoveredSource *discovered_source = link->data;
-               SoupURI *soup_uri;
-
-               if (!discovered_source || !discovered_source->href || !discovered_source->display_name)
-                       continue;
-
-               soup_uri = soup_uri_new (discovered_source->href);
-               if (!soup_uri)
-                       continue;
-
-               for (ii = 0; ii < n_source_types; ii++) {
-                       if ((discovered_source->supports & source_types[ii]) == source_types[ii])
-                               owncloud_add_found_source (collection, source_types[ii], soup_uri,
-                                       discovered_source->display_name, discovered_source->color, 
known_sources);
-               }
-
-               soup_uri_free (soup_uri);
-       }
-}
-
 static ESourceAuthenticationResult
 owncloud_backend_authenticate_sync (EBackend *backend,
                                    const ENamedParameters *credentials,
@@ -283,22 +78,12 @@ owncloud_backend_authenticate_sync (EBackend *backend,
                                    GCancellable *cancellable,
                                    GError **error)
 {
-       ECollectionBackend *collection = E_COLLECTION_BACKEND (backend);
-       ESourceCollection *collection_extension;
        ESourceGoa *goa_extension;
        ESource *source;
-       ESourceAuthenticationResult result;
-       GHashTable *known_sources;
-       GList *sources;
-       GSList *discovered_sources = NULL;
-       ENamedParameters *credentials_copy = NULL;
-       gboolean any_success = FALSE, contacts_found = FALSE;
-       GError *local_error = NULL;
 
-       g_return_val_if_fail (collection != NULL, E_SOURCE_AUTHENTICATION_ERROR);
+       g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), E_SOURCE_AUTHENTICATION_ERROR);
 
        source = e_backend_get_source (backend);
-       collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
 
        /* Ignore the request for non-GOA ownCloud sources by pretending success */
        if (!e_source_has_extension (source, E_SOURCE_EXTENSION_GOA))
@@ -306,160 +91,19 @@ owncloud_backend_authenticate_sync (EBackend *backend,
 
        goa_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_GOA);
 
-       if (!e_source_collection_get_calendar_enabled (collection_extension) &&
-           !e_source_collection_get_contacts_enabled (collection_extension))
-               return E_SOURCE_AUTHENTICATION_ACCEPTED;
-
-       if (credentials && !e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME)) {
-               credentials_copy = e_named_parameters_new_clone (credentials);
-               e_named_parameters_set (credentials_copy, E_SOURCE_CREDENTIAL_USERNAME, 
e_source_collection_get_identity (collection_extension));
-               credentials = credentials_copy;
-       }
-
-       /* resource-id => source's UID */
-       known_sources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-
-       sources = e_collection_backend_list_calendar_sources (collection);
-       g_list_foreach (sources, owncloud_add_uid_to_hashtable, known_sources);
-       g_list_free_full (sources, g_object_unref);
-
-       sources = e_collection_backend_list_contacts_sources (collection);
-       g_list_foreach (sources, owncloud_add_uid_to_hashtable, known_sources);
-       g_list_free_full (sources, g_object_unref);
-
-       if (e_source_collection_get_calendar_enabled (collection_extension) && e_source_goa_get_calendar_url 
(goa_extension) &&
-           e_webdav_discover_sources_sync (source, e_source_goa_get_calendar_url (goa_extension), 
E_WEBDAV_DISCOVER_SUPPORTS_NONE,
-               credentials, out_certificate_pem, out_certificate_errors,
-               &discovered_sources, NULL, cancellable, &local_error)) {
-               GSList *link;
-               EWebDAVDiscoverSupports source_types[] = {
-                       E_WEBDAV_DISCOVER_SUPPORTS_EVENTS,
-                       E_WEBDAV_DISCOVER_SUPPORTS_MEMOS,
-                       E_WEBDAV_DISCOVER_SUPPORTS_TASKS,
-                       E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS
-               };
-
-               for (link = discovered_sources; link && !contacts_found; link = g_slist_next (link)) {
-                       EWebDAVDiscoveredSource *discovered_source = link->data;
-
-                       if (discovered_source)
-                               contacts_found = contacts_found || (discovered_source->supports & 
E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS) != 0;
-               }
-
-               owncloud_process_discovered_sources (collection, discovered_sources, known_sources, 
source_types,
-                       G_N_ELEMENTS (source_types) - (contacts_found ? 0 : 1));
-
-               e_webdav_discover_free_discovered_sources (discovered_sources);
-               discovered_sources = NULL;
-               any_success = TRUE;
-       }
-
-       /* Skip search in this URL, if the previous one returned also contacts - it's quite likely it did */
-       if (!contacts_found && !local_error && e_source_collection_get_contacts_enabled 
(collection_extension) &&
-           e_source_goa_get_contacts_url (goa_extension) &&
-           e_webdav_discover_sources_sync (source, e_source_goa_get_contacts_url (goa_extension), 
E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS,
-               credentials, out_certificate_pem, out_certificate_errors,
-               &discovered_sources, NULL, cancellable, &local_error)) {
-               EWebDAVDiscoverSupports source_types[] = {
-                       E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS
-               };
-
-               owncloud_process_discovered_sources (collection, discovered_sources, known_sources, 
source_types, G_N_ELEMENTS (source_types));
-
-               e_webdav_discover_free_discovered_sources (discovered_sources);
-               discovered_sources = NULL;
-               any_success = TRUE;
-       }
-
-       if (any_success) {
-               ESourceRegistryServer *server;
-
-               server = e_collection_backend_ref_server (collection);
-
-               if (server) {
-                       g_hash_table_foreach (known_sources, owncloud_remove_unknown_sources_cb, server);
-                       g_object_unref (server);
-               }
-
-               g_clear_error (&local_error);
-       }
-
-       if (local_error == NULL) {
-               result = E_SOURCE_AUTHENTICATION_ACCEPTED;
-               e_collection_backend_authenticate_children (collection, credentials);
-       } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) ||
-                  g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_FORBIDDEN)) {
-               result = E_SOURCE_AUTHENTICATION_REJECTED;
-               g_clear_error (&local_error);
-       } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
-               result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
-               g_propagate_error (error, local_error);
-       } else {
-               result = E_SOURCE_AUTHENTICATION_ERROR;
-               g_propagate_error (error, local_error);
-       }
-
-       g_hash_table_destroy (known_sources);
-       e_named_parameters_free (credentials_copy);
-
-       return result;
-}
-
-static void
-owncloud_backend_populate (ECollectionBackend *collection)
-{
-       GList *list, *liter;
-       ESourceRegistryServer *server;
-       ESourceCollection *collection_extension;
-       ESource *source;
-
-       /* Chain up to parent's populate() method. */
-       E_COLLECTION_BACKEND_CLASS (e_owncloud_backend_parent_class)->populate (collection);
-
-       server = e_collection_backend_ref_server (collection);
-       list = e_collection_backend_claim_all_resources (collection);
-
-       for (liter = list; liter; liter = g_list_next (liter)) {
-               ESource *source = liter->data;
-
-               if (e_source_has_extension (source, E_SOURCE_EXTENSION_RESOURCE)) {
-                       ESourceResource *resource;
-                       ESource *child;
-
-                       resource = e_source_get_extension (source, E_SOURCE_EXTENSION_RESOURCE);
-                       child = e_collection_backend_new_child (collection, e_source_resource_get_identity 
(resource));
-                       if (child) {
-                               e_source_registry_server_add_source (server, source);
-                               g_object_unref (child);
-                       }
-               }
-       }
-
-       g_list_free_full (list, g_object_unref);
-       g_object_unref (server);
-
-       source = e_backend_get_source (E_BACKEND (collection));
-       collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
-
-       if (e_source_get_enabled (source) && (
-           e_source_collection_get_calendar_enabled (collection_extension) ||
-           e_source_collection_get_contacts_enabled (collection_extension))) {
-               e_backend_schedule_credentials_required (E_BACKEND (collection),
-                       E_SOURCE_CREDENTIALS_REASON_REQUIRED, NULL, 0, NULL, NULL, G_STRFUNC);
-       }
+       return e_webdav_collection_backend_discover_sync (E_WEBDAV_COLLECTION_BACKEND (backend),
+               e_source_goa_get_calendar_url (goa_extension),
+               e_source_goa_get_contacts_url (goa_extension),
+               credentials, out_certificate_pem, out_certificate_errors, cancellable, error);
 }
 
 static void
 e_owncloud_backend_class_init (EOwncloudBackendClass *class)
 {
        EBackendClass *backend_class;
-       ECollectionBackendClass *collection_backend_class;
 
        backend_class = E_BACKEND_CLASS (class);
        backend_class->authenticate_sync = owncloud_backend_authenticate_sync;
-
-       collection_backend_class = E_COLLECTION_BACKEND_CLASS (class);
-       collection_backend_class->populate = owncloud_backend_populate;
 }
 
 static void
diff --git a/src/modules/yahoo-backend/module-yahoo-backend.c 
b/src/modules/yahoo-backend/module-yahoo-backend.c
index 0a34236..2d98870 100644
--- a/src/modules/yahoo-backend/module-yahoo-backend.c
+++ b/src/modules/yahoo-backend/module-yahoo-backend.c
@@ -43,15 +43,9 @@
 #define YAHOO_SMTP_PORT                        465
 #define YAHOO_SMTP_SECURITY_METHOD     METHOD (SSL_ON_ALTERNATE_PORT)
 
-/* Calendar Configuration Details */
-#define YAHOO_CALENDAR_BACKEND_NAME    "caldav"
-#define YAHOO_CALENDAR_HOST            "caldav.calendar.yahoo.com"
-#define YAHOO_CALENDAR_CALDAV_PATH     "/dav/%s/Calendar/%s"
-#define YAHOO_CALENDAR_RESOURCE_ID     "Calendar"
+/* WebDAV Configuration Details */
+#define YAHOO_WEBDAV_URL               "https://caldav.calendar.yahoo.com/dav/";
 
-/* Tasks Configuration Details
- * (mostly the same as calendar) */
-#define YAHOO_TASKS_RESOURCE_ID                "Tasks"
 
 typedef struct _EYahooBackend EYahooBackend;
 typedef struct _EYahooBackendClass EYahooBackendClass;
@@ -60,12 +54,12 @@ typedef struct _EYahooBackendFactory EYahooBackendFactory;
 typedef struct _EYahooBackendFactoryClass EYahooBackendFactoryClass;
 
 struct _EYahooBackend {
-       ECollectionBackend parent;
+       EWebDAVCollectionBackend parent;
        GWeakRef mail_identity_source;
 };
 
 struct _EYahooBackendClass {
-       ECollectionBackendClass parent_class;
+       EWebDAVCollectionBackendClass parent_class;
 };
 
 struct _EYahooBackendFactory {
@@ -87,191 +81,26 @@ GType e_yahoo_backend_factory_get_type (void);
 G_DEFINE_DYNAMIC_TYPE (
        EYahooBackend,
        e_yahoo_backend,
-       E_TYPE_COLLECTION_BACKEND)
+       E_TYPE_WEBDAV_COLLECTION_BACKEND)
 
 G_DEFINE_DYNAMIC_TYPE (
        EYahooBackendFactory,
        e_yahoo_backend_factory,
        E_TYPE_COLLECTION_BACKEND_FACTORY)
 
-static void
-yahoo_backend_config_calendar_child (ECollectionBackend *backend,
-                                     ESource *source)
+static ESourceAuthenticationResult
+yahoo_backend_authenticate_sync (EBackend *backend,
+                                const ENamedParameters *credentials,
+                                gchar **out_certificate_pem,
+                                GTlsCertificateFlags *out_certificate_errors,
+                                GCancellable *cancellable,
+                                GError **error)
 {
-       EYahooBackend *yahoo_backend;
-       ESource *collection_source;
-       ESource *mail_identity_source;
-       ESourceExtension *extension;
-       ESourceCollection *collection_extension;
-       const gchar *extension_name;
-       const gchar *identity;
-       gchar *display_name = NULL;
-
-       /* FIXME As a future enhancement, we should query Yahoo!
-        *       for a list of user calendars and add them to the
-        *       collection with matching display names and colors. */
-
-       yahoo_backend = E_YAHOO_BACKEND (backend);
-
-       collection_source = e_backend_get_source (E_BACKEND (backend));
-
-       collection_extension = e_source_get_extension (
-               collection_source, E_SOURCE_EXTENSION_COLLECTION);
-
-       identity = e_source_collection_get_identity (collection_extension);
-
-       /* XXX Assume the calendar's display name can be derived from
-        *     the user's mail identity.  As mentioned above, we should
-        *     really just query Yahoo! for a list of user calendars. */
-       mail_identity_source =
-               g_weak_ref_get (&yahoo_backend->mail_identity_source);
-       if (mail_identity_source != NULL) {
-               extension = e_source_get_extension (
-                       mail_identity_source,
-                       E_SOURCE_EXTENSION_MAIL_IDENTITY);
-               display_name = e_source_mail_identity_dup_name (
-                       E_SOURCE_MAIL_IDENTITY (extension));
-               if (display_name != NULL)
-                       g_strdelimit (display_name, " ", '_');
-               g_object_unref (mail_identity_source);
-       }
-
-       extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
-       extension = e_source_get_extension (source, extension_name);
-
-       e_source_authentication_set_host (
-               E_SOURCE_AUTHENTICATION (extension),
-               YAHOO_CALENDAR_HOST);
-
-       e_binding_bind_property (
-               collection_extension, "identity",
-               extension, "user",
-               G_BINDING_SYNC_CREATE);
+       g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), E_SOURCE_AUTHENTICATION_ERROR);
 
-       extension_name = E_SOURCE_EXTENSION_SECURITY;
-       extension = e_source_get_extension (source, extension_name);
-
-       e_source_security_set_secure (
-               E_SOURCE_SECURITY (extension), TRUE);
-
-       extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
-       extension = e_source_get_extension (source, extension_name);
-
-       e_source_webdav_set_display_name (
-               E_SOURCE_WEBDAV (extension), display_name);
-
-       if (identity != NULL && display_name != NULL) {
-               gchar *resource_path;
-
-               resource_path = g_strdup_printf (
-                       YAHOO_CALENDAR_CALDAV_PATH, identity, display_name);
-               e_source_webdav_set_resource_path (
-                       E_SOURCE_WEBDAV (extension), resource_path);
-               g_free (resource_path);
-       }
-
-       g_free (display_name);
-}
-
-static void
-yahoo_backend_add_calendar (ECollectionBackend *backend)
-{
-       ESource *source;
-       ESourceBackend *extension;
-       ESourceRegistryServer *server;
-       const gchar *backend_name;
-       const gchar *extension_name;
-       const gchar *resource_id;
-
-       /* XXX We could just stick a [Calendar] and [Task List] extension
-        *     into the same ESource since all other settings are exactly
-        *     the same.  But it might be confusing if tweaking a setting
-        *     in your Yahoo! Calendar also gets applied to your Yahoo!
-        *     Task List in Evolution. */
-
-       backend_name = YAHOO_CALENDAR_BACKEND_NAME;
-
-       server = e_collection_backend_ref_server (backend);
-
-       /* Add Yahoo! Calendar */
-
-       resource_id = YAHOO_CALENDAR_RESOURCE_ID;
-       source = e_collection_backend_new_child (backend, resource_id);
-       e_source_set_display_name (source, _("Calendar"));
-
-       extension_name = E_SOURCE_EXTENSION_CALENDAR;
-       extension = e_source_get_extension (source, extension_name);
-       e_source_backend_set_backend_name (extension, backend_name);
-
-       extension_name = E_SOURCE_EXTENSION_ALARMS;
-       extension = e_source_get_extension (source, extension_name);
-       if (!e_source_alarms_get_last_notified (E_SOURCE_ALARMS (extension))) {
-               GTimeVal today_tv;
-               gchar *today;
-
-               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);
-               g_free (today);
-       }
-
-       yahoo_backend_config_calendar_child (backend, source);
-       e_source_registry_server_add_source (server, source);
-
-       g_object_unref (source);
-
-       /* Add Yahoo! Tasks */
-
-       resource_id = YAHOO_TASKS_RESOURCE_ID;
-       source = e_collection_backend_new_child (backend, resource_id);
-       e_source_set_display_name (source, _("Tasks"));
-
-       extension_name = E_SOURCE_EXTENSION_TASK_LIST;
-       extension = e_source_get_extension (source, extension_name);
-       e_source_backend_set_backend_name (extension, backend_name);
-
-       yahoo_backend_config_calendar_child (backend, source);
-       e_source_registry_server_add_source (server, source);
-
-       g_object_unref (source);
-
-       g_object_unref (server);
-}
-
-static void
-yahoo_backend_populate (ECollectionBackend *backend)
-{
-       GList *list;
-
-       /* Chain up to parent's populate() method. */
-       E_COLLECTION_BACKEND_CLASS (e_yahoo_backend_parent_class)->
-               populate (backend);
-
-       /* Chain up first so we pick up the mail identity source. */
-       list = e_collection_backend_list_calendar_sources (backend);
-       if (list == NULL)
-               yahoo_backend_add_calendar (backend);
-       g_list_free_full (list, (GDestroyNotify) g_object_unref);
-}
-
-static gchar *
-yahoo_backend_dup_resource_id (ECollectionBackend *backend,
-                               ESource *child_source)
-{
-       const gchar *extension_name;
-
-       /* XXX This is trival for now since we only
-        *     add one calendar and one task list. */
-
-       extension_name = E_SOURCE_EXTENSION_CALENDAR;
-       if (e_source_has_extension (child_source, extension_name))
-               return g_strdup (YAHOO_CALENDAR_RESOURCE_ID);
-
-       extension_name = E_SOURCE_EXTENSION_TASK_LIST;
-       if (e_source_has_extension (child_source, extension_name))
-               return g_strdup (YAHOO_TASKS_RESOURCE_ID);
-
-       return NULL;
+       return e_webdav_collection_backend_discover_sync (E_WEBDAV_COLLECTION_BACKEND (backend),
+               YAHOO_WEBDAV_URL, YAHOO_WEBDAV_URL, credentials,
+               out_certificate_pem, out_certificate_errors, cancellable, error);
 }
 
 static void
@@ -351,15 +180,17 @@ static void
 e_yahoo_backend_class_init (EYahooBackendClass *class)
 {
        GObjectClass *object_class;
-       ECollectionBackendClass *backend_class;
+       EBackendClass *backend_class;
+       ECollectionBackendClass *collection_backend_class;
 
        object_class = G_OBJECT_CLASS (class);
        object_class->finalize = yahoo_backend_finalize;
 
-       backend_class = E_COLLECTION_BACKEND_CLASS (class);
-       backend_class->populate = yahoo_backend_populate;
-       backend_class->dup_resource_id = yahoo_backend_dup_resource_id;
-       backend_class->child_added = yahoo_backend_child_added;
+       backend_class = E_BACKEND_CLASS (class);
+       backend_class->authenticate_sync = yahoo_backend_authenticate_sync;
+
+       collection_backend_class = E_COLLECTION_BACKEND_CLASS (class);
+       collection_backend_class->child_added = yahoo_backend_child_added;
 }
 
 static void


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]