[evolution-data-server/wip/offline-cache] Make EBookBackendWebDAV derive from EBookMetaBackend



commit 3795e05a2461e2acb5e9744b394414a67c5c03b2
Author: Milan Crha <mcrha redhat com>
Date:   Fri May 12 12:46:46 2017 +0200

    Make EBookBackendWebDAV derive from EBookMetaBackend

 .../backends/webdav/e-book-backend-webdav.c        | 2390 +++++++-------------
 .../backends/webdav/e-book-backend-webdav.h        |   23 +-
 src/addressbook/libedata-book/e-book-cache.c       |   58 +-
 src/addressbook/libedata-book/e-book-cache.h       |   13 +-
 .../libedata-book/e-book-meta-backend.c            |   71 +-
 .../backends/caldav/e-cal-backend-caldav.c         |   82 +-
 src/calendar/libedata-cal/e-cal-meta-backend.c     |   72 +-
 7 files changed, 1014 insertions(+), 1695 deletions(-)
---
diff --git a/src/addressbook/backends/webdav/e-book-backend-webdav.c 
b/src/addressbook/backends/webdav/e-book-backend-webdav.c
index 7deda66..a4617d9 100644
--- a/src/addressbook/backends/webdav/e-book-backend-webdav.c
+++ b/src/addressbook/backends/webdav/e-book-backend-webdav.c
@@ -1,6 +1,6 @@
-/* e-book-backend-webdav.c - Webdav contact backend.
- *
+/*
  * Copyright (C) 2008 Matthias Braun <matze braunis de>
+ * 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
@@ -17,12 +17,6 @@
  * Authors: Matthias Braun <matze braunis de>
  */
 
-/*
- * Implementation notes:
- *   We use the DavResource URIs as UID in the evolution contact
- *   ETags are saved in the WEBDAV_CONTACT_ETAG field so we know which cached contacts
- *   are outdated.
- */
 #include "evolution-data-server-config.h"
 
 #include <stdio.h>
@@ -30,84 +24,36 @@
 #include <string.h>
 #include <glib/gi18n-lib.h>
 
+#include "libedataserver/libedataserver.h"
+
 #include "e-book-backend-webdav.h"
 
-#include <libsoup/soup.h>
-
-#include <libxml/parser.h>
-#include <libxml/xmlreader.h>
-#include <libxml/xpath.h>
-#include <libxml/xpathInternals.h>
-
-#define E_BOOK_BACKEND_WEBDAV_GET_PRIVATE(obj) \
-       (G_TYPE_INSTANCE_GET_PRIVATE \
-       ((obj), E_TYPE_BOOK_BACKEND_WEBDAV, EBookBackendWebdavPrivate))
-
-#define USERAGENT             "Evolution/" VERSION
-#define WEBDAV_CLOSURE_NAME   "EBookBackendWebdav.BookView::closure"
-#define WEBDAV_CTAG_KEY "WEBDAV_CTAG"
-#define WEBDAV_CACHE_VERSION_KEY "WEBDAV_CACHE_VERSION"
-#define WEBDAV_CACHE_VERSION "2"
-#define WEBDAV_CONTACT_ETAG "X-EVOLUTION-WEBDAV-ETAG"
-#define WEBDAV_CONTACT_HREF "X-EVOLUTION-WEBDAV-HREF"
-
-G_DEFINE_TYPE (EBookBackendWebdav, e_book_backend_webdav, E_TYPE_BOOK_BACKEND)
-
-struct _EBookBackendWebdavPrivate {
-       gboolean           marked_for_offline;
-       SoupSession       *session;
-       gchar             *uri;
-       gchar              *username;
-       gchar              *password;
-       gboolean supports_getctag;
-       gint64 last_server_test_us; /* real-time, in microseconds, when the last server test
-                                       for changes had been made, when the server doesn't support ctag */
-
-       GMutex cache_lock;
-       GMutex update_lock;
-       EBookBackendCache *cache;
-};
+#define E_WEBDAV_MAX_MULTIGET_AMOUNT 100 /* what's the maximum count of items to fetch within a multiget 
request */
 
-typedef struct {
-       EBookBackendWebdav *webdav;
-       GThread            *thread;
-       EFlag              *running;
-} WebdavBackendSearchClosure;
+#define E_WEBDAV_X_ETAG "X-EVOLUTION-WEBDAV-ETAG"
 
-static void
-webdav_debug_setup (SoupSession *session)
-{
-       const gchar *debug_str;
-       SoupLogger *logger;
-       SoupLoggerLogLevel level;
-
-       g_return_if_fail (session != NULL);
-
-       debug_str = g_getenv ("WEBDAV_DEBUG");
-       if (!debug_str || !*debug_str)
-               return;
-
-       if (g_ascii_strcasecmp (debug_str, "all") == 0)
-               level = SOUP_LOGGER_LOG_BODY;
-       else if (g_ascii_strcasecmp (debug_str, "headers") == 0)
-               level = SOUP_LOGGER_LOG_HEADERS;
-       else
-               level = SOUP_LOGGER_LOG_MINIMAL;
-
-       logger = soup_logger_new (level, 100 * 1024 * 1024);
-       soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
-       g_object_unref (logger);
-}
+#define EDB_ERROR(_code) e_data_book_create_error (_code, NULL)
+#define EDB_ERROR_EX(_code, _msg) e_data_book_create_error (_code, _msg)
+
+struct _EBookBackendWebDAVPrivate {
+       /* The main WebDAV session  */
+       EWebDAVSession *webdav;
+
+       /* support for 'getctag' extension */
+       gboolean ctag_supported;
+};
+
+G_DEFINE_TYPE (EBookBackendWebDAV, e_book_backend_webdav, E_TYPE_BOOK_META_BACKEND)
 
 static void
-webdav_contact_set_etag (EContact *contact,
-                         const gchar *etag)
+ebb_webdav_set_contact_etag (EContact *contact,
+                            const gchar *etag)
 {
        EVCardAttribute *attr;
 
        g_return_if_fail (E_IS_CONTACT (contact));
 
-       attr = e_vcard_get_attribute (E_VCARD (contact), WEBDAV_CONTACT_ETAG);
+       attr = e_vcard_get_attribute (E_VCARD (contact), E_WEBDAV_X_ETAG);
 
        if (attr) {
                e_vcard_attribute_remove_values (attr);
@@ -119,20 +65,20 @@ webdav_contact_set_etag (EContact *contact,
        } else if (etag) {
                e_vcard_append_attribute_with_value (
                        E_VCARD (contact),
-                       e_vcard_attribute_new (NULL, WEBDAV_CONTACT_ETAG),
+                       e_vcard_attribute_new (NULL, E_WEBDAV_X_ETAG),
                        etag);
        }
 }
 
 static gchar *
-webdav_contact_get_etag (EContact *contact)
+ebb_webdav_dup_contact_etag (EContact *contact)
 {
        EVCardAttribute *attr;
        GList *v = NULL;
 
        g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
 
-       attr = e_vcard_get_attribute (E_VCARD (contact), WEBDAV_CONTACT_ETAG);
+       attr = e_vcard_get_attribute (E_VCARD (contact), E_WEBDAV_X_ETAG);
 
        if (attr)
                v = e_vcard_attribute_get_values (attr);
@@ -140,1850 +86,1052 @@ webdav_contact_get_etag (EContact *contact)
        return ((v && v->data) ? g_strstrip (g_strdup (v->data)) : NULL);
 }
 
-static void
-webdav_contact_set_href (EContact *contact,
-                         const gchar *href)
-{
-       EVCardAttribute *attr;
-
-       g_return_if_fail (E_IS_CONTACT (contact));
-
-       attr = e_vcard_get_attribute (E_VCARD (contact), WEBDAV_CONTACT_HREF);
-
-       if (attr) {
-               e_vcard_attribute_remove_values (attr);
-               if (href) {
-                       e_vcard_attribute_add_value (attr, href);
-               } else {
-                       e_vcard_remove_attribute (E_VCARD (contact), attr);
-               }
-       } else if (href) {
-               e_vcard_append_attribute_with_value (
-                       E_VCARD (contact),
-                       e_vcard_attribute_new (NULL, WEBDAV_CONTACT_HREF),
-                       href);
-       }
-}
-
-static gchar *
-webdav_contact_get_href (EContact *contact)
+static gboolean
+ebb_webdav_connect_sync (EBookMetaBackend *meta_backend,
+                        const ENamedParameters *credentials,
+                        ESourceAuthenticationResult *out_auth_result,
+                        gchar **out_certificate_pem,
+                        GTlsCertificateFlags *out_certificate_errors,
+                        GCancellable *cancellable,
+                        GError **error)
 {
-       EVCardAttribute *attr;
-       GList *v = NULL;
+       EBookBackendWebDAV *bbdav;
+       GHashTable *capabilities = NULL, *allows = NULL;
+       ESource *source;
+       gboolean success;
+       GError *local_error = NULL;
 
-       g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_WEBDAV (meta_backend), FALSE);
+       g_return_val_if_fail (out_auth_result != NULL, FALSE);
 
-       attr = e_vcard_get_attribute (E_VCARD (contact), WEBDAV_CONTACT_HREF);
+       bbdav = E_BOOK_BACKEND_WEBDAV (meta_backend);
 
-       if (attr)
-               v = e_vcard_attribute_get_values (attr);
+       if (bbdav->priv->webdav)
+               return TRUE;
 
-       return ((v && v->data) ? g_strstrip (g_strdup (v->data)) : NULL);
-}
+       source = e_backend_get_source (E_BACKEND (meta_backend));
 
-static void
-closure_destroy (WebdavBackendSearchClosure *closure)
-{
-       e_flag_free (closure->running);
-       if (closure->thread)
-               g_thread_unref (closure->thread);
-       g_free (closure);
-}
+       bbdav->priv->webdav = e_webdav_session_new (source);
 
-static WebdavBackendSearchClosure *
-init_closure (EDataBookView *book_view,
-              EBookBackendWebdav *webdav)
-{
-       WebdavBackendSearchClosure *closure = g_new (WebdavBackendSearchClosure, 1);
+       e_soup_session_setup_logging (E_SOUP_SESSION (bbdav->priv->webdav), g_getenv ("WEBDAV_DEBUG"));
 
-       closure->webdav = webdav;
-       closure->thread = NULL;
-       closure->running = e_flag_new ();
+       e_binding_bind_property (
+               bbdav, "proxy-resolver",
+               bbdav->priv->webdav, "proxy-resolver",
+               G_BINDING_SYNC_CREATE);
 
-       g_object_set_data_full (
-               G_OBJECT (book_view), WEBDAV_CLOSURE_NAME,
-               closure, (GDestroyNotify) closure_destroy);
+       /* Thinks the 'getctag' extension is available the first time, but unset it when realizes it isn't. */
+       bbdav->priv->ctag_supported = TRUE;
 
-       return closure;
-}
+       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
 
-static WebdavBackendSearchClosure *
-get_closure (EDataBookView *book_view)
-{
-       return g_object_get_data (G_OBJECT (book_view), WEBDAV_CLOSURE_NAME);
-}
+       e_soup_session_set_credentials (E_SOUP_SESSION (bbdav->priv->webdav), credentials);
 
-static guint
-send_and_handle_ssl (EBookBackendWebdav *webdav,
-                     SoupMessage *message,
-                     GCancellable *cancellable)
-{
-       guint status_code;
+       success = e_webdav_session_options_sync (bbdav->priv->webdav, NULL,
+               &capabilities, &allows, cancellable, &local_error);
 
-       e_soup_ssl_trust_connect (message, e_backend_get_source (E_BACKEND (webdav)));
+       /* iCloud and Google servers can return "404 Not Found" when issued OPTIONS on the addressbook 
collection */
+       if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_NOT_FOUND)) {
+               ESourceWebdav *webdav_extension;
+               SoupURI *soup_uri;
 
-       status_code = soup_session_send_message (webdav->priv->session, message);
+               webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+               soup_uri = e_source_webdav_dup_soup_uri (webdav_extension);
+               if (soup_uri) {
+                       if (soup_uri->host && soup_uri->path && *soup_uri->path &&
+                           e_util_utf8_strstrcase (soup_uri->host, ".icloud.com")) {
+                               /* Try parent directory */
+                               gchar *path;
+                               gint len = strlen (soup_uri->path);
 
-       if (SOUP_STATUS_IS_SUCCESSFUL (status_code))
-               e_backend_ensure_source_status_connected (E_BACKEND (webdav));
+                               if (soup_uri->path[len - 1] == '/')
+                                       soup_uri->path[len - 1] = '\0';
 
-       return status_code;
-}
+                               path = g_path_get_dirname (soup_uri->path);
+                               if (path && g_str_has_prefix (soup_uri->path, path)) {
+                                       gchar *uri;
 
-static EContact *
-download_contact (EBookBackendWebdav *webdav,
-                  const gchar *uri,
-                  GCancellable *cancellable)
-{
-       SoupMessage *message;
-       const gchar  *etag;
-       EContact    *contact;
-       guint        status;
-
-       message = soup_message_new (SOUP_METHOD_GET, uri);
-       soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
-       soup_message_headers_append (message->request_headers, "Connection", "close");
-
-       status = send_and_handle_ssl (webdav, message, cancellable);
-       if (status != 200) {
-               g_warning ("Couldn't load '%s' (http status %d)", uri, status);
-               g_object_unref (message);
-               return NULL;
-       }
+                                       soup_uri_set_path (soup_uri, path);
 
-       if (message->response_body == NULL) {
-               g_message ("no response body after requesting '%s'", uri);
-               g_object_unref (message);
-               return NULL;
-       }
+                                       uri = soup_uri_to_string (soup_uri, FALSE);
+                                       if (uri) {
+                                               g_clear_error (&local_error);
 
-       if (message->response_body->length <= 11 || 0 != g_ascii_strncasecmp ((const gchar *) 
message->response_body->data, "BEGIN:VCARD", 11)) {
-               g_object_unref (message);
-               return NULL;
-       }
+                                               success = e_webdav_session_options_sync (bbdav->priv->webdav, 
uri,
+                                                       &capabilities, &allows, cancellable, &local_error);
+                                       }
 
-       etag = soup_message_headers_get_list (message->response_headers, "ETag");
+                                       g_free (uri);
+                               }
 
-       /* we use our URI as UID */
-       contact = e_contact_new_from_vcard (message->response_body->data);
-       if (contact == NULL) {
-               g_warning ("Invalid vcard at '%s'", uri);
-               g_object_unref (message);
-               return NULL;
-       }
+                               g_free (path);
+                       } else if (soup_uri->host && e_util_utf8_strstrcase (soup_uri->host, 
".googleusercontent.com")) {
+                               g_clear_error (&local_error);
+                               success = TRUE;
 
-       webdav_contact_set_href (contact, uri);
-       /* the etag is remembered in the WEBDAV_CONTACT_ETAG field */
-       if (etag != NULL) {
-               webdav_contact_set_etag (contact, etag);
-       }
+                               /* Google's WebDAV doesn't like OPTIONS, hard-code it */
+                               capabilities = g_hash_table_new_full (camel_strcase_hash, 
camel_strcase_equal, g_free, NULL);
+                               g_hash_table_insert (capabilities, g_strdup 
(E_WEBDAV_CAPABILITY_ADDRESSBOOK), GINT_TO_POINTER (1));
 
-       g_object_unref (message);
-       return contact;
-}
+                               allows = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, 
g_free, NULL);
+                               g_hash_table_insert (allows, g_strdup (SOUP_METHOD_PUT), GINT_TO_POINTER (1));
+                       }
 
-static guint
-upload_contact (EBookBackendWebdav *webdav,
-               const gchar *uri,
-                EContact *contact,
-                gchar **reason,
-                GCancellable *cancellable)
-{
-       ESource     *source;
-       ESourceWebdav *webdav_extension;
-       SoupMessage *message;
-       gchar       *etag;
-       const gchar  *new_etag, *redir_uri;
-       gchar        *request;
-       guint        status;
-       gboolean     avoid_ifmatch;
-       const gchar *extension_name;
-
-       g_return_val_if_fail (uri != NULL, SOUP_STATUS_BAD_REQUEST);
-
-       source = e_backend_get_source (E_BACKEND (webdav));
-
-       extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
-       webdav_extension = e_source_get_extension (source, extension_name);
-
-       message = soup_message_new (SOUP_METHOD_PUT, uri);
-       soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
-       soup_message_headers_append (message->request_headers, "Connection", "close");
-
-       avoid_ifmatch = e_source_webdav_get_avoid_ifmatch (webdav_extension);
-
-       /* some servers (like apache < 2.2.8) don't handle If-Match, correctly so
-        * we can leave it out */
-       if (!avoid_ifmatch) {
-               /* only override if etag is still the same on the server */
-               etag = webdav_contact_get_etag (contact);
-               if (etag == NULL) {
-                       soup_message_headers_append (
-                               message->request_headers,
-                               "If-None-Match", "*");
-               } else if (etag[0] == 'W' && etag[1] == '/') {
-                       g_warning ("we only have a weak ETag, don't use If-Match synchronisation");
-               } else {
-                       soup_message_headers_append (
-                               message->request_headers,
-                               "If-Match", etag);
+                       soup_uri_free (soup_uri);
                }
-
-               g_free (etag);
        }
 
-       /* Remove the stored ETag, before saving to the server */
-       webdav_contact_set_etag (contact, NULL);
-
-       request = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
-       soup_message_set_request (
-               message, "text/vcard", SOUP_MEMORY_TEMPORARY,
-               request, strlen (request));
+       if (success) {
+               ESourceWebdav *webdav_extension;
+               EBookCache *book_cache;
+               SoupURI *soup_uri;
+               gboolean is_writable;
+               gboolean addressbook;
 
-       status = send_and_handle_ssl (webdav, message, cancellable);
-       new_etag = soup_message_headers_get_list (message->response_headers, "ETag");
+               webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+               soup_uri = e_source_webdav_dup_soup_uri (webdav_extension);
+               book_cache = e_book_meta_backend_ref_cache (meta_backend);
 
-       redir_uri = soup_message_headers_get_list (message->response_headers, "Location");
+               /* The POST added for FastMail servers, which doesn't advertise PUT on collections. */
+               is_writable = allows && (
+                       g_hash_table_contains (allows, SOUP_METHOD_PUT) ||
+                       g_hash_table_contains (allows, SOUP_METHOD_POST) ||
+                       g_hash_table_contains (allows, SOUP_METHOD_DELETE));
 
-       /* set UID and WEBDAV_CONTACT_ETAG fields */
-       webdav_contact_set_etag (contact, new_etag);
-       if (redir_uri && *redir_uri) {
-               if (!strstr (redir_uri, "://")) {
-                       /* it's a relative URI */
-                       SoupURI *suri = soup_uri_new (uri);
-                       gchar *full_uri;
+               addressbook = capabilities && g_hash_table_contains (capabilities, 
E_WEBDAV_CAPABILITY_ADDRESSBOOK);
 
-                       if (*redir_uri != '/' && *redir_uri != '\\') {
-                               gchar *slashed_path = g_strconcat ("/", redir_uri, NULL);
+               if (addressbook) {
+                       e_book_backend_set_writable (E_BOOK_BACKEND (bbdav), is_writable);
 
-                               soup_uri_set_path (suri, slashed_path);
-                               g_free (slashed_path);
-                       } else {
-                               soup_uri_set_path (suri, redir_uri);
-                       }
-                       full_uri = soup_uri_to_string (suri, FALSE);
-
-                       webdav_contact_set_href (contact, full_uri);
-
-                       g_free (full_uri);
-                       soup_uri_free (suri);
+                       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
                } else {
-                       webdav_contact_set_href (contact, redir_uri);
-               }
-       } else {
-               webdav_contact_set_href (contact, uri);
-       }
+                       gchar *uri;
 
-       if (reason != NULL) {
-               const gchar *phrase;
+                       uri = soup_uri_to_string (soup_uri, FALSE);
 
-               phrase = message->reason_phrase;
-               if (phrase == NULL)
-                       phrase = soup_status_get_phrase (message->status_code);
-               if (phrase == NULL)
-                       phrase = _("Unknown error");
-
-               *reason = g_strdup (phrase);
-       }
+                       success = FALSE;
+                       g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+                               _("Given URL ā€œ%sā€ doesn't reference WebDAV address book"), uri);
 
-       g_object_unref (message);
-       g_free (request);
+                       g_free (uri);
 
-       return status;
-}
+                       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+               }
 
-static gboolean
-webdav_handle_auth_request (EBookBackendWebdav *webdav,
-                            GError **error)
-{
-       EBookBackendWebdavPrivate *priv = webdav->priv;
-
-       if (priv->username != NULL) {
-               g_free (priv->username);
-               priv->username = NULL;
-               g_free (priv->password);
-               priv->password = NULL;
-
-               g_set_error_literal (
-                       error, E_CLIENT_ERROR,
-                       E_CLIENT_ERROR_AUTHENTICATION_FAILED,
-                       e_client_error_to_string (
-                       E_CLIENT_ERROR_AUTHENTICATION_FAILED));
-       } else {
-               g_set_error_literal (
-                       error, E_CLIENT_ERROR,
-                       E_CLIENT_ERROR_AUTHENTICATION_REQUIRED,
-                       e_client_error_to_string (
-                       E_CLIENT_ERROR_AUTHENTICATION_REQUIRED));
+               g_clear_object (&book_cache);
+               soup_uri_free (soup_uri);
        }
 
-       return FALSE;
-}
-
-static guint
-delete_contact (EBookBackendWebdav *webdav,
-                const gchar *uri,
-                GCancellable *cancellable)
-{
-       SoupMessage *message;
-       guint        status;
-
-       message = soup_message_new (SOUP_METHOD_DELETE, uri);
-       soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
-       soup_message_headers_append (message->request_headers, "Connection", "close");
-
-       status = send_and_handle_ssl (webdav, message, cancellable);
-       g_object_unref (message);
-
-       return status;
-}
-
-typedef struct parser_strings_t {
-       const xmlChar *multistatus;
-       const xmlChar *dav;
-       const xmlChar *href;
-       const xmlChar *response;
-       const xmlChar *propstat;
-       const xmlChar *prop;
-       const xmlChar *getetag;
-} parser_strings_t;
-
-typedef struct response_element_t response_element_t;
-struct response_element_t {
-       xmlChar            *href;
-       xmlChar            *etag;
-       response_element_t *next;
-};
-
-static response_element_t *
-parse_response_tag (const parser_strings_t *strings,
-                    xmlTextReaderPtr reader)
-{
-       xmlChar            *href = NULL;
-       xmlChar            *etag = NULL;
-       gint                 depth = xmlTextReaderDepth (reader);
-       response_element_t *element;
-
-       while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > depth) {
-               const xmlChar *tag_name;
-               if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
-                       continue;
+       if (success) {
+               gchar *ctag = NULL;
 
-               if (xmlTextReaderConstNamespaceUri (reader) != strings->dav)
-                       continue;
+               /* Some servers, notably Google, allow OPTIONS when not
+                  authorized (aka without credentials), thus try something
+                  more aggressive, just in case.
 
-               tag_name = xmlTextReaderConstLocalName (reader);
-               if (tag_name == strings->href) {
-                       if (href != NULL) {
-                               /* multiple href elements?!? */
-                               xmlFree (href);
-                       }
-                       href = xmlTextReaderReadString (reader);
-               } else if (tag_name == strings->propstat) {
-                       /* find <propstat><prop><getetag> hierarchy */
-                       gint depth2 = xmlTextReaderDepth (reader);
-                       while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > depth2) {
-                               gint depth3;
-                               if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
-                                       continue;
-
-                               if (xmlTextReaderConstNamespaceUri (reader) != strings->dav
-                                               || xmlTextReaderConstLocalName (reader) != strings->prop)
-                                       continue;
-
-                               depth3 = xmlTextReaderDepth (reader);
-                               while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > 
depth3) {
-                                       if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
-                                               continue;
-
-                                       if (xmlTextReaderConstNamespaceUri (reader) != strings->dav
-                                                       || xmlTextReaderConstLocalName (reader)
-                                                       != strings->getetag)
-                                               continue;
-
-                                       if (etag != NULL) {
-                                               /* multiple etag elements?!? */
-                                               xmlFree (etag);
-                                       }
-                                       etag = xmlTextReaderReadString (reader);
-                               }
-                       }
+                  The 'getctag' extension is not required, thuch check
+                  for unauthorized error only. */
+               if (!e_webdav_session_getctag_sync (bbdav->priv->webdav, NULL, &ctag, cancellable, 
&local_error) &&
+                   g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
+                       success = FALSE;
+               } else {
+                       g_clear_error (&local_error);
                }
-       }
-
-       if (href == NULL) {
-               g_warning ("webdav returned response element without href");
-               return NULL;
-       }
-
-       /* append element to list */
-       element = g_malloc (sizeof (element[0]));
-       element->href = href;
-       element->etag = etag;
-       return element;
-}
-
-static response_element_t *
-parse_propfind_response (xmlTextReaderPtr reader)
-{
-       parser_strings_t    strings;
-       response_element_t *elements;
-
-       /* get internalized versions of some strings to avoid strcmp while
-        * parsing */
-       strings.multistatus = xmlTextReaderConstString (reader, BAD_CAST "multistatus");
-       strings.dav = xmlTextReaderConstString (reader, BAD_CAST "DAV:");
-       strings.href = xmlTextReaderConstString (reader, BAD_CAST "href");
-       strings.response = xmlTextReaderConstString (reader, BAD_CAST "response");
-       strings.propstat = xmlTextReaderConstString (reader, BAD_CAST "propstat");
-       strings.prop = xmlTextReaderConstString (reader, BAD_CAST "prop");
-       strings.getetag = xmlTextReaderConstString (reader, BAD_CAST "getetag");
-
-       while (xmlTextReaderRead (reader) == 1 && xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT) {
-       }
 
-       if (xmlTextReaderConstLocalName (reader) != strings.multistatus
-                       || xmlTextReaderConstNamespaceUri (reader) != strings.dav) {
-               g_warning ("webdav PROPFIND result is not <DAV:multistatus>");
-               return NULL;
+               g_free (ctag);
        }
 
-       elements = NULL;
-
-       /* parse all DAV:response tags */
-       while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > 0) {
-               response_element_t *element;
-
-               if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
-                       continue;
-
-               if (xmlTextReaderConstLocalName (reader) != strings.response
-                               || xmlTextReaderConstNamespaceUri (reader) != strings.dav)
-                       continue;
-
-               element = parse_response_tag (&strings, reader);
-               if (element == NULL)
-                       continue;
-
-               element->next = elements;
-               elements = element;
-       }
+       if (success) {
+               *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED;
+       } else {
+               gboolean credentials_empty;
+               gboolean is_ssl_error;
+
+               credentials_empty = !credentials || !e_named_parameters_count (credentials);
+               is_ssl_error = g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED);
+
+               *out_auth_result = E_SOURCE_AUTHENTICATION_ERROR;
+
+               /* because evolution knows only G_IO_ERROR_CANCELLED */
+               if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED)) {
+                       local_error->domain = G_IO_ERROR;
+                       local_error->code = G_IO_ERROR_CANCELLED;
+               } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_FORBIDDEN) && 
credentials_empty) {
+                       *out_auth_result = E_SOURCE_AUTHENTICATION_REQUIRED;
+               } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
+                       if (credentials_empty)
+                               *out_auth_result = E_SOURCE_AUTHENTICATION_REQUIRED;
+                       else
+                               *out_auth_result = E_SOURCE_AUTHENTICATION_REJECTED;
+               } else if (local_error) {
+                       g_propagate_error (error, local_error);
+                       local_error = NULL;
+               } else {
+                       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                               _("Unknown error"));
+               }
 
-       return elements;
-}
+               if (is_ssl_error) {
+                       *out_auth_result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
 
-static SoupMessage *
-send_propfind (EBookBackendWebdav *webdav,
-              GCancellable *cancellable,
-              GError **error)
-{
-       SoupMessage               *message;
-       EBookBackendWebdavPrivate *priv = webdav->priv;
-       const gchar               *request =
-               "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
-               "<propfind xmlns=\"DAV:\"><prop><getetag/></prop></propfind>";
-
-       message = soup_message_new (SOUP_METHOD_PROPFIND, priv->uri);
-       if (!message) {
-               g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, _("Malformed URI: %s"), 
priv->uri);
-               return NULL;
+                       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_SSL_FAILED);
+                       e_soup_session_get_ssl_error_details (E_SOUP_SESSION (bbdav->priv->webdav), 
out_certificate_pem, out_certificate_errors);
+               } else {
+                       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+               }
        }
 
-       soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
-       soup_message_headers_append (message->request_headers, "Connection", "close");
-       soup_message_headers_append (message->request_headers, "Depth", "1");
-       soup_message_set_request (
-               message, "text/xml", SOUP_MEMORY_TEMPORARY,
-               (gchar *) request, strlen (request));
+       if (capabilities)
+               g_hash_table_destroy (capabilities);
+       if (allows)
+               g_hash_table_destroy (allows);
 
-       send_and_handle_ssl (webdav, message, cancellable);
+       if (!success)
+               g_clear_object (&bbdav->priv->webdav);
 
-       return message;
+       return success;
 }
 
-static xmlXPathObjectPtr
-xpath_eval (xmlXPathContextPtr ctx,
-            const gchar *format,
-            ...)
+static gboolean
+ebb_webdav_disconnect_sync (EBookMetaBackend *meta_backend,
+                           GCancellable *cancellable,
+                           GError **error)
 {
-       xmlXPathObjectPtr  result;
-       va_list            args;
-       gchar              *expr;
+       EBookBackendWebDAV *bbdav;
+       ESource *source;
 
-       if (ctx == NULL) {
-               return NULL;
-       }
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_WEBDAV (meta_backend), FALSE);
 
-       va_start (args, format);
-       expr = g_strdup_vprintf (format, args);
-       va_end (args);
+       bbdav = E_BOOK_BACKEND_WEBDAV (meta_backend);
 
-       result = xmlXPathEvalExpression ((xmlChar *) expr, ctx);
-       g_free (expr);
+       if (bbdav->priv->webdav)
+               soup_session_abort (SOUP_SESSION (bbdav->priv->webdav));
 
-       if (result == NULL) {
-               return NULL;
-       }
+       g_clear_object (&bbdav->priv->webdav);
 
-       if (result->type == XPATH_NODESET &&
-           xmlXPathNodeSetIsEmpty (result->nodesetval)) {
-               xmlXPathFreeObject (result);
-               return NULL;
-       }
+       source = e_backend_get_source (E_BACKEND (meta_backend));
+       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
 
-       return result;
+       return TRUE;
 }
 
-static gchar *
-xp_object_get_string (xmlXPathObjectPtr result)
+static void
+ebb_webdav_update_nfo_with_contact (EBookMetaBackendInfo *nfo,
+                                   EContact *contact,
+                                   const gchar *etag)
 {
-       gchar *ret = NULL;
+       const gchar *uid;
 
-       if (result == NULL)
-               return ret;
+       g_return_if_fail (nfo != NULL);
+       g_return_if_fail (E_IS_CONTACT (contact));
 
-       if (result->type == XPATH_STRING) {
-               ret = g_strdup ((gchar *) result->stringval);
-       }
+       uid = e_contact_get_const (contact, E_CONTACT_UID);
 
-       xmlXPathFreeObject (result);
-       return ret;
-}
+       if (!etag || !*etag)
+               etag = nfo->revision;
 
-static guint
-xp_object_get_status (xmlXPathObjectPtr result)
-{
-       gboolean res;
-       guint    ret = 0;
+       ebb_webdav_set_contact_etag (contact, etag);
 
-       if (result == NULL)
-               return ret;
+       g_warn_if_fail (nfo->object == NULL);
+       nfo->object = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
 
-       if (result->type == XPATH_STRING) {
-               res = soup_headers_parse_status_line ((gchar *) result->stringval, NULL, &ret, NULL);
-               if (!res) {
-                       ret = 0;
-               }
+       if (!nfo->uid || !*(nfo->uid)) {
+               g_free (nfo->uid);
+               nfo->uid = g_strdup (uid);
        }
 
-       xmlXPathFreeObject (result);
-       return ret;
+       if (g_strcmp0 (etag, nfo->revision) != 0) {
+               gchar *copy = g_strdup (etag);
+
+               g_free (nfo->revision);
+               nfo->revision = copy;
+       }
 }
 
 static gboolean
-check_addressbook_changed (EBookBackendWebdav *webdav,
-                           gchar **new_ctag,
-                           GCancellable *cancellable)
+ebb_webdav_multiget_response_cb (EWebDAVSession *webdav,
+                                xmlXPathContextPtr xpath_ctx,
+                                const gchar *xpath_prop_prefix,
+                                const SoupURI *request_uri,
+                                const gchar *href,
+                                guint status_code,
+                                gpointer user_data)
 {
-       gboolean res = TRUE;
-       const gchar *request = "<?xml version=\"1.0\" encoding=\"utf-8\"?><propfind 
xmlns=\"DAV:\"><prop><getctag/></prop></propfind>";
-       EBookBackendWebdavPrivate *priv;
-       SoupMessage *message;
+       GSList **from_link = user_data;
 
-       g_return_val_if_fail (webdav != NULL, TRUE);
-       g_return_val_if_fail (new_ctag != NULL, TRUE);
+       g_return_val_if_fail (from_link != NULL, FALSE);
 
-       *new_ctag = NULL;
-       priv = webdav->priv;
+       if (!xpath_prop_prefix) {
+               e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CARDDAV, NULL);
+       } else if (status_code == SOUP_STATUS_OK) {
+               gchar *address_data, *etag;
 
-       if (!priv->supports_getctag) {
-               gint64 real_time_us = g_get_real_time ();
+               g_return_val_if_fail (href != NULL, FALSE);
 
-               /* Fifteen minutes in microseconds */
-               if (real_time_us - priv->last_server_test_us < 15 * 60 * 1000 * 1000)
-                       return FALSE;
+               address_data = e_xml_xpath_eval_as_string (xpath_ctx, "%s/C:address-data", xpath_prop_prefix);
+               etag = e_webdav_session_util_maybe_dequote (e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/D:getetag", xpath_prop_prefix));
 
-               priv->last_server_test_us = real_time_us;
+               if (address_data) {
+                       EContact *contact;
 
-               return TRUE;
-       }
+                       contact = e_contact_new_from_vcard (address_data);
+                       if (contact) {
+                               const gchar *uid;
 
-       priv->supports_getctag = FALSE;
+                               uid = e_contact_get_const (contact, E_CONTACT_UID);
+                               if (uid) {
+                                       GSList *link;
 
-       message = soup_message_new (SOUP_METHOD_PROPFIND, priv->uri);
-       if (!message)
-               return TRUE;
+                                       for (link = *from_link; link; link = g_slist_next (link)) {
+                                               EBookMetaBackendInfo *nfo = link->data;
 
-       soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
-       soup_message_headers_append (message->request_headers, "Connection", "close");
-       soup_message_headers_append (message->request_headers, "Depth", "0");
-       soup_message_set_request (message, "text/xml", SOUP_MEMORY_TEMPORARY, (gchar *) request, strlen 
(request));
-       send_and_handle_ssl (webdav, message, cancellable);
-
-       if (message->status_code == 207 && message->response_body) {
-               xmlDocPtr xml;
-
-               xml = xmlReadMemory (message->response_body->data, message->response_body->length, NULL, 
NULL, XML_PARSE_NOWARNING);
-               if (xml) {
-                       const gchar *GETCTAG_XPATH_STATUS = 
"string(/D:multistatus/D:response/D:propstat/D:prop/D:getctag/../../D:status)";
-                       const gchar *GETCTAG_XPATH_VALUE = 
"string(/D:multistatus/D:response/D:propstat/D:prop/D:getctag)";
-                       xmlXPathContextPtr xpctx;
-
-                       xpctx = xmlXPathNewContext (xml);
-                       xmlXPathRegisterNs (xpctx, (xmlChar *) "D", (xmlChar *) "DAV:");
-
-                       if (xp_object_get_status (xpath_eval (xpctx, GETCTAG_XPATH_STATUS)) == 200) {
-                               gchar *txt = xp_object_get_string (xpath_eval (xpctx, GETCTAG_XPATH_VALUE));
-                               const gchar *stored_version;
-                               gboolean old_version;
-
-                               g_mutex_lock (&priv->cache_lock);
-                               stored_version = e_file_cache_get_object (E_FILE_CACHE (priv->cache), 
WEBDAV_CACHE_VERSION_KEY);
-
-                               /* The ETag was moved from REV to its own attribute, thus
-                                * if the cache version is too low, update it. */
-                               old_version = !stored_version || atoi (stored_version) < atoi 
(WEBDAV_CACHE_VERSION);
-                               g_mutex_unlock (&priv->cache_lock);
-
-                               if (txt && *txt) {
-                                       gint len = strlen (txt);
-
-                                       if (*txt == '\"' && len > 2 && txt[len - 1] == '\"') {
-                                               /* dequote */
-                                               *new_ctag = g_strndup (txt + 1, len - 2);
-                                       } else {
-                                               *new_ctag = txt;
-                                               txt = NULL;
-                                       }
+                                               if (!nfo)
+                                                       continue;
 
-                                       if (*new_ctag) {
-                                               const gchar *my_ctag;
+                                               if (g_strcmp0 (nfo->extra, href) == 0) {
+                                                       /* If the server returns data in the same order as it 
had been requested,
+                                                          then this speeds up lookup for the matching 
object. */
+                                                       if (link == *from_link)
+                                                               *from_link = g_slist_next (*from_link);
 
-                                               g_mutex_lock (&priv->cache_lock);
-                                               my_ctag = e_file_cache_get_object (E_FILE_CACHE 
(priv->cache), WEBDAV_CTAG_KEY);
-                                               res = old_version || !my_ctag || !g_str_equal (my_ctag, 
*new_ctag);
+                                                       ebb_webdav_update_nfo_with_contact (nfo, contact, 
etag);
 
-                                               priv->supports_getctag = TRUE;
-                                               g_mutex_unlock (&priv->cache_lock);
+                                                       break;
+                                               }
                                        }
                                }
 
-                               g_free (txt);
-
-                               if (old_version) {
-                                       g_mutex_lock (&priv->cache_lock);
-
-                                       if (!e_file_cache_replace_object (E_FILE_CACHE (priv->cache),
-                                               WEBDAV_CACHE_VERSION_KEY,
-                                               WEBDAV_CACHE_VERSION))
-                                               e_file_cache_add_object (
-                                                       E_FILE_CACHE (priv->cache),
-                                                       WEBDAV_CACHE_VERSION_KEY,
-                                                       WEBDAV_CACHE_VERSION);
-
-                                       g_mutex_unlock (&priv->cache_lock);
-                               }
+                               g_object_unref (contact);
                        }
-
-                       xmlXPathFreeContext (xpctx);
-                       xmlFreeDoc (xml);
                }
-       }
-
-       g_object_unref (message);
-
-       return res;
-}
 
-static void
-remove_unknown_contacts_cb (gpointer href,
-                           gpointer pcontact,
-                           gpointer pwebdav)
-{
-       EContact *contact = pcontact;
-       EBookBackendWebdav *webdav = pwebdav;
-       const gchar *uid;
+               g_free (address_data);
+               g_free (etag);
+       }
 
-       uid = e_contact_get_const (contact, E_CONTACT_UID);
-       if (uid && e_book_backend_cache_remove_contact (webdav->priv->cache, uid))
-               e_book_backend_notify_remove ((EBookBackend *) webdav, uid);
+       return TRUE;
 }
 
 static gboolean
-download_contacts (EBookBackendWebdav *webdav,
-                   EFlag *running,
-                   EDataBookView *book_view,
-                  gboolean force,
-                   GCancellable *cancellable,
-                   GError **error)
+ebb_webdav_multiget_from_sets_sync (EBookBackendWebDAV *bbdav,
+                                   GSList **in_link,
+                                   GSList **set2,
+                                   GCancellable *cancellable,
+                                   GError **error)
 {
-       EBookBackendWebdavPrivate *priv = webdav->priv;
-       EBookBackend              *book_backend;
-       SoupMessage               *message;
-       guint                      status;
-       xmlTextReaderPtr           reader;
-       response_element_t        *elements;
-       response_element_t        *element;
-       response_element_t        *next;
-       gint                        count;
-       gint                        i;
-       gchar                     *new_ctag = NULL;
-       GHashTable                *href_to_contact;
-       GList                     *cached_contacts, *iter;
-
-       g_mutex_lock (&priv->update_lock);
-
-       if (!force && !check_addressbook_changed (webdav, &new_ctag, cancellable)) {
-               g_free (new_ctag);
-               g_mutex_unlock (&priv->update_lock);
-               return TRUE;
-       }
+       EXmlDocument *xml;
+       gint left_to_go = E_WEBDAV_MAX_MULTIGET_AMOUNT;
+       GSList *link;
+       gboolean success = TRUE;
 
-       book_backend = E_BOOK_BACKEND (webdav);
+       g_return_val_if_fail (in_link != NULL, FALSE);
+       g_return_val_if_fail (*in_link != NULL, FALSE);
+       g_return_val_if_fail (set2 != NULL, FALSE);
 
-       if (book_view != NULL) {
-               e_data_book_view_notify_progress (book_view, -1,
-                               _("Loading Addressbook summary..."));
-       }
+       xml = e_xml_document_new (E_WEBDAV_NS_CARDDAV, "addressbook-multiget");
+       g_return_val_if_fail (xml != NULL, FALSE);
 
-       message = send_propfind (webdav, cancellable, error);
-       if (!message) {
-               g_free (new_ctag);
-               if (book_view)
-                       e_data_book_view_notify_progress (book_view, -1, NULL);
-               g_mutex_unlock (&priv->update_lock);
-               return FALSE;
-       }
+       e_xml_document_add_namespaces (xml, "D", E_WEBDAV_NS_DAV, NULL);
 
-       status = message->status_code;
-
-       if (status == SOUP_STATUS_UNAUTHORIZED ||
-           status == SOUP_STATUS_PROXY_UNAUTHORIZED ||
-           status == SOUP_STATUS_FORBIDDEN) {
-               g_object_unref (message);
-               g_free (new_ctag);
-               if (book_view)
-                       e_data_book_view_notify_progress (book_view, -1, NULL);
-               g_mutex_unlock (&priv->update_lock);
-               return webdav_handle_auth_request (webdav, error);
-       }
-       if (status != 207) {
-               g_set_error (
-                       error, E_CLIENT_ERROR,
-                       E_CLIENT_ERROR_OTHER_ERROR,
-                       _("PROPFIND on webdav failed with HTTP status %d (%s)"),
-                       status,
-                       message->reason_phrase && *message->reason_phrase ? message->reason_phrase :
-                       (soup_status_get_phrase (message->status_code) ? soup_status_get_phrase 
(message->status_code) : _("Unknown error")));
+       e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "prop");
+       e_xml_document_add_empty_element (xml, E_WEBDAV_NS_DAV, "getetag");
+       e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CARDDAV, "address-data");
+       e_xml_document_end_element (xml); /* prop */
 
-               g_object_unref (message);
-               g_free (new_ctag);
+       link = *in_link;
 
-               if (book_view)
-                       e_data_book_view_notify_progress (book_view, -1, NULL);
+       while (link && left_to_go > 0) {
+               EBookMetaBackendInfo *nfo = link->data;
+               SoupURI *suri;
+               gchar *path = NULL;
 
-               g_mutex_unlock (&priv->update_lock);
+               link = g_slist_next (link);
+               if (!link) {
+                       link = *set2;
+                       *set2 = NULL;
+               }
 
-               return FALSE;
-       }
-       if (message->response_body == NULL) {
-               g_set_error_literal (
-                       error, E_CLIENT_ERROR,
-                       E_CLIENT_ERROR_OTHER_ERROR,
-                       _("No response body in webdav PROPFIND result"));
+               if (!nfo)
+                       continue;
 
-               g_object_unref (message);
-               g_free (new_ctag);
+               left_to_go--;
 
-               if (book_view)
-                       e_data_book_view_notify_progress (book_view, -1, NULL);
+               suri = soup_uri_new (nfo->extra);
+               if (suri) {
+                       path = soup_uri_to_string (suri, TRUE);
+                       soup_uri_free (suri);
+               }
 
-               g_mutex_unlock (&priv->update_lock);
+               e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "href");
+               e_xml_document_write_string (xml, path ? path : nfo->extra);
+               e_xml_document_end_element (xml); /* href */
 
-               return FALSE;
+               g_free (path);
        }
 
-       /* parse response */
-       reader = xmlReaderForMemory (
-               message->response_body->data,
-               message->response_body->length, NULL, NULL,
-               XML_PARSE_NOWARNING);
+       if (left_to_go != E_WEBDAV_MAX_MULTIGET_AMOUNT && success) {
+               GSList *from_link = *in_link;
 
-       elements = parse_propfind_response (reader);
-
-       /* count contacts */
-       count = 0;
-       for (element = elements; element != NULL; element = element->next) {
-               ++count;
+               success = e_webdav_session_report_sync (bbdav->priv->webdav, NULL, NULL, xml,
+                       ebb_webdav_multiget_response_cb, &from_link, NULL, NULL, cancellable, error);
        }
 
-       href_to_contact = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
-       g_mutex_lock (&priv->cache_lock);
-       e_file_cache_freeze_changes (E_FILE_CACHE (priv->cache));
-       cached_contacts = e_book_backend_cache_get_contacts (priv->cache, NULL);
-       for (iter = cached_contacts; iter; iter = g_list_next (iter)) {
-               EContact *contact = iter->data;
-               gchar *href;
-
-               if (!contact)
-                       continue;
+       g_object_unref (xml);
 
-               href = webdav_contact_get_href (contact);
+       *in_link = link;
 
-               if (href)
-                       g_hash_table_insert (href_to_contact, href, g_object_ref (contact));
-       }
-       g_list_free_full (cached_contacts, g_object_unref);
-       g_mutex_unlock (&priv->cache_lock);
-
-       /* download contacts */
-       i = 0;
-       for (element = elements; element != NULL; element = element->next, ++i) {
-               const gchar  *uri;
-               const gchar *etag;
-               EContact    *contact;
-               gchar *complete_uri, *stored_etag;
-
-               /* stop downloading if search was aborted */
-               if (running != NULL && !e_flag_is_set (running))
-                       break;
-
-               if (book_view != NULL) {
-                       gfloat percent = 100.0 / count * i;
-                       gchar buf[100];
-                       snprintf (buf, sizeof (buf), _("Loading Contacts (%d%%)"), (gint) percent);
-                       e_data_book_view_notify_progress (book_view, -1, buf);
-               }
+       return success;
+}
 
-               /* skip collections */
-               uri = (const gchar *) element->href;
-               if (uri[strlen (uri) - 1] == '/')
-                       continue;
+static gboolean
+ebb_webdav_get_contact_items_cb (EWebDAVSession *webdav,
+                                xmlXPathContextPtr xpath_ctx,
+                                const gchar *xpath_prop_prefix,
+                                const SoupURI *request_uri,
+                                const gchar *href,
+                                guint status_code,
+                                gpointer user_data)
+{
+       GHashTable *known_items = user_data; /* gchar *href ~> EBookMetaBackendInfo * */
 
-               /* uri might be relative, construct complete one */
-               if (uri[0] == '/') {
-                       SoupURI *soup_uri = soup_uri_new (priv->uri);
-                       g_free (soup_uri->path);
-                       soup_uri->path = g_strdup (uri);
+       g_return_val_if_fail (xpath_ctx != NULL, FALSE);
+       g_return_val_if_fail (known_items != NULL, FALSE);
 
-                       complete_uri = soup_uri_to_string (soup_uri, FALSE);
-                       soup_uri_free (soup_uri);
-               } else {
-                       complete_uri = g_strdup (uri);
-               }
+       if (xpath_prop_prefix &&
+           status_code == SOUP_STATUS_OK) {
+               EBookMetaBackendInfo *nfo;
+               gchar *etag;
 
-               etag = (const gchar *) element->etag;
+               g_return_val_if_fail (href != NULL, FALSE);
 
-               contact = g_hash_table_lookup (href_to_contact, complete_uri);
-               if (contact) {
-                       g_object_ref (contact);
-                       g_hash_table_remove (href_to_contact, complete_uri);
-                       stored_etag = webdav_contact_get_etag (contact);
-               } else {
-                       stored_etag = NULL;
+               /* Skip collection resource, if returned by the server (like iCloud.com does) */
+               if (g_str_has_suffix (href, "/") ||
+                   (request_uri && request_uri->path && g_str_has_suffix (href, request_uri->path))) {
+                       return TRUE;
                }
 
+               etag = e_webdav_session_util_maybe_dequote (e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/D:getetag", xpath_prop_prefix));
+               /* Return 'TRUE' to not stop on faulty data from the server */
+               g_return_val_if_fail (etag != NULL, TRUE);
 
-               /* download contact if it is not cached or its ETag changed */
-               if (contact == NULL || etag == NULL || !stored_etag ||
-                   strcmp (stored_etag, etag) != 0) {
-                       if (contact != NULL)
-                               g_object_unref (contact);
-                       contact = download_contact (webdav, complete_uri, cancellable);
-                       if (contact != NULL) {
-                               const gchar *uid;
+               /* UID is unknown at this moment */
+               nfo = e_book_meta_backend_info_new ("", etag, NULL, href);
 
-                               uid = e_contact_get_const (contact, E_CONTACT_UID);
-
-                               g_mutex_lock (&priv->cache_lock);
-                               if (e_book_backend_cache_remove_contact (priv->cache, uid))
-                                       e_book_backend_notify_remove (book_backend, uid);
-                               e_book_backend_cache_add_contact (priv->cache, contact);
-                               g_mutex_unlock (&priv->cache_lock);
-                               e_book_backend_notify_update (book_backend, contact);
-                       }
-               }
+               g_free (etag);
+               g_return_val_if_fail (nfo != NULL, FALSE);
 
-               if (contact != NULL)
-                       g_object_unref (contact);
-               g_free (complete_uri);
-               g_free (stored_etag);
+               g_hash_table_insert (known_items, g_strdup (href), nfo);
        }
 
-       /* free element list */
-       for (element = elements; element != NULL; element = next) {
-               next = element->next;
+       return TRUE;
+}
 
-               xmlFree (element->href);
-               xmlFree (element->etag);
-               g_free (element);
-       }
+typedef struct _WebDAVChangesData {
+       GSList **out_modified_objects;
+       GSList **out_removed_objects;
+       GHashTable *known_items; /* gchar *href ~> EBookMetaBackendInfo * */
+} WebDAVChangesData;
 
-       xmlFreeTextReader (reader);
-       g_object_unref (message);
+static gboolean
+ebb_webdav_search_changes_cb (EBookCache *book_cache,
+                             const gchar *uid,
+                             const gchar *revision,
+                             const gchar *object,
+                             const gchar *extra,
+                             EOfflineState offline_state,
+                             gpointer user_data)
+{
+       WebDAVChangesData *ccd = user_data;
 
-       if (new_ctag) {
-               g_mutex_lock (&priv->cache_lock);
-               if (!e_file_cache_replace_object (E_FILE_CACHE (priv->cache), WEBDAV_CTAG_KEY, new_ctag))
-                       e_file_cache_add_object (E_FILE_CACHE (priv->cache), WEBDAV_CTAG_KEY, new_ctag);
-               g_mutex_unlock (&priv->cache_lock);
-       }
-       g_free (new_ctag);
+       g_return_val_if_fail (ccd != NULL, FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
 
-       if (book_view)
-               e_data_book_view_notify_progress (book_view, -1, NULL);
+       /* Can be NULL for added components in offline mode */
+       if (extra && *extra) {
+               EBookMetaBackendInfo *nfo;
 
-       g_mutex_lock (&priv->cache_lock);
+               nfo = g_hash_table_lookup (ccd->known_items, extra);
+               if (nfo) {
+                       if (g_strcmp0 (revision, nfo->revision) == 0) {
+                               g_hash_table_remove (ccd->known_items, extra);
+                       } else {
+                               if (!nfo->uid || !*(nfo->uid)) {
+                                       g_free (nfo->uid);
+                                       nfo->uid = g_strdup (uid);
+                               }
 
-       if (!g_cancellable_is_cancelled (cancellable) &&
-           (!running || e_flag_is_set (running))) {
-               /* clean-up the cache only if it wasn't cancelled during the work */
-               g_hash_table_foreach (href_to_contact, remove_unknown_contacts_cb, webdav);
-       }
-       e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
-       g_mutex_unlock (&priv->cache_lock);
-       g_mutex_unlock (&priv->update_lock);
+                               *(ccd->out_modified_objects) = g_slist_prepend (*(ccd->out_modified_objects),
+                                       e_book_meta_backend_info_copy (nfo));
 
-       g_hash_table_destroy (href_to_contact);
+                               g_hash_table_remove (ccd->known_items, extra);
+                       }
+               } else {
+                       *(ccd->out_removed_objects) = g_slist_prepend (*(ccd->out_removed_objects),
+                               e_book_meta_backend_info_new (uid, revision, object, extra));
+               }
+       }
 
        return TRUE;
 }
 
-static gpointer
-book_view_thread (gpointer data)
+static gboolean
+ebb_webdav_get_changes_sync (EBookMetaBackend *meta_backend,
+                            const gchar *last_sync_tag,
+                            gboolean is_repeat,
+                            gchar **out_new_sync_tag,
+                            gboolean *out_repeat,
+                            GSList **out_created_objects,
+                            GSList **out_modified_objects,
+                            GSList **out_removed_objects,
+                            GCancellable *cancellable,
+                            GError **error)
 {
-       EDataBookView *book_view = data;
-       WebdavBackendSearchClosure *closure = get_closure (book_view);
-       EBookBackendWebdav *webdav = closure->webdav;
-
-       e_flag_set (closure->running);
-
-       /* ref the book view because it'll be removed and unrefed when/if
-        * it's stopped */
-       g_object_ref (book_view);
-
-       download_contacts (webdav, closure->running, book_view, FALSE, NULL, NULL);
-
-       g_object_unref (book_view);
-
-       return NULL;
-}
+       EBookBackendWebDAV *bbdav;
+       EXmlDocument *xml;
+       GHashTable *known_items; /* gchar *href ~> EBookMetaBackendInfo * */
+       GHashTableIter iter;
+       gpointer key = NULL, value = NULL;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_WEBDAV (meta_backend), FALSE);
+       g_return_val_if_fail (out_new_sync_tag, FALSE);
+       g_return_val_if_fail (out_created_objects, FALSE);
+       g_return_val_if_fail (out_modified_objects, FALSE);
+       g_return_val_if_fail (out_removed_objects, FALSE);
+
+       *out_new_sync_tag = NULL;
+       *out_created_objects = NULL;
+       *out_modified_objects = NULL;
+       *out_removed_objects = NULL;
+
+       bbdav = E_BOOK_BACKEND_WEBDAV (meta_backend);
+
+       if (bbdav->priv->ctag_supported) {
+               gchar *new_sync_tag = NULL;
+
+               success = e_webdav_session_getctag_sync (bbdav->priv->webdav, NULL, &new_sync_tag, 
cancellable, NULL);
+               if (!success) {
+                       bbdav->priv->ctag_supported = g_cancellable_set_error_if_cancelled (cancellable, 
error);
+                       if (bbdav->priv->ctag_supported || !bbdav->priv->webdav)
+                               return FALSE;
+               } else if (new_sync_tag && last_sync_tag && g_strcmp0 (last_sync_tag, new_sync_tag) == 0) {
+                       *out_new_sync_tag = new_sync_tag;
+                       return TRUE;
+               }
 
-static void
-e_book_backend_webdav_start_view (EBookBackend *backend,
-                                  EDataBookView *book_view)
-{
-       EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
-       EBookBackendWebdavPrivate *priv = webdav->priv;
-       EBookBackendSExp *sexp;
-       const gchar *query;
-       GList *contacts;
-       GList *l;
-
-       sexp = e_data_book_view_get_sexp (book_view);
-       query = e_book_backend_sexp_text (sexp);
-
-       g_mutex_lock (&priv->cache_lock);
-       contacts = e_book_backend_cache_get_contacts (priv->cache, query);
-       g_mutex_unlock (&priv->cache_lock);
-
-       for (l = contacts; l != NULL; l = g_list_next (l)) {
-               EContact *contact = l->data;
-               e_data_book_view_notify_update (book_view, contact);
-               g_object_unref (contact);
+               *out_new_sync_tag = new_sync_tag;
        }
-       g_list_free (contacts);
 
-       /* this way the UI is notified about cached contacts immediately,
-        * and the update thread notifies about possible changes only */
-       e_data_book_view_notify_complete (book_view, NULL /* Success */);
+       xml = e_xml_document_new (E_WEBDAV_NS_DAV, "propfind");
+       g_return_val_if_fail (xml != NULL, FALSE);
 
-       if (e_backend_get_online (E_BACKEND (backend))) {
-               WebdavBackendSearchClosure *closure;
+       e_xml_document_start_element (xml, NULL, "prop");
+       e_xml_document_add_empty_element (xml, NULL, "getetag");
+       e_xml_document_end_element (xml); /* prop */
 
-               closure = init_closure (
-                       book_view, E_BOOK_BACKEND_WEBDAV (backend));
+       known_items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, e_book_meta_backend_info_free);
 
-               closure->thread = g_thread_new (
-                       NULL, book_view_thread, book_view);
+       success = e_webdav_session_propfind_sync (bbdav->priv->webdav, NULL, 
E_WEBDAV_DEPTH_THIS_AND_CHILDREN, xml,
+               ebb_webdav_get_contact_items_cb, known_items, cancellable, error);
 
-               e_flag_wait (closure->running);
-       }
-}
+       g_object_unref (xml);
 
-static void
-e_book_backend_webdav_stop_view (EBookBackend *backend,
-                                 EDataBookView *book_view)
-{
-       WebdavBackendSearchClosure *closure;
-       gboolean                    need_join;
+       if (success) {
+               EBookCache *book_cache;
+               WebDAVChangesData ccd;
 
-       if (!e_backend_get_online (E_BACKEND (backend)))
-               return;
+               ccd.out_modified_objects = out_modified_objects;
+               ccd.out_removed_objects = out_removed_objects;
+               ccd.known_items = known_items;
 
-       closure = get_closure (book_view);
-       if (closure == NULL)
-               return;
+               book_cache = e_book_meta_backend_ref_cache (meta_backend);
 
-       need_join = e_flag_is_set (closure->running);
-       e_flag_clear (closure->running);
+               success = e_book_cache_search_with_callback (book_cache, NULL, ebb_webdav_search_changes_cb, 
&ccd, cancellable, error);
 
-       if (need_join) {
-               g_thread_join (closure->thread);
-               closure->thread = NULL;
+               g_clear_object (&book_cache);
        }
-}
 
-/** authentication callback for libsoup */
-static void
-soup_authenticate (SoupSession *session,
-                   SoupMessage *message,
-                   SoupAuth *auth,
-                   gboolean retrying,
-                   gpointer data)
-{
-       EBookBackendWebdav        *webdav = data;
-       EBookBackendWebdavPrivate *priv = webdav->priv;
-
-       if (retrying)
-               return;
-
-       if (!priv->username || !*priv->username || !priv->password)
-               soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
-       else
-               soup_auth_authenticate (auth, priv->username, priv->password);
-}
-
-static void
-e_book_backend_webdav_notify_online_cb (EBookBackend *backend,
-                                        GParamSpec *pspec)
-{
-       gboolean online;
+       if (!success) {
+               g_hash_table_destroy (known_items);
+               return FALSE;
+       }
 
-       /* set_mode is called before the backend is loaded */
-       if (!e_book_backend_is_opened (backend))
-               return;
+       g_hash_table_iter_init (&iter, known_items);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               *out_created_objects = g_slist_prepend (*out_created_objects, e_book_meta_backend_info_copy 
(value));
+       }
 
-       /* XXX Could just use a property binding for this.
-        *     EBackend:online --> EBookBackend:writable */
-       online = e_backend_get_online (E_BACKEND (backend));
-       e_book_backend_set_writable (backend, online);
-}
+       g_hash_table_destroy (known_items);
 
-static void
-book_backend_webdav_dispose (GObject *object)
-{
-       EBookBackendWebdavPrivate *priv;
+       if (*out_created_objects || *out_modified_objects) {
+               GSList *link, *set2 = *out_modified_objects;
 
-       priv = E_BOOK_BACKEND_WEBDAV_GET_PRIVATE (object);
+               if (*out_created_objects) {
+                       link = *out_created_objects;
+               } else {
+                       link = set2;
+                       set2 = NULL;
+               }
 
-       g_clear_object (&priv->session);
-       g_clear_object (&priv->cache);
+               do {
+                       success = ebb_webdav_multiget_from_sets_sync (bbdav, &link, &set2, cancellable, 
error);
+               } while (success && link);
+       }
 
-       /* Chain up to parent's dispose() method. */
-       G_OBJECT_CLASS (e_book_backend_webdav_parent_class)->dispose (object);
+       return success;
 }
 
-static void
-book_backend_webdav_finalize (GObject *object)
+static gboolean
+ebb_webdav_extract_existing_cb (EWebDAVSession *webdav,
+                               xmlXPathContextPtr xpath_ctx,
+                               const gchar *xpath_prop_prefix,
+                               const SoupURI *request_uri,
+                               const gchar *href,
+                               guint status_code,
+                               gpointer user_data)
 {
-       EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (object);
-       EBookBackendWebdavPrivate *priv = webdav->priv;
+       GSList **out_existing_objects = user_data;
 
-       g_free (priv->uri);
-       g_free (priv->username);
-       g_free (priv->password);
+       g_return_val_if_fail (out_existing_objects != NULL, FALSE);
 
-       g_mutex_clear (&priv->cache_lock);
-       g_mutex_clear (&priv->update_lock);
+       if (!xpath_prop_prefix) {
+               e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CARDDAV, NULL);
+       } else if (status_code == SOUP_STATUS_OK) {
+               gchar *etag;
+               gchar *address_data;
 
-       /* Chain up to parent's finalize() method. */
-       G_OBJECT_CLASS (e_book_backend_webdav_parent_class)->finalize (object);
-}
+               g_return_val_if_fail (href != NULL, FALSE);
 
-static gchar *
-book_backend_webdav_get_backend_property (EBookBackend *backend,
-                                          const gchar *prop_name)
-{
-       g_return_val_if_fail (prop_name != NULL, NULL);
+               etag = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:getetag", xpath_prop_prefix);
+               address_data = e_xml_xpath_eval_as_string (xpath_ctx, "%s/C:address-data", xpath_prop_prefix);
 
-       if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
-               return g_strdup ("net,do-initial-query,contact-lists,refresh-supported");
+               if (address_data) {
+                       EContact *contact;
 
-       } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
-               return g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
+                       contact = e_contact_new_from_vcard (address_data);
+                       if (contact) {
+                               const gchar *uid;
 
-       } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
-               GString *fields;
-               gint ii;
+                               uid = e_contact_get_const (contact, E_CONTACT_UID);
 
-               fields = g_string_sized_new (1024);
+                               if (uid) {
+                                       etag = e_webdav_session_util_maybe_dequote (etag);
+                                       *out_existing_objects = g_slist_prepend (*out_existing_objects,
+                                               e_book_meta_backend_info_new (uid, etag, NULL, href));
+                               }
 
-               /* we support everything */
-               for (ii = 1; ii < E_CONTACT_FIELD_LAST; ii++) {
-                       if (fields->len > 0)
-                               g_string_append_c (fields, ',');
-                       g_string_append (fields, e_contact_field_name (ii));
+                               g_object_unref (contact);
+                       }
                }
 
-               return g_string_free (fields, FALSE);
+               g_free (address_data);
+               g_free (etag);
        }
 
-       /* Chain up to parent's get_backend_property() method. */
-       return E_BOOK_BACKEND_CLASS (e_book_backend_webdav_parent_class)->
-               get_backend_property (backend, prop_name);
+       return TRUE;
 }
 
 static gboolean
-book_backend_webdav_test_can_connect (EBookBackendWebdav *webdav,
-                                     gchar **out_certificate_pem,
-                                     GTlsCertificateFlags *out_certificate_errors,
-                                     GCancellable *cancellable,
-                                     GError **error)
+ebb_webdav_list_existing_sync (EBookMetaBackend *meta_backend,
+                              gchar **out_new_sync_tag,
+                              GSList **out_existing_objects,
+                              GCancellable *cancellable,
+                              GError **error)
 {
-       SoupMessage *message;
-       gboolean res = FALSE;
+       EBookBackendWebDAV *bbdav;
+       EXmlDocument *xml;
+       gboolean success;
 
-       g_return_val_if_fail (E_IS_BOOK_BACKEND_WEBDAV (webdav), FALSE);
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_WEBDAV (meta_backend), FALSE);
+       g_return_val_if_fail (out_existing_objects != NULL, FALSE);
 
-       /* Send a PROPFIND to test whether user/password is correct. */
-       message = send_propfind (webdav, cancellable, error);
-       if (!message)
-               return FALSE;
+       *out_existing_objects = NULL;
 
-       switch (message->status_code) {
-               case SOUP_STATUS_OK:
-               case SOUP_STATUS_MULTI_STATUS:
-                       res = TRUE;
-                       break;
-
-               case SOUP_STATUS_UNAUTHORIZED:
-               case SOUP_STATUS_PROXY_UNAUTHORIZED:
-                       g_free (webdav->priv->username);
-                       webdav->priv->username = NULL;
-                       g_free (webdav->priv->password);
-                       webdav->priv->password = NULL;
-                       g_set_error_literal (error, E_CLIENT_ERROR, E_CLIENT_ERROR_AUTHENTICATION_FAILED,
-                               e_client_error_to_string (E_CLIENT_ERROR_AUTHENTICATION_FAILED));
-                       break;
-
-               case SOUP_STATUS_FORBIDDEN:
-                       g_free (webdav->priv->username);
-                       webdav->priv->username = NULL;
-                       g_free (webdav->priv->password);
-                       webdav->priv->password = NULL;
-                       g_set_error_literal (error, E_CLIENT_ERROR, E_CLIENT_ERROR_AUTHENTICATION_REQUIRED,
-                               e_client_error_to_string (E_CLIENT_ERROR_AUTHENTICATION_REQUIRED));
-                       break;
-
-               case SOUP_STATUS_SSL_FAILED:
-                       if (out_certificate_pem && out_certificate_errors) {
-                               GTlsCertificate *certificate = NULL;
-
-                               g_object_get (G_OBJECT (message),
-                                       "tls-certificate", &certificate,
-                                       "tls-errors", out_certificate_errors,
-                                       NULL);
-
-                               if (certificate) {
-                                       g_object_get (certificate, "certificate-pem", out_certificate_pem, 
NULL);
-                                       g_object_unref (certificate);
-                               }
-                       }
-
-                       g_set_error_literal (
-                               error, SOUP_HTTP_ERROR,
-                               message->status_code,
-                               message->reason_phrase);
-                       break;
-
-               default:
-                       g_set_error_literal (
-                               error, SOUP_HTTP_ERROR,
-                               message->status_code,
-                               message->reason_phrase);
-                       break;
-       }
+       bbdav = E_BOOK_BACKEND_WEBDAV (meta_backend);
 
-       g_object_unref (message);
+       xml = e_xml_document_new (E_WEBDAV_NS_CARDDAV, "addressbook-query");
+       g_return_val_if_fail (xml != NULL, FALSE);
 
-       return res;
-}
-
-static gboolean
-book_backend_webdav_open_sync (EBookBackend *backend,
-                               GCancellable *cancellable,
-                               GError **error)
-{
-       EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
-       ESourceAuthentication     *auth_extension;
-       ESourceOffline            *offline_extension;
-       ESourceWebdav             *webdav_extension;
-       ESource                   *source;
-       const gchar               *extension_name;
-       const gchar               *cache_dir;
-       gchar                     *filename;
-       SoupSession               *session;
-       SoupURI                   *suri;
-       gboolean                   success = TRUE;
-
-       /* will try fetch ctag for the first time, if it fails then sets this to FALSE */
-       webdav->priv->supports_getctag = TRUE;
-
-       source = e_backend_get_source (E_BACKEND (backend));
-       cache_dir = e_book_backend_get_cache_dir (backend);
-
-       extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
-       auth_extension = e_source_get_extension (source, extension_name);
-
-       extension_name = E_SOURCE_EXTENSION_OFFLINE;
-       offline_extension = e_source_get_extension (source, extension_name);
-
-       extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
-       webdav_extension = e_source_get_extension (source, extension_name);
-
-       webdav->priv->marked_for_offline =
-               e_source_offline_get_stay_synchronized (offline_extension);
-
-       if (!e_backend_get_online (E_BACKEND (backend)) &&
-           !webdav->priv->marked_for_offline ) {
-               g_set_error_literal (
-                       error, E_CLIENT_ERROR,
-                       E_CLIENT_ERROR_OFFLINE_UNAVAILABLE,
-                       e_client_error_to_string (
-                       E_CLIENT_ERROR_OFFLINE_UNAVAILABLE));
-               return FALSE;
-       }
+       e_xml_document_add_namespaces (xml, "D", E_WEBDAV_NS_DAV, NULL);
 
-       suri = e_source_webdav_dup_soup_uri (webdav_extension);
-
-       webdav->priv->uri = soup_uri_to_string (suri, FALSE);
-       if (!webdav->priv->uri || !*webdav->priv->uri) {
-               g_free (webdav->priv->uri);
-               webdav->priv->uri = NULL;
-               soup_uri_free (suri);
-               g_set_error_literal (
-                       error, E_CLIENT_ERROR,
-                       E_CLIENT_ERROR_OTHER_ERROR,
-                       _("Cannot transform SoupURI to string"));
-               return FALSE;
-       }
+       e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "prop");
+       e_xml_document_add_empty_element (xml, E_WEBDAV_NS_DAV, "getetag");
+       e_xml_document_start_element (xml, E_WEBDAV_NS_CARDDAV, "address-data");
+       e_xml_document_start_element (xml, E_WEBDAV_NS_CARDDAV, "prop");
+       e_xml_document_add_attribute (xml, NULL, "name", "VERSION");
+       e_xml_document_end_element (xml); /* prop / VERSION */
+       e_xml_document_start_element (xml, E_WEBDAV_NS_CARDDAV, "prop");
+       e_xml_document_add_attribute (xml, NULL, "name", "UID");
+       e_xml_document_end_element (xml); /* prop / UID */
+       e_xml_document_end_element (xml); /* address-data */
+       e_xml_document_end_element (xml); /* prop */
 
-       g_mutex_lock (&webdav->priv->cache_lock);
+       success = e_webdav_session_report_sync (bbdav->priv->webdav, NULL, E_WEBDAV_DEPTH_THIS, xml,
+               ebb_webdav_extract_existing_cb, out_existing_objects, NULL, NULL, cancellable, error);
 
-       /* make sure the uri ends with a forward slash */
-       if (webdav->priv->uri[strlen (webdav->priv->uri) - 1] != '/') {
-               gchar *tmp = webdav->priv->uri;
-               webdav->priv->uri = g_strconcat (tmp, "/", NULL);
-               g_free (tmp);
-       }
+       g_object_unref (xml);
 
-       if (!webdav->priv->cache) {
-               filename = g_build_filename (cache_dir, "cache.xml", NULL);
-               webdav->priv->cache = e_book_backend_cache_new (filename);
-               g_free (filename);
-       }
-       g_mutex_unlock (&webdav->priv->cache_lock);
+       if (success)
+               *out_existing_objects = g_slist_reverse (*out_existing_objects);
 
-       session = soup_session_sync_new ();
-       g_object_set (
-               session,
-               SOUP_SESSION_TIMEOUT, 90,
-               SOUP_SESSION_SSL_STRICT, TRUE,
-               SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
-               SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
-               NULL);
-
-       e_binding_bind_property (
-               backend, "proxy-resolver",
-               session, "proxy-resolver",
-               G_BINDING_SYNC_CREATE);
-
-       e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
-
-       g_signal_connect (
-               session, "authenticate",
-               G_CALLBACK (soup_authenticate), webdav);
-
-       webdav->priv->session = session;
-       webdav_debug_setup (webdav->priv->session);
+       return success;
+}
 
-       e_backend_set_online (E_BACKEND (backend), TRUE);
-       e_book_backend_set_writable (backend, TRUE);
+static gchar *
+ebb_webdav_uid_to_uri (EBookBackendWebDAV *bbdav,
+                      const gchar *uid,
+                      const gchar *extension)
+{
+       ESourceWebdav *webdav_extension;
+       SoupURI *soup_uri;
+       gchar *uri, *tmp, *filename;
 
-       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_WEBDAV (bbdav), NULL);
+       g_return_val_if_fail (uid != NULL, NULL);
 
-       if (e_source_authentication_required (auth_extension)) {
-               e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+       webdav_extension = e_source_get_extension (e_backend_get_source (E_BACKEND (bbdav)), 
E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+       soup_uri = e_source_webdav_dup_soup_uri (webdav_extension);
+       g_return_val_if_fail (soup_uri != NULL, NULL);
 
-               success = e_backend_credentials_required_sync (E_BACKEND (backend),
-                       E_SOURCE_CREDENTIALS_REASON_REQUIRED, NULL, 0, NULL,
-                       cancellable, error);
+       if (extension) {
+               tmp = g_strconcat (uid, extension, NULL);
+               filename = soup_uri_encode (tmp, NULL);
+               g_free (tmp);
        } else {
-               gchar *certificate_pem = NULL;
-               GTlsCertificateFlags certificate_errors = 0;
-               GError *local_error = NULL;
-
-               success = book_backend_webdav_test_can_connect (webdav, &certificate_pem, 
&certificate_errors, cancellable, &local_error);
-               if (!success && !g_cancellable_is_cancelled (cancellable)) {
-                       ESourceCredentialsReason reason;
-                       GError *local_error2 = NULL;
-
-                       if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
-                               reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED;
-                               e_source_set_connection_status (source, 
E_SOURCE_CONNECTION_STATUS_SSL_FAILED);
-                       } else if (g_error_matches (local_error, E_CLIENT_ERROR, 
E_CLIENT_ERROR_AUTHENTICATION_FAILED) ||
-                                  g_error_matches (local_error, E_CLIENT_ERROR, 
E_CLIENT_ERROR_AUTHENTICATION_REQUIRED)) {
-                               reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
-                       } else {
-                               reason = E_SOURCE_CREDENTIALS_REASON_ERROR;
-                       }
-
-                       if (!e_backend_credentials_required_sync (E_BACKEND (backend), reason, 
certificate_pem, certificate_errors,
-                               local_error, cancellable, &local_error2)) {
-                               g_warning ("%s: Failed to call credentials required: %s", G_STRFUNC, 
local_error2 ? local_error2->message : "Unknown error");
-                       }
-
-                       if (!local_error2 && g_error_matches (local_error, SOUP_HTTP_ERROR, 
SOUP_STATUS_SSL_FAILED)) {
-                               /* These cerificate errors are treated through the authentication */
-                               g_clear_error (&local_error);
-                       } else {
-                               g_propagate_error (error, local_error);
-                               local_error = NULL;
-                       }
-
-                       g_clear_error (&local_error2);
-               } else {
-                       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
-               }
-
-               g_free (certificate_pem);
-
-               if (local_error)
-                       g_propagate_error (error, local_error);
+               filename = soup_uri_encode (uid, NULL);
        }
 
-       soup_uri_free (suri);
+       if (soup_uri->path) {
+               gchar *slash = strrchr (soup_uri->path, '/');
 
-       return success;
-}
+               if (slash && !slash[1])
+                       *slash = '\0';
+       }
 
-static gboolean
-webdav_can_use_uid (const gchar *uid)
-{
-       const gchar *ptr;
+       soup_uri_set_user (soup_uri, NULL);
+       soup_uri_set_password (soup_uri, NULL);
 
-       if (!uid || !*uid)
-               return FALSE;
+       tmp = g_strconcat (soup_uri->path && *soup_uri->path ? soup_uri->path : "", "/", filename, NULL);
+       soup_uri_set_path (soup_uri, tmp);
+       g_free (tmp);
 
-       for (ptr = uid; *ptr; ptr++) {
-               if ((*ptr >= 'a' && *ptr <= 'z') ||
-                   (*ptr >= 'A' && *ptr <= 'Z') ||
-                   (*ptr >= '0' && *ptr <= '9') ||
-                   strchr (".-@", *ptr) != NULL)
-                       continue;
+       uri = soup_uri_to_string (soup_uri, FALSE);
 
-               return FALSE;
-       }
+       soup_uri_free (soup_uri);
+       g_free (filename);
 
-       return TRUE;
+       return uri;
 }
 
 static gboolean
-book_backend_webdav_create_contacts_sync (EBookBackend *backend,
-                                          const gchar * const *vcards,
-                                          GQueue *out_contacts,
-                                          GCancellable *cancellable,
-                                          GError **error)
+ebb_webdav_load_contact_sync (EBookMetaBackend *meta_backend,
+                             const gchar *uid,
+                             const gchar *extra,
+                             EContact **out_contact,
+                             gchar **out_extra,
+                             GCancellable *cancellable,
+                             GError **error)
 {
-       EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
-       EContact *contact;
-       gchar *uid, *href;
-       const gchar *orig_uid;
-       guint status;
-       gchar *status_reason = NULL, *stored_etag;
-
-       /* We make the assumption that the vCard list we're passed is
-        * always exactly one element long, since we haven't specified
-        * "bulk-adds" in our static capability list.  This is because
-        * there is no way to roll back changes in case of an error. */
-       if (g_strv_length ((gchar **) vcards) > 1) {
-               g_set_error_literal (
-                       error, E_CLIENT_ERROR,
-                       E_CLIENT_ERROR_NOT_SUPPORTED,
-                       _("The backend does not support bulk additions"));
-               return FALSE;
-       }
-
-       if (!e_backend_get_online (E_BACKEND (backend))) {
-               g_set_error_literal (
-                       error, E_CLIENT_ERROR,
-                       E_CLIENT_ERROR_REPOSITORY_OFFLINE,
-                       e_client_error_to_string (
-                       E_CLIENT_ERROR_REPOSITORY_OFFLINE));
-               return FALSE;
-       }
+       EBookBackendWebDAV *bbdav;
+       gchar *uri = NULL, *href = NULL, *etag = NULL, *bytes = NULL;
+       gsize length = -1;
+       gboolean success = FALSE;
+       GError *local_error = NULL;
 
-       contact = e_contact_new_from_vcard (vcards[0]);
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_WEBDAV (meta_backend), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+       g_return_val_if_fail (out_contact != NULL, FALSE);
 
-       orig_uid = e_contact_get_const (contact, E_CONTACT_UID);
-       if (orig_uid && *orig_uid && webdav_can_use_uid (orig_uid) && !e_book_backend_cache_check_contact 
(webdav->priv->cache, orig_uid)) {
-               uid = g_strdup (orig_uid);
-       } else {
-               uid = NULL;
+       bbdav = E_BOOK_BACKEND_WEBDAV (meta_backend);
 
-               do {
-                       g_free (uid);
+       if (extra && *extra) {
+               uri = g_strdup (extra);
 
-                       /* do 3 random() calls to construct a unique ID... poor way but should be
-                        * good enough for us */
-                       uid = g_strdup_printf ("%08X-%08X-%08X", g_random_int (), g_random_int (), 
g_random_int ());
+               success = e_webdav_session_get_data_sync (bbdav->priv->webdav, uri, &href, &etag, &bytes, 
&length, cancellable, &local_error);
 
-               } while (e_book_backend_cache_check_contact (webdav->priv->cache, uid) &&
-                        !g_cancellable_is_cancelled (cancellable));
-
-               e_contact_set (contact, E_CONTACT_UID, uid);
+               if (!success) {
+                       g_free (uri);
+                       uri = NULL;
+               }
        }
 
-       href = g_strconcat (webdav->priv->uri, uid, ".vcf", NULL);
+       if (!success) {
+               uri = ebb_webdav_uid_to_uri (bbdav, uid, ".vcf");
+               g_return_val_if_fail (uri != NULL, FALSE);
+
+               g_clear_error (&local_error);
 
-       /* kill WEBDAV_CONTACT_ETAG field (might have been set by some other backend) */
-       webdav_contact_set_href (contact, NULL);
-       webdav_contact_set_etag (contact, NULL);
+               success = e_webdav_session_get_data_sync (bbdav->priv->webdav, uri, &href, &etag, &bytes, 
&length, cancellable, &local_error);
+               if (!success && !g_cancellable_is_cancelled (cancellable) &&
+                   g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_NOT_FOUND)) {
+                       g_free (uri);
+                       uri = ebb_webdav_uid_to_uri (bbdav, uid, NULL);
 
-       status = upload_contact (webdav, href, contact, &status_reason, cancellable);
-       g_free (href);
+                       if (uri) {
+                               g_clear_error (&local_error);
 
-       if (status != 201 && status != 204) {
-               g_object_unref (contact);
-               if (status == 401 || status == 407) {
-                       webdav_handle_auth_request (webdav, error);
-               } else {
-                       g_set_error (
-                               error, E_CLIENT_ERROR,
-                               E_CLIENT_ERROR_OTHER_ERROR,
-                               _("Create resource ā€œ%sā€ failed with HTTP status %d (%s)"),
-                               uid, status, status_reason);
+                               success = e_webdav_session_get_data_sync (bbdav->priv->webdav, uri, &href, 
&etag, &bytes, &length, cancellable, &local_error);
+                       }
                }
-               g_free (uid);
-               g_free (status_reason);
-               return FALSE;
        }
 
-       g_free (status_reason);
-       g_free (uid);
+       if (success) {
+               *out_contact = NULL;
 
-       /* PUT request didn't return an etag? try downloading to get one */
-       stored_etag = webdav_contact_get_etag (contact);
-       if (!stored_etag) {
-               gchar *href;
-               EContact *new_contact = NULL;
+               if (href && etag && bytes && length != ((gsize) -1)) {
+                       EContact *contact;
 
-               href = webdav_contact_get_href (contact);
-               if (href) {
-                       new_contact = download_contact (webdav, href, cancellable);
-                       g_free (href);
+                       contact = e_contact_new_from_vcard (bytes);
+                       if (contact) {
+                               ebb_webdav_set_contact_etag (contact, etag);
+                               *out_contact = contact;
+                       }
                }
 
-               g_object_unref (contact);
-
-               if (new_contact == NULL) {
-                       g_set_error_literal (
-                               error, E_CLIENT_ERROR,
-                               E_CLIENT_ERROR_OTHER_ERROR,
-                               e_client_error_to_string (
-                               E_CLIENT_ERROR_OTHER_ERROR));
-                       return FALSE;
+               if (!*out_contact) {
+                       success = FALSE;
+                       g_propagate_error (&local_error, EDB_ERROR_EX (E_DATA_BOOK_STATUS_OTHER_ERROR, 
_("Received object is not a valid vCard")));
                }
-               contact = new_contact;
-       } else {
-               g_free (stored_etag);
        }
 
-       g_mutex_lock (&webdav->priv->cache_lock);
-       e_book_backend_cache_add_contact (webdav->priv->cache, contact);
-       g_mutex_unlock (&webdav->priv->cache_lock);
-
-       g_queue_push_tail (out_contacts, g_object_ref (contact));
+       g_free (uri);
+       g_free (href);
+       g_free (etag);
+       g_free (bytes);
 
-       g_object_unref (contact);
+       if (local_error)
+               g_propagate_error (error, local_error);
 
-       return TRUE;
+       return success;
 }
 
 static gboolean
-book_backend_webdav_modify_contacts_sync (EBookBackend *backend,
-                                          const gchar * const *vcards,
-                                          GQueue *out_contacts,
-                                          GCancellable *cancellable,
-                                          GError **error)
+ebb_webdav_save_contact_sync (EBookMetaBackend *meta_backend,
+                             gboolean overwrite_existing,
+                             EConflictResolution conflict_resolution,
+                             /* const */ EContact *contact,
+                             const gchar *extra,
+                             gchar **out_new_uid,
+                             gchar **out_new_extra,
+                             GCancellable *cancellable,
+                             GError **error)
 {
-       EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
-       EContact *contact;
-       const gchar *uid;
-       gchar *href, *etag;
-       guint status;
-       gchar *status_reason = NULL;
-
-       /* We make the assumption that the vCard list we're passed is
-        * always exactly one element long, since we haven't specified
-        * "bulk-modifies" in our static capability list.  This is because
-        * there is no clean way to roll back changes in case of an error. */
-       if (g_strv_length ((gchar **) vcards) > 1) {
-               g_set_error_literal (
-                       error, E_CLIENT_ERROR,
-                       E_CLIENT_ERROR_NOT_SUPPORTED,
-                       _("The backend does not support bulk modifications"));
-               return FALSE;
-       }
+       EBookBackendWebDAV *bbdav;
+       gchar *href = NULL, *etag = NULL, *uid = NULL;
+       gchar *vcard_string = NULL;
+       gboolean success;
 
-       if (!e_backend_get_online (E_BACKEND (backend))) {
-               g_set_error_literal (
-                       error, E_CLIENT_ERROR,
-                       E_CLIENT_ERROR_REPOSITORY_OFFLINE,
-                       e_client_error_to_string (
-                       E_CLIENT_ERROR_REPOSITORY_OFFLINE));
-               return FALSE;
-       }
-
-       /* modify contact */
-       contact = e_contact_new_from_vcard (vcards[0]);
-       href = webdav_contact_get_href (contact);
-       status = upload_contact (webdav, href, contact, &status_reason, cancellable);
-       g_free (href);
-       if (status != 200 && status != 201 && status != 204) {
-               g_object_unref (contact);
-               if (status == 401 || status == 407) {
-                       webdav_handle_auth_request (webdav, error);
-                       g_free (status_reason);
-                       return FALSE;
-               }
-               /* data changed on server while we were editing */
-               if (status == 412) {
-                       /* too bad no special error code in evolution for this... */
-                       g_set_error_literal (
-                               error, E_CLIENT_ERROR,
-                               E_CLIENT_ERROR_OTHER_ERROR,
-                               _("Contact on server changed -> not modifying"));
-                       g_free (status_reason);
-                       return FALSE;
-               }
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_WEBDAV (meta_backend), FALSE);
+       g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
+       g_return_val_if_fail (out_new_uid, FALSE);
+       g_return_val_if_fail (out_new_extra, FALSE);
 
-               g_set_error (
-                       error, E_CLIENT_ERROR,
-                       E_CLIENT_ERROR_OTHER_ERROR,
-                       _("Modify contact failed with HTTP status %d (%s)"),
-                       status, status_reason);
+       bbdav = E_BOOK_BACKEND_WEBDAV (meta_backend);
 
-               g_free (status_reason);
-               return FALSE;
-       }
+       uid = e_contact_get (contact, E_CONTACT_UID);
+       etag = ebb_webdav_dup_contact_etag (contact);
 
-       g_free (status_reason);
+       ebb_webdav_set_contact_etag (contact, NULL);
 
-       uid = e_contact_get_const (contact, E_CONTACT_UID);
-       g_mutex_lock (&webdav->priv->cache_lock);
-       e_book_backend_cache_remove_contact (webdav->priv->cache, uid);
+       vcard_string = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
 
-       etag = webdav_contact_get_etag (contact);
+       if (uid && vcard_string && (!overwrite_existing || (extra && *extra))) {
+               gboolean force_write = FALSE;
 
-       /* PUT request didn't return an etag? try downloading to get one */
-       if (etag == NULL || (etag[0] == 'W' && etag[1] == '/')) {
-               EContact *new_contact = NULL;
+               if (!extra || !*extra)
+                       href = ebb_webdav_uid_to_uri (bbdav, uid, ".vcf");
 
-               href = webdav_contact_get_href (contact);
-               if (href) {
-                       new_contact = download_contact (webdav, href, cancellable);
-                       g_free (href);
+               if (overwrite_existing) {
+                       switch (conflict_resolution) {
+                       case E_CONFLICT_RESOLUTION_FAIL:
+                       case E_CONFLICT_RESOLUTION_USE_NEWER:
+                       case E_CONFLICT_RESOLUTION_KEEP_SERVER:
+                       case E_CONFLICT_RESOLUTION_WRITE_COPY:
+                               break;
+                       case E_CONFLICT_RESOLUTION_KEEP_LOCAL:
+                               force_write = TRUE;
+                               break;
+                       }
                }
 
-               if (new_contact != NULL) {
-                       g_object_unref (contact);
-                       contact = new_contact;
-               }
+               success = e_webdav_session_put_data_sync (bbdav->priv->webdav, (extra && *extra) ? extra : 
href,
+                       force_write ? "" : overwrite_existing ? etag : NULL, E_WEBDAV_CONTENT_TYPE_VCARD,
+                       vcard_string, -1, out_new_extra, NULL, cancellable, error);
+
+               /* To read the component back, because server can change it */
+               if (success)
+                       *out_new_uid = g_strdup (uid);
+       } else {
+               success = FALSE;
+               g_propagate_error (error, EDB_ERROR_EX (E_DATA_BOOK_STATUS_OTHER_ERROR, _("Object to save is 
not a valid vCard")));
        }
 
+       g_free (vcard_string);
+       g_free (href);
        g_free (etag);
+       g_free (uid);
 
-       e_book_backend_cache_add_contact (webdav->priv->cache, contact);
-       g_mutex_unlock (&webdav->priv->cache_lock);
-
-       g_queue_push_tail (out_contacts, g_object_ref (contact));
-
-       g_object_unref (contact);
-
-       return TRUE;
+       return success;
 }
 
 static gboolean
-book_backend_webdav_remove_contacts_sync (EBookBackend *backend,
-                                          const gchar * const *uids,
-                                          GCancellable *cancellable,
-                                          GError **error)
+ebb_webdav_remove_contact_sync (EBookMetaBackend *meta_backend,
+                               EConflictResolution conflict_resolution,
+                               const gchar *uid,
+                               const gchar *extra,
+                               const gchar *object,
+                               GCancellable *cancellable,
+                               GError **error)
 {
-       EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
+       EBookBackendWebDAV *bbdav;
        EContact *contact;
-       gchar *href;
-       guint status;
-
-       /* We make the assumption that the ID list we're passed is
-        * always exactly one element long, since we haven't specified
-        * "bulk-removes" in our static capability list. */
-       if (g_strv_length ((gchar **) uids) > 1) {
-               g_set_error_literal (
-                       error, E_CLIENT_ERROR,
-                       E_CLIENT_ERROR_NOT_SUPPORTED,
-                       _("The backend does not support bulk removals"));
-               return FALSE;
-       }
-
-       if (!e_backend_get_online (E_BACKEND (backend))) {
-               g_set_error_literal (
-                       error, E_CLIENT_ERROR,
-                       E_CLIENT_ERROR_REPOSITORY_OFFLINE,
-                       e_client_error_to_string (
-                       E_CLIENT_ERROR_REPOSITORY_OFFLINE));
-               return FALSE;
-       }
+       gchar *etag = NULL;
+       gboolean success;
+       GError *local_error = NULL;
 
-       g_mutex_lock (&webdav->priv->cache_lock);
-       contact = e_book_backend_cache_get_contact (webdav->priv->cache, uids[0]);
-       g_mutex_unlock (&webdav->priv->cache_lock);
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_WEBDAV (meta_backend), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+       g_return_val_if_fail (object != NULL, FALSE);
 
-       if (!contact) {
-               g_set_error_literal (
-                       error, E_BOOK_CLIENT_ERROR,
-                       E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND,
-                       e_book_client_error_to_string (
-                       E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND));
-               return FALSE;
-       }
+       bbdav = E_BOOK_BACKEND_WEBDAV (meta_backend);
 
-       href = webdav_contact_get_href (contact);
-       if (!href) {
-               g_object_unref (contact);
-               g_set_error (
-                       error, E_CLIENT_ERROR,
-                       E_CLIENT_ERROR_OTHER_ERROR,
-                       _("DELETE failed with HTTP status %d"), SOUP_STATUS_MALFORMED);
+       if (!extra || !*extra) {
+               g_propagate_error (error, EDB_ERROR (E_DATA_BOOK_STATUS_INVALID_ARG));
                return FALSE;
        }
 
-       status = delete_contact (webdav, href, cancellable);
-
-       g_object_unref (contact);
-       g_free (href);
-
-       if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
-               if (status == 401 || status == 407) {
-                       webdav_handle_auth_request (webdav, error);
-               } else {
-                       g_set_error (
-                               error, E_CLIENT_ERROR,
-                               E_CLIENT_ERROR_OTHER_ERROR,
-                               _("DELETE failed with HTTP status %d"), status);
-               }
+       contact = e_contact_new_from_vcard (object);
+       if (!contact) {
+               g_propagate_error (error, EDB_ERROR (E_DATA_BOOK_STATUS_INVALID_ARG));
                return FALSE;
        }
 
-       g_mutex_lock (&webdav->priv->cache_lock);
-       e_book_backend_cache_remove_contact (webdav->priv->cache, uids[0]);
-       g_mutex_unlock (&webdav->priv->cache_lock);
-
-       return TRUE;
-}
-
-static EContact *
-book_backend_webdav_get_contact_sync (EBookBackend *backend,
-                                      const gchar *uid,
-                                      GCancellable *cancellable,
-                                      GError **error)
-{
-       EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
-       EContact *contact;
+       if (conflict_resolution == E_CONFLICT_RESOLUTION_FAIL)
+               etag = ebb_webdav_dup_contact_etag (contact);
 
-       g_mutex_lock (&webdav->priv->cache_lock);
-       contact = e_book_backend_cache_get_contact (
-               webdav->priv->cache, uid);
-       g_mutex_unlock (&webdav->priv->cache_lock);
+       success = e_webdav_session_delete_sync (bbdav->priv->webdav, extra,
+               E_WEBDAV_DEPTH_THIS, etag, cancellable, &local_error);
 
-       if (contact && e_backend_get_online (E_BACKEND (backend))) {
+       if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_NOT_FOUND)) {
                gchar *href;
 
-               href = webdav_contact_get_href (contact);
-               g_object_unref (contact);
-
+               href = ebb_webdav_uid_to_uri (bbdav, uid, ".vcf");
                if (href) {
-                       contact = download_contact (webdav, href, cancellable);
+                       g_clear_error (&local_error);
+                       success = e_webdav_session_delete_sync (bbdav->priv->webdav, href,
+                               E_WEBDAV_DEPTH_THIS, etag, cancellable, &local_error);
+
                        g_free (href);
-               } else {
-                       contact = NULL;
                }
 
-               /* update cache as we possibly have changes */
-               if (contact != NULL) {
-                       g_mutex_lock (&webdav->priv->cache_lock);
-                       e_book_backend_cache_remove_contact (
-                               webdav->priv->cache, uid);
-                       e_book_backend_cache_add_contact (
-                               webdav->priv->cache, contact);
-                       g_mutex_unlock (&webdav->priv->cache_lock);
+               if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_NOT_FOUND)) {
+                       href = ebb_webdav_uid_to_uri (bbdav, uid, NULL);
+                       if (href) {
+                               g_clear_error (&local_error);
+                               success = e_webdav_session_delete_sync (bbdav->priv->webdav, href,
+                                       E_WEBDAV_DEPTH_THIS, etag, cancellable, &local_error);
+
+                               g_free (href);
+                       }
                }
        }
 
-       if (contact == NULL) {
-               g_set_error_literal (
-                       error, E_BOOK_CLIENT_ERROR,
-                       E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND,
-                       e_book_client_error_to_string (
-                       E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND));
-               return FALSE;
-       }
+       g_object_unref (contact);
+       g_free (etag);
 
-       return contact;
+       if (local_error)
+               g_propagate_error (error, local_error);
+
+       return success;
 }
 
-static gboolean
-book_backend_webdav_get_contact_list_sync (EBookBackend *backend,
-                                           const gchar *query,
-                                           GQueue *out_contacts,
-                                           GCancellable *cancellable,
-                                           GError **error)
+static gchar *
+ebb_webdav_get_backend_property (EBookBackend *book_backend,
+                                const gchar *prop_name)
 {
-       EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
-       GList *contact_list;
-
-       if (e_backend_get_online (E_BACKEND (backend)) &&
-           e_source_get_connection_status (e_backend_get_source (E_BACKEND (backend))) == 
E_SOURCE_CONNECTION_STATUS_CONNECTED) {
-               /* make sure the cache is up to date */
-               if (!download_contacts (webdav, NULL, NULL, FALSE, cancellable, error))
-                       return FALSE;
-       }
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_WEBDAV (book_backend), NULL);
+       g_return_val_if_fail (prop_name != NULL, NULL);
 
-       /* answer query from cache */
-       g_mutex_lock (&webdav->priv->cache_lock);
-       contact_list = e_book_backend_cache_get_contacts (
-               webdav->priv->cache, query);
-       g_mutex_unlock (&webdav->priv->cache_lock);
-
-       /* This appends contact_list to out_contacts, one element at a
-        * time, since GLib lacks something like g_queue_append_list().
-        *
-        * XXX Would be better if e_book_backend_cache_get_contacts()
-        *     took an output GQueue instead of returning a GList. */
-       while (contact_list != NULL) {
-               GList *link = contact_list;
-               contact_list = g_list_remove_link (contact_list, link);
-               g_queue_push_tail_link (out_contacts, link);
+       if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
+               return g_strjoin (",",
+                       "net",
+                       "do-initial-query",
+                       "contact-lists",
+                       e_book_meta_backend_get_capabilities (E_BOOK_META_BACKEND (book_backend)),
+                       NULL);
        }
 
-       return TRUE;
+       /* Chain up to parent's method. */
+       return E_BOOK_BACKEND_CLASS (e_book_backend_webdav_parent_class)->get_backend_property (book_backend, 
prop_name);
 }
 
-static ESourceAuthenticationResult
-book_backend_webdav_authenticate_sync (EBackend *backend,
-                                      const ENamedParameters *credentials,
-                                      gchar **out_certificate_pem,
-                                      GTlsCertificateFlags *out_certificate_errors,
-                                      GCancellable *cancellable,
-                                      GError **error)
+static gchar *
+ebb_webdav_dup_contact_revision_cb (EBookCache *book_cache,
+                                   EContact *contact)
 {
-       EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
-       ESourceAuthentication *auth_extension;
-       ESourceAuthenticationResult result;
-       ESource *source;
-       const gchar *username;
-       GError *local_error = NULL;
+       g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
 
-       source = e_backend_get_source (backend);
-       auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+       return ebb_webdav_dup_contact_etag (contact);
+}
 
-       g_free (webdav->priv->username);
-       webdav->priv->username = NULL;
+static void
+e_book_backend_webdav_constructed (GObject *object)
+{
+       EBookBackendWebDAV *bbdav = E_BOOK_BACKEND_WEBDAV (object);
+       EBookCache *book_cache;
 
-       g_free (webdav->priv->password);
-       webdav->priv->password = g_strdup (e_named_parameters_get (credentials, 
E_SOURCE_CREDENTIAL_PASSWORD));
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_book_backend_webdav_parent_class)->constructed (object);
 
-       username = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME);
-       if (username && *username) {
-               webdav->priv->username = g_strdup (username);
-       } else {
-               webdav->priv->username = e_source_authentication_dup_user (auth_extension);
-       }
+       book_cache = e_book_meta_backend_ref_cache (E_BOOK_META_BACKEND (bbdav));
 
-       if (book_backend_webdav_test_can_connect (webdav, out_certificate_pem, out_certificate_errors, 
cancellable, &local_error)) {
-               result = E_SOURCE_AUTHENTICATION_ACCEPTED;
-       } else if (g_error_matches (local_error, E_CLIENT_ERROR, E_CLIENT_ERROR_AUTHENTICATION_FAILED) ||
-                  g_error_matches (local_error, E_CLIENT_ERROR, E_CLIENT_ERROR_AUTHENTICATION_REQUIRED)) {
-               if (!e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD) ||
-                   g_error_matches (local_error, E_CLIENT_ERROR, E_CLIENT_ERROR_AUTHENTICATION_REQUIRED))
-                       result = E_SOURCE_AUTHENTICATION_REQUIRED;
-               else
-                       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_signal_connect (book_cache, "dup-contact-revision",
+               G_CALLBACK (ebb_webdav_dup_contact_revision_cb), NULL);
 
-       return result;
+       g_clear_object (&book_cache);
 }
 
-static gboolean
-e_book_backend_webdav_refresh_sync (EBookBackend *book_backend,
-                                   GCancellable *cancellable,
-                                   GError **error)
+static void
+e_book_backend_webdav_dispose (GObject *object)
 {
-       EBackend *backend;
-
-       g_return_val_if_fail (E_IS_BOOK_BACKEND_WEBDAV (book_backend), FALSE);
+       EBookBackendWebDAV *bbdav = E_BOOK_BACKEND_WEBDAV (object);
 
-       backend = E_BACKEND (book_backend);
+       g_clear_object (&bbdav->priv->webdav);
 
-       if (!e_backend_get_online (backend) &&
-           e_backend_is_destination_reachable (backend, cancellable, NULL)) {
-               e_backend_set_online (backend, TRUE);
-       }
-
-       if (e_backend_get_online (backend) && !g_cancellable_is_cancelled (cancellable)) {
-               return download_contacts (E_BOOK_BACKEND_WEBDAV (book_backend), NULL, NULL, TRUE, 
cancellable, error);
-       }
-
-       return TRUE;
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_book_backend_webdav_parent_class)->dispose (object);
 }
 
 static void
-e_book_backend_webdav_class_init (EBookBackendWebdavClass *class)
+e_book_backend_webdav_init (EBookBackendWebDAV *bbdav)
 {
-       GObjectClass *object_class;
-       EBackendClass *backend_class;
-       EBookBackendClass *book_backend_class;
-
-       g_type_class_add_private (class, sizeof (EBookBackendWebdavPrivate));
-
-       object_class = G_OBJECT_CLASS (class);
-       object_class->dispose = book_backend_webdav_dispose;
-       object_class->finalize = book_backend_webdav_finalize;
-
-       backend_class = E_BACKEND_CLASS (class);
-       backend_class->authenticate_sync = book_backend_webdav_authenticate_sync;
-
-       book_backend_class = E_BOOK_BACKEND_CLASS (class);
-       book_backend_class->get_backend_property = book_backend_webdav_get_backend_property;
-       book_backend_class->open_sync = book_backend_webdav_open_sync;
-       book_backend_class->create_contacts_sync = book_backend_webdav_create_contacts_sync;
-       book_backend_class->modify_contacts_sync = book_backend_webdav_modify_contacts_sync;
-       book_backend_class->remove_contacts_sync = book_backend_webdav_remove_contacts_sync;
-       book_backend_class->get_contact_sync = book_backend_webdav_get_contact_sync;
-       book_backend_class->get_contact_list_sync = book_backend_webdav_get_contact_list_sync;
-       book_backend_class->start_view = e_book_backend_webdav_start_view;
-       book_backend_class->stop_view = e_book_backend_webdav_stop_view;
-       book_backend_class->refresh_sync = e_book_backend_webdav_refresh_sync;
+       bbdav->priv = G_TYPE_INSTANCE_GET_PRIVATE (bbdav, E_TYPE_BOOK_BACKEND_WEBDAV, 
EBookBackendWebDAVPrivate);
 }
 
 static void
-e_book_backend_webdav_init (EBookBackendWebdav *backend)
+e_book_backend_webdav_class_init (EBookBackendWebDAVClass *klass)
 {
-       backend->priv = E_BOOK_BACKEND_WEBDAV_GET_PRIVATE (backend);
-
-       g_mutex_init (&backend->priv->cache_lock);
-       g_mutex_init (&backend->priv->update_lock);
-
-       g_signal_connect (
-               backend, "notify::online",
-               G_CALLBACK (e_book_backend_webdav_notify_online_cb), NULL);
+       GObjectClass *object_class;
+       EBookBackendClass *book_backend_class;
+       EBookMetaBackendClass *book_meta_backend_class;
+
+       g_type_class_add_private (klass, sizeof (EBookBackendWebDAVPrivate));
+
+       book_meta_backend_class = E_BOOK_META_BACKEND_CLASS (klass);
+       book_meta_backend_class->backend_module_filename = "libebookbackendwebdav.so";
+       book_meta_backend_class->backend_factory_type_name = "EBookBackendWebdavFactory";
+       book_meta_backend_class->connect_sync = ebb_webdav_connect_sync;
+       book_meta_backend_class->disconnect_sync = ebb_webdav_disconnect_sync;
+       book_meta_backend_class->get_changes_sync = ebb_webdav_get_changes_sync;
+       book_meta_backend_class->list_existing_sync = ebb_webdav_list_existing_sync;
+       book_meta_backend_class->load_contact_sync = ebb_webdav_load_contact_sync;
+       book_meta_backend_class->save_contact_sync = ebb_webdav_save_contact_sync;
+       book_meta_backend_class->remove_contact_sync = ebb_webdav_remove_contact_sync;
+
+       book_backend_class = E_BOOK_BACKEND_CLASS (klass);
+       book_backend_class->get_backend_property = ebb_webdav_get_backend_property;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->constructed = e_book_backend_webdav_constructed;
+       object_class->dispose = e_book_backend_webdav_dispose;
 }
-
diff --git a/src/addressbook/backends/webdav/e-book-backend-webdav.h 
b/src/addressbook/backends/webdav/e-book-backend-webdav.h
index e7b1ed0..93162fb 100644
--- a/src/addressbook/backends/webdav/e-book-backend-webdav.h
+++ b/src/addressbook/backends/webdav/e-book-backend-webdav.h
@@ -27,10 +27,10 @@
        (e_book_backend_webdav_get_type ())
 #define E_BOOK_BACKEND_WEBDAV(obj) \
        (G_TYPE_CHECK_INSTANCE_CAST \
-       ((obj), E_TYPE_BOOK_BACKEND_WEBDAV, EBookBackendWebdav))
+       ((obj), E_TYPE_BOOK_BACKEND_WEBDAV, EBookBackendWebDAV))
 #define E_BOOK_BACKEND_WEBDAV_CLASS(cls) \
        (G_TYPE_CHECK_CLASS_CAST \
-       ((cls), E_TYPE_BOOK_BACKEND_WEBDAV, EBookBackendWebdavClass))
+       ((cls), E_TYPE_BOOK_BACKEND_WEBDAV, EBookBackendWebDAVClass))
 #define E_IS_BOOK_BACKEND_WEBDAV(obj) \
        (G_TYPE_CHECK_INSTANCE_TYPE \
        ((obj), E_TYPE_BOOK_BACKEND_WEBDAV))
@@ -39,21 +39,21 @@
        ((cls), E_TYPE_BOOK_BACKEND_WEBDAV))
 #define E_BOOK_BACKEND_WEBDAV_GET_CLASS(cls) \
        (G_TYPE_INSTANCE_GET_CLASS \
-       ((obj), E_TYPE_BOOK_BACKEND_WEBDAV, EBookBackendWebdavClass))
+       ((obj), E_TYPE_BOOK_BACKEND_WEBDAV, EBookBackendWebDAVClass))
 
 G_BEGIN_DECLS
 
-typedef struct _EBookBackendWebdav EBookBackendWebdav;
-typedef struct _EBookBackendWebdavClass EBookBackendWebdavClass;
-typedef struct _EBookBackendWebdavPrivate EBookBackendWebdavPrivate;
+typedef struct _EBookBackendWebDAV EBookBackendWebDAV;
+typedef struct _EBookBackendWebDAVClass EBookBackendWebDAVClass;
+typedef struct _EBookBackendWebDAVPrivate EBookBackendWebDAVPrivate;
 
-struct _EBookBackendWebdav {
-       EBookBackend parent;
-       EBookBackendWebdavPrivate *priv;
+struct _EBookBackendWebDAV {
+       EBookMetaBackend parent;
+       EBookBackendWebDAVPrivate *priv;
 };
 
-struct _EBookBackendWebdavClass {
-       EBookBackendClass parent_class;
+struct _EBookBackendWebDAVClass {
+       EBookMetaBackendClass parent_class;
 };
 
 GType          e_book_backend_webdav_get_type  (void);
@@ -61,4 +61,3 @@ GType         e_book_backend_webdav_get_type  (void);
 G_END_DECLS
 
 #endif /* E_BOOK_BACKEND_WEBDAV_H */
-
diff --git a/src/addressbook/libedata-book/e-book-cache.c b/src/addressbook/libedata-book/e-book-cache.c
index 49f809c..8b1916a 100644
--- a/src/addressbook/libedata-book/e-book-cache.c
+++ b/src/addressbook/libedata-book/e-book-cache.c
@@ -131,6 +131,7 @@ enum {
 
 enum {
        E164_CHANGED,
+       DUP_CONTACT_REVISION,
        LAST_SIGNAL
 };
 
@@ -4592,6 +4593,34 @@ e_book_cache_ref_source (EBookCache *book_cache)
 }
 
 /**
+ * e_book_cache_dup_contact_revision:
+ * @book_cache: an #EBookCache
+ * @contact: an #EContact
+ *
+ * Returns the @contact revision, used to detect changes.
+ * The returned string should be freed with g_free(), when
+ * no longer needed.
+ *
+ * Returns: (transfer full): A newly allocated string containing
+ *    revision of the @contact.
+ *
+ * Since: 3.26
+ **/
+gchar *
+e_book_cache_dup_contact_revision (EBookCache *book_cache,
+                                  EContact *contact)
+{
+       gchar *revision = NULL;
+
+       g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), NULL);
+       g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
+
+       g_signal_emit (book_cache, signals[DUP_CONTACT_REVISION], 0, contact, &revision);
+
+       return revision;
+}
+
+/**
  * e_book_cache_set_locale:
  * @book_cache: An #EBookCache
  * @lc_collate: The new locale for the cache
@@ -4790,7 +4819,7 @@ e_book_cache_put_contacts (EBookCache *book_cache,
                        e_cache_column_values_take_value (other_columns, EBC_COLUMN_EXTRA, g_strdup (extra));
 
                uid = e_contact_get (contact, E_CONTACT_UID);
-               rev = e_contact_get (contact, E_CONTACT_REV);
+               rev = e_book_cache_dup_contact_revision (book_cache, contact);
 
                ebc_fill_other_columns (book_cache, contact, other_columns);
 
@@ -5859,6 +5888,15 @@ e_book_cache_cursor_compare_contact (EBookCache *book_cache,
        return comparison;
 }
 
+static gchar *
+ebc_dup_contact_revision (EBookCache *book_cache,
+                         EContact *contact)
+{
+       g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
+
+       return e_contact_get (contact, E_CONTACT_REV);
+}
+
 static gboolean
 e_book_cache_put_locked (ECache *cache,
                         const gchar *uid,
@@ -6022,6 +6060,8 @@ e_book_cache_class_init (EBookCacheClass *klass)
        cache_class->remove_all_locked = e_book_cache_remove_all_locked;
        cache_class->clear_offline_changes_locked = e_book_cache_clear_offline_changes_locked;
 
+       klass->dup_contact_revision = ebc_dup_contact_revision;
+
        g_object_class_install_property (
                object_class,
                PROP_LOCALE,
@@ -6044,6 +6084,22 @@ e_book_cache_class_init (EBookCacheClass *klass)
                G_TYPE_NONE, 2,
                E_TYPE_CONTACT,
                G_TYPE_BOOLEAN);
+
+       /**
+        * @EBookCache:dup-contact-revision:
+        * A signal being called to get revision of an EContact.
+        * The default implementation returns E_CONTACT_REV field value.
+        **/
+       signals[DUP_CONTACT_REVISION] = g_signal_new (
+               "dup-contact-revision",
+               G_OBJECT_CLASS_TYPE (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EBookCacheClass, dup_contact_revision),
+               g_signal_accumulator_first_wins,
+               NULL,
+               g_cclosure_marshal_generic,
+               G_TYPE_STRING, 1,
+               E_TYPE_CONTACT);
 }
 
 static void
diff --git a/src/addressbook/libedata-book/e-book-cache.h b/src/addressbook/libedata-book/e-book-cache.h
index e36b86c..5e13799 100644
--- a/src/addressbook/libedata-book/e-book-cache.h
+++ b/src/addressbook/libedata-book/e-book-cache.h
@@ -139,9 +139,13 @@ struct _EBookCacheClass {
        ECacheClass parent_class;
 
        /* Signals */
-       void    (* e164_changed)        (EBookCache *book_cache,
-                                        EContact *contact,
-                                        gboolean is_replace);
+       void            (* e164_changed)        (EBookCache *book_cache,
+                                                EContact *contact,
+                                                gboolean is_replace);
+
+       gchar *         (* dup_contact_revision)
+                                               (EBookCache *book_cache,
+                                                EContact *contact);
 
        /* Padding for future expansion */
        gpointer reserved[10];
@@ -202,6 +206,9 @@ EBookCache *        e_book_cache_new_full           (const gchar *filename,
                                                 GCancellable *cancellable,
                                                 GError **error);
 ESource *      e_book_cache_ref_source         (EBookCache *book_cache);
+gchar *                e_book_cache_dup_contact_revision
+                                               (EBookCache *book_cache,
+                                                EContact *contact);
 gboolean       e_book_cache_set_locale         (EBookCache *book_cache,
                                                 const gchar *lc_collate,
                                                 GCancellable *cancellable,
diff --git a/src/addressbook/libedata-book/e-book-meta-backend.c 
b/src/addressbook/libedata-book/e-book-meta-backend.c
index b0ebbcc..ee79232 100644
--- a/src/addressbook/libedata-book/e-book-meta-backend.c
+++ b/src/addressbook/libedata-book/e-book-meta-backend.c
@@ -108,6 +108,7 @@ static gboolean ebmb_load_contact_wrapper_sync (EBookMetaBackend *meta_backend,
                                                const gchar *uid,
                                                const gchar *preloaded_object,
                                                const gchar *preloaded_extra,
+                                               gchar **out_new_uid,
                                                GCancellable *cancellable,
                                                GError **error);
 static gboolean ebmb_save_contact_wrapper_sync (EBookMetaBackend *meta_backend,
@@ -902,7 +903,7 @@ ebmb_refresh_thread_func (EBookBackend *book_backend,
                goto done;
        }
 
-       success = ebmb_upload_local_changes_sync (meta_backend, book_cache, E_CONFLICT_RESOLUTION_KEEP_LOCAL, 
cancellable, error);
+       success = ebmb_upload_local_changes_sync (meta_backend, book_cache, E_CONFLICT_RESOLUTION_FAIL, 
cancellable, error);
 
        while (repeat && success &&
               !g_cancellable_set_error_if_cancelled (cancellable, error)) {
@@ -947,12 +948,13 @@ ebmb_refresh_thread_func (EBookBackend *book_backend,
                                        continue;
                                }
 
-                               if (g_hash_table_contains (covered_uids, nfo->uid))
+                               if (!*nfo->uid ||
+                                   g_hash_table_contains (covered_uids, nfo->uid))
                                        continue;
 
                                g_hash_table_insert (covered_uids, nfo->uid, NULL);
 
-                               success = ebmb_load_contact_wrapper_sync (meta_backend, book_cache, nfo->uid, 
nfo->object, nfo->extra, cancellable, &local_error);
+                               success = ebmb_load_contact_wrapper_sync (meta_backend, book_cache, nfo->uid, 
nfo->object, nfo->extra, NULL, cancellable, &local_error);
 
                                /* Do not stop on invalid objects, just notify about them later, and load as 
many as possible */
                                if (!success && g_error_matches (local_error, E_DATA_BOOK_ERROR, 
E_DATA_BOOK_STATUS_INVALID_ARG)) {
@@ -981,7 +983,10 @@ ebmb_refresh_thread_func (EBookBackend *book_backend,
                                        continue;
                                }
 
-                               success = ebmb_load_contact_wrapper_sync (meta_backend, book_cache, nfo->uid, 
nfo->object, nfo->extra, cancellable, &local_error);
+                               if (!*nfo->uid)
+                                       continue;
+
+                               success = ebmb_load_contact_wrapper_sync (meta_backend, book_cache, nfo->uid, 
nfo->object, nfo->extra, NULL, cancellable, &local_error);
 
                                /* Do not stop on invalid objects, just notify about them later, and load as 
many as possible */
                                if (!success && g_error_matches (local_error, E_DATA_BOOK_ERROR, 
E_DATA_BOOK_STATUS_INVALID_ARG)) {
@@ -1182,6 +1187,7 @@ ebmb_load_contact_wrapper_sync (EBookMetaBackend *meta_backend,
                                const gchar *uid,
                                const gchar *preloaded_object,
                                const gchar *preloaded_extra,
+                               gchar **out_new_uid,
                                GCancellable *cancellable,
                                GError **error)
 {
@@ -1208,6 +1214,9 @@ ebmb_load_contact_wrapper_sync (EBookMetaBackend *meta_backend,
        success = ebmb_put_contact (meta_backend, book_cache, offline_flag,
                contact, extra ? extra : preloaded_extra, cancellable, error);
 
+       if (success && out_new_uid)
+               *out_new_uid = e_contact_get (contact, E_CONTACT_UID);
+
        g_object_unref (contact);
        g_free (extra);
 
@@ -1245,22 +1254,26 @@ ebmb_save_contact_wrapper_sync (EBookMetaBackend *meta_backend,
        success = success && e_book_meta_backend_save_contact_sync (meta_backend, overwrite_existing, 
conflict_resolution,
                contact, extra, &new_uid, &new_extra, cancellable, error);
 
-       if (success && new_uid) {
+       if (success && new_uid && *new_uid) {
+               gchar *loaded_uid = NULL;
+
                success = ebmb_load_contact_wrapper_sync (meta_backend, book_cache, new_uid, NULL,
-                       new_extra ? new_extra : extra, cancellable, error);
+                       new_extra ? new_extra : extra, &loaded_uid, cancellable, error);
 
-               if (success && g_strcmp0 (new_uid, orig_uid) != 0)
+               if (success && g_strcmp0 (loaded_uid, orig_uid) != 0)
                        success = ebmb_maybe_remove_from_cache (meta_backend, book_cache, E_CACHE_IS_ONLINE, 
orig_uid, cancellable, error);
 
                if (success && out_new_uid)
-                       *out_new_uid = new_uid;
+                       *out_new_uid = loaded_uid;
                else
-                       g_free (new_uid);
+                       g_free (loaded_uid);
 
                if (out_requires_put)
                        *out_requires_put = FALSE;
        }
 
+       g_free (new_uid);
+
        if (success && out_new_extra)
                *out_new_extra = new_extra;
        else
@@ -1292,6 +1305,22 @@ ebmb_get_backend_property (EBookBackend *book_backend,
                return revision;
        } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
                return g_strdup (e_book_meta_backend_get_capabilities (E_BOOK_META_BACKEND (book_backend)));
+       } else  if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
+               return g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
+       } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
+               GString *fields;
+               gint ii;
+
+               fields = g_string_sized_new (1024);
+
+               /* Claim to support everything by default */
+               for (ii = 1; ii < E_CONTACT_FIELD_LAST; ii++) {
+                       if (fields->len > 0)
+                               g_string_append_c (fields, ',');
+                       g_string_append (fields, e_contact_field_name (ii));
+               }
+
+               return g_string_free (fields, FALSE);
        }
 
        /* Chain up to parent's method. */
@@ -1336,8 +1365,13 @@ ebmb_open_sync (EBookBackend *book_backend,
                e_book_backend_set_writable (E_BOOK_BACKEND (meta_backend),
                        e_book_meta_backend_get_connected_writable (meta_backend));
        } else {
-               if (!ebmb_connect_wrapper_sync (meta_backend, cancellable, error))
+               if (!ebmb_connect_wrapper_sync (meta_backend, cancellable, error)) {
+                       g_mutex_lock (&meta_backend->priv->property_lock);
+                       meta_backend->priv->refresh_after_authenticate = TRUE;
+                       g_mutex_unlock (&meta_backend->priv->property_lock);
+
                        return FALSE;
+               }
        }
 
        ebmb_schedule_refresh (E_BOOK_META_BACKEND (book_backend));
@@ -1491,7 +1525,7 @@ ebmb_create_contact_sync (EBookMetaBackend *meta_backend,
                if (out_new_contact) {
                        if (new_uid) {
                                if (!e_book_cache_get_contact (book_cache, new_uid, FALSE, out_new_contact, 
cancellable, NULL))
-                                       *out_new_contact = NULL;
+                                       *out_new_contact = g_object_ref (contact);
                        } else {
                                *out_new_contact = g_object_ref (contact);
                        }
@@ -1514,7 +1548,7 @@ ebmb_create_contacts_sync (EBookBackend *book_backend,
        EBookMetaBackend *meta_backend;
        EBookCache *book_cache;
        ECacheOfflineFlag offline_flag = E_CACHE_OFFLINE_UNKNOWN;
-       EConflictResolution conflict_resolution = E_CONFLICT_RESOLUTION_KEEP_LOCAL;
+       EConflictResolution conflict_resolution = E_CONFLICT_RESOLUTION_FAIL;
        gint ii;
        gboolean success = TRUE;
 
@@ -1650,7 +1684,7 @@ ebmb_modify_contacts_sync (EBookBackend *book_backend,
        EBookMetaBackend *meta_backend;
        EBookCache *book_cache;
        ECacheOfflineFlag offline_flag = E_CACHE_OFFLINE_UNKNOWN;
-       EConflictResolution conflict_resolution = E_CONFLICT_RESOLUTION_KEEP_LOCAL;
+       EConflictResolution conflict_resolution = E_CONFLICT_RESOLUTION_FAIL;
        gint ii;
        gboolean success = TRUE;
 
@@ -1772,7 +1806,7 @@ ebmb_remove_contacts_sync (EBookBackend *book_backend,
        EBookMetaBackend *meta_backend;
        EBookCache *book_cache;
        ECacheOfflineFlag offline_flag = E_CACHE_OFFLINE_UNKNOWN;
-       EConflictResolution conflict_resolution = E_CONFLICT_RESOLUTION_KEEP_LOCAL;
+       EConflictResolution conflict_resolution = E_CONFLICT_RESOLUTION_FAIL;
        gint ii;
        gboolean success = TRUE;
 
@@ -1822,7 +1856,7 @@ ebmb_get_contact_sync (EBookBackend *book_backend,
        GError *local_error = NULL;
 
        g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), NULL);
-       g_return_val_if_fail (uid != NULL, NULL);
+       g_return_val_if_fail (uid && *uid, NULL);
 
        meta_backend = E_BOOK_META_BACKEND (book_backend);
        book_cache = e_book_meta_backend_ref_cache (meta_backend);
@@ -1831,6 +1865,7 @@ ebmb_get_contact_sync (EBookBackend *book_backend,
 
        if (!e_book_cache_get_contact (book_cache, uid, FALSE, &contact, cancellable, &local_error) &&
            g_error_matches (local_error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND)) {
+               gchar *loaded_uid = NULL;
                gboolean found = FALSE;
 
                g_clear_error (&local_error);
@@ -1838,12 +1873,14 @@ ebmb_get_contact_sync (EBookBackend *book_backend,
                /* Ignore errors here, just try whether it's on the remote side, but not in the local cache */
                if (e_backend_get_online (E_BACKEND (meta_backend)) &&
                    ebmb_connect_wrapper_sync (meta_backend, cancellable, NULL) &&
-                   ebmb_load_contact_wrapper_sync (meta_backend, book_cache, uid, NULL, NULL, cancellable, 
NULL)) {
-                       found = e_book_cache_get_contact (book_cache, uid, FALSE, &contact, cancellable, 
NULL);
+                   ebmb_load_contact_wrapper_sync (meta_backend, book_cache, uid, NULL, NULL, &loaded_uid, 
cancellable, NULL)) {
+                       found = e_book_cache_get_contact (book_cache, loaded_uid, FALSE, &contact, 
cancellable, NULL);
                }
 
                if (!found)
                        g_propagate_error (error, e_data_book_create_error 
(E_DATA_BOOK_STATUS_CONTACT_NOT_FOUND, NULL));
+
+               g_free (loaded_uid);
        } else if (local_error) {
                g_propagate_error (error, e_data_book_create_error (E_DATA_BOOK_STATUS_OTHER_ERROR, 
local_error->message));
                g_clear_error (&local_error);
diff --git a/src/calendar/backends/caldav/e-cal-backend-caldav.c 
b/src/calendar/backends/caldav/e-cal-backend-caldav.c
index 38b9594..f3800b7 100644
--- a/src/calendar/backends/caldav/e-cal-backend-caldav.c
+++ b/src/calendar/backends/caldav/e-cal-backend-caldav.c
@@ -26,10 +26,6 @@
 
 #include "e-cal-backend-caldav.h"
 
-#define E_CAL_BACKEND_CALDAV_GET_PRIVATE(obj) \
-       (G_TYPE_INSTANCE_GET_PRIVATE \
-       ((obj), E_TYPE_CAL_BACKEND_CALDAV, ECalBackendCalDAVPrivate))
-
 #define E_CALDAV_MAX_MULTIGET_AMOUNT 100 /* what's the maximum count of items to fetch within a multiget 
request */
 
 #define E_CALDAV_X_ETAG "X-EVOLUTION-CALDAV-ETAG"
@@ -37,9 +33,8 @@
 #define EDC_ERROR(_code) e_data_cal_create_error (_code, NULL)
 #define EDC_ERROR_EX(_code, _msg) e_data_cal_create_error (_code, _msg)
 
-/* Private part of the ECalBackendHttp structure */
 struct _ECalBackendCalDAVPrivate {
-       /* The main soup session  */
+       /* The main WebDAV session  */
        EWebDAVSession *webdav;
 
        /* support for 'getctag' extension */
@@ -155,6 +150,7 @@ ecb_caldav_connect_sync (ECalMetaBackend *meta_backend,
                g_clear_object (&cal_cache);
                soup_uri_free (soup_uri);
        }
+
        if (success) {
                gchar *ctag = NULL;
 
@@ -447,9 +443,20 @@ ecb_caldav_multiget_from_sets_sync (ECalBackendCalDAV *cbdav,
                        if (!success)
                                break;
                } else {
+                       SoupURI *suri;
+                       gchar *path = NULL;
+
+                       suri = soup_uri_new (nfo->extra);
+                       if (suri) {
+                               path = soup_uri_to_string (suri, TRUE);
+                               soup_uri_free (suri);
+                       }
+
                        e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "href");
-                       e_xml_document_write_string (xml, nfo->extra);
+                       e_xml_document_write_string (xml, path ? path : nfo->extra);
                        e_xml_document_end_element (xml); /* href */
+
+                       g_free (path);
                }
        }
 
@@ -491,11 +498,13 @@ ecb_caldav_get_calendar_items_cb (EWebDAVSession *webdav,
                g_return_val_if_fail (href != NULL, FALSE);
 
                /* Skip collection resource, if returned by the server (like iCloud.com does) */
-               if (request_uri && request_uri->path && g_str_has_suffix (href, request_uri->path))
+               if (g_str_has_suffix (href, "/") ||
+                   (request_uri && request_uri->path && g_str_has_suffix (href, request_uri->path)))
                        return TRUE;
 
                etag = e_webdav_session_util_maybe_dequote (e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/D:getetag", xpath_prop_prefix));
-               g_return_val_if_fail (etag != NULL, FALSE);
+               /* Return 'TRUE' to not stop on faulty data from the server */
+               g_return_val_if_fail (etag != NULL, TRUE);
 
                /* UID is unknown at this moment */
                nfo = e_cal_meta_backend_info_new ("", etag, NULL, href);
@@ -598,7 +607,7 @@ ecb_caldav_get_changes_sync (ECalMetaBackend *meta_backend,
                success = e_webdav_session_getctag_sync (cbdav->priv->webdav, NULL, &new_sync_tag, 
cancellable, NULL);
                if (!success) {
                        cbdav->priv->ctag_supported = g_cancellable_set_error_if_cancelled (cancellable, 
error);
-                       if (cbdav->priv->ctag_supported)
+                       if (cbdav->priv->ctag_supported || !cbdav->priv->webdav)
                                return FALSE;
                } else if (!is_repeat && new_sync_tag && last_sync_tag && g_strcmp0 (last_sync_tag, 
new_sync_tag) == 0) {
                        *out_new_sync_tag = new_sync_tag;
@@ -874,6 +883,9 @@ ecb_caldav_uid_to_uri (ECalBackendCalDAV *cbdav,
                        *slash = '\0';
        }
 
+       soup_uri_set_user (soup_uri, NULL);
+       soup_uri_set_password (soup_uri, NULL);
+
        tmp = g_strconcat (soup_uri->path && *soup_uri->path ? soup_uri->path : "", "/", filename, NULL);
        soup_uri_set_path (soup_uri, tmp);
        g_free (tmp);
@@ -899,6 +911,7 @@ ecb_caldav_load_component_sync (ECalMetaBackend *meta_backend,
        gchar *uri = NULL, *href = NULL, *etag = NULL, *bytes = NULL;
        gsize length = -1;
        gboolean success = FALSE;
+       GError *local_error = NULL;
 
        g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
        g_return_val_if_fail (uid != NULL, FALSE);
@@ -909,7 +922,7 @@ ecb_caldav_load_component_sync (ECalMetaBackend *meta_backend,
        if (extra && *extra) {
                uri = g_strdup (extra);
 
-               success = e_webdav_session_get_data_sync (cbdav->priv->webdav, uri, &href, &etag, &bytes, 
&length, cancellable, error);
+               success = e_webdav_session_get_data_sync (cbdav->priv->webdav, uri, &href, &etag, &bytes, 
&length, cancellable, &local_error);
 
                if (!success) {
                        g_free (uri);
@@ -918,11 +931,11 @@ ecb_caldav_load_component_sync (ECalMetaBackend *meta_backend,
        }
 
        if (!success) {
-               GError *local_error = NULL;
-
                uri = ecb_caldav_uid_to_uri (cbdav, uid, ".ics");
                g_return_val_if_fail (uri != NULL, FALSE);
 
+               g_clear_error (&local_error);
+
                success = e_webdav_session_get_data_sync (cbdav->priv->webdav, uri, &href, &etag, &bytes, 
&length, cancellable, &local_error);
                if (!success && !g_cancellable_is_cancelled (cancellable) &&
                    g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_NOT_FOUND)) {
@@ -932,10 +945,8 @@ ecb_caldav_load_component_sync (ECalMetaBackend *meta_backend,
                        if (uri) {
                                g_clear_error (&local_error);
 
-                               success = e_webdav_session_get_data_sync (cbdav->priv->webdav, uri, &href, 
&etag, &bytes, &length, cancellable, error);
+                               success = e_webdav_session_get_data_sync (cbdav->priv->webdav, uri, &href, 
&etag, &bytes, &length, cancellable, &local_error);
                        }
-               } else if (local_error) {
-                       g_propagate_error (error, local_error);
                }
        }
 
@@ -967,7 +978,7 @@ ecb_caldav_load_component_sync (ECalMetaBackend *meta_backend,
 
                if (!*out_component) {
                        success = FALSE;
-                       g_propagate_error (error, EDC_ERROR (InvalidObject));
+                       g_propagate_error (&local_error, EDC_ERROR (InvalidObject));
                }
        }
 
@@ -976,6 +987,9 @@ ecb_caldav_load_component_sync (ECalMetaBackend *meta_backend,
        g_free (etag);
        g_free (bytes);
 
+       if (local_error)
+               g_propagate_error (error, local_error);
+
        return success;
 }
 
@@ -1029,7 +1043,7 @@ ecb_caldav_save_component_sync (ECalMetaBackend *meta_backend,
        if (uid && ical_string && (!overwrite_existing || (extra && *extra))) {
                gboolean force_write = FALSE;
 
-               if (!extra)
+               if (!extra || !*extra)
                        href = ecb_caldav_uid_to_uri (cbdav, uid, ".ics");
 
                if (overwrite_existing) {
@@ -1078,6 +1092,7 @@ ecb_caldav_remove_component_sync (ECalMetaBackend *meta_backend,
        icalcomponent *icalcomp;
        gchar *etag = NULL;
        gboolean success;
+       GError *local_error = NULL;
 
        g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
        g_return_val_if_fail (uid != NULL, FALSE);
@@ -1096,15 +1111,42 @@ ecb_caldav_remove_component_sync (ECalMetaBackend *meta_backend,
                return FALSE;
        }
 
-       if (conflict_resolution != E_CONFLICT_RESOLUTION_FAIL)
+       if (conflict_resolution == E_CONFLICT_RESOLUTION_FAIL)
                etag = e_cal_util_dup_x_property (icalcomp, E_CALDAV_X_ETAG);
 
        success = e_webdav_session_delete_sync (cbdav->priv->webdav, extra,
-               E_WEBDAV_DEPTH_THIS, etag, cancellable, error);
+               E_WEBDAV_DEPTH_THIS, etag, cancellable, &local_error);
+
+       if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_NOT_FOUND)) {
+               gchar *href;
+
+               href = ecb_caldav_uid_to_uri (cbdav, uid, ".ics");
+               if (href) {
+                       g_clear_error (&local_error);
+                       success = e_webdav_session_delete_sync (cbdav->priv->webdav, href,
+                               E_WEBDAV_DEPTH_THIS, etag, cancellable, &local_error);
+
+                       g_free (href);
+               }
+
+               if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_NOT_FOUND)) {
+                       href = ecb_caldav_uid_to_uri (cbdav, uid, NULL);
+                       if (href) {
+                               g_clear_error (&local_error);
+                               success = e_webdav_session_delete_sync (cbdav->priv->webdav, href,
+                                       E_WEBDAV_DEPTH_THIS, etag, cancellable, &local_error);
+
+                               g_free (href);
+                       }
+               }
+       }
 
        icalcomponent_free (icalcomp);
        g_free (etag);
 
+       if (local_error)
+               g_propagate_error (error, local_error);
+
        return success;
 }
 
diff --git a/src/calendar/libedata-cal/e-cal-meta-backend.c b/src/calendar/libedata-cal/e-cal-meta-backend.c
index 620f0bd..64b91b6 100644
--- a/src/calendar/libedata-cal/e-cal-meta-backend.c
+++ b/src/calendar/libedata-cal/e-cal-meta-backend.c
@@ -103,6 +103,7 @@ static gboolean ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
                                                  const gchar *uid,
                                                  const gchar *preloaded_object,
                                                  const gchar *preloaded_extra,
+                                                 gchar **out_new_uid,
                                                  GCancellable *cancellable,
                                                  GError **error);
 static gboolean ecmb_save_component_wrapper_sync (ECalMetaBackend *meta_backend,
@@ -806,7 +807,7 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
                goto done;
        }
 
-       success = ecmb_upload_local_changes_sync (meta_backend, cal_cache, E_CONFLICT_RESOLUTION_KEEP_LOCAL, 
cancellable, error);
+       success = ecmb_upload_local_changes_sync (meta_backend, cal_cache, E_CONFLICT_RESOLUTION_FAIL, 
cancellable, error);
 
        while (repeat && success &&
               !g_cancellable_set_error_if_cancelled (cancellable, error)) {
@@ -851,12 +852,13 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
                                        continue;
                                }
 
-                               if (g_hash_table_contains (covered_uids, nfo->uid))
+                               if (!*nfo->uid ||
+                                   g_hash_table_contains (covered_uids, nfo->uid))
                                        continue;
 
                                g_hash_table_insert (covered_uids, nfo->uid, NULL);
 
-                               success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, 
nfo->uid, nfo->object, nfo->extra, cancellable, &local_error);
+                               success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, 
nfo->uid, nfo->object, nfo->extra, NULL, cancellable, &local_error);
 
                                /* Do not stop on invalid objects, just notify about them later, and load as 
many as possible */
                                if (!success && g_error_matches (local_error, E_DATA_CAL_ERROR, 
InvalidObject)) {
@@ -885,7 +887,10 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
                                        continue;
                                }
 
-                               success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, 
nfo->uid, nfo->object, nfo->extra, cancellable, &local_error);
+                               if (!*nfo->uid)
+                                       continue;
+
+                               success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, 
nfo->uid, nfo->object, nfo->extra, NULL, cancellable, &local_error);
 
                                /* Do not stop on invalid objects, just notify about them later, and load as 
many as possible */
                                if (!success && g_error_matches (local_error, E_DATA_CAL_ERROR, 
InvalidObject)) {
@@ -1189,6 +1194,7 @@ ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
                                  const gchar *uid,
                                  const gchar *preloaded_object,
                                  const gchar *preloaded_extra,
+                                 gchar **out_new_uid,
                                  GCancellable *cancellable,
                                  GError **error)
 {
@@ -1196,6 +1202,7 @@ ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
        icalcomponent *icalcomp = NULL;
        GSList *new_instances = NULL;
        gchar *extra = NULL;
+       const gchar *loaded_uid = NULL;
        gboolean success = TRUE;
 
        if (preloaded_object && *preloaded_object) {
@@ -1226,23 +1233,34 @@ ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
                     subcomp = icalcomponent_get_next_component (icalcomp, kind)) {
                        ECalComponent *comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone 
(subcomp));
 
-                       if (comp)
+                       if (comp) {
                                new_instances = g_slist_prepend (new_instances, comp);
+
+                               if (!loaded_uid)
+                                       loaded_uid = icalcomponent_get_uid (e_cal_component_get_icalcomponent 
(comp));
+                       }
                }
        } else {
                ECalComponent *comp = e_cal_component_new_from_icalcomponent (icalcomp);
 
                icalcomp = NULL;
 
-               if (comp)
+               if (comp) {
                        new_instances = g_slist_prepend (new_instances, comp);
+
+                       if (!loaded_uid)
+                               loaded_uid = icalcomponent_get_uid (e_cal_component_get_icalcomponent (comp));
+               }
        }
 
        if (new_instances) {
                new_instances = g_slist_reverse (new_instances);
 
-               success = ecmb_put_instances (meta_backend, cal_cache, uid, offline_flag,
+               success = ecmb_put_instances (meta_backend, cal_cache, loaded_uid ? loaded_uid : uid, 
offline_flag,
                        new_instances, extra ? extra : preloaded_extra, cancellable, error);
+
+               if (success && out_new_uid)
+                       *out_new_uid = g_strdup (loaded_uid ? loaded_uid : uid);
        } else {
                g_propagate_error (error, e_data_cal_create_error_fmt (InvalidObject, _("Received object for 
UID ā€œ%sā€ doesn't contain any expected component"), uid));
                success = FALSE;
@@ -1306,22 +1324,26 @@ ecmb_save_component_wrapper_sync (ECalMetaBackend *meta_backend,
        success = success && e_cal_meta_backend_save_component_sync (meta_backend, overwrite_existing, 
conflict_resolution,
                instances ? instances : in_instances, extra, &new_uid, &new_extra, cancellable, error);
 
-       if (success && new_uid) {
+       if (success && new_uid && *new_uid) {
+               gchar *loaded_uid = NULL;
+
                success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, new_uid, NULL,
-                       new_extra ? new_extra : extra, cancellable, error);
+                       new_extra ? new_extra : extra, &loaded_uid, cancellable, error);
 
-               if (success && g_strcmp0 (new_uid, orig_uid) != 0)
+               if (success && g_strcmp0 (loaded_uid, orig_uid) != 0)
                        success = ecmb_maybe_remove_from_cache (meta_backend, cal_cache, E_CACHE_IS_ONLINE, 
orig_uid, cancellable, error);
 
                if (success && out_new_uid)
-                       *out_new_uid = new_uid;
+                       *out_new_uid = loaded_uid;
                else
-                       g_free (new_uid);
+                       g_free (loaded_uid);
 
                if (out_requires_put)
                        *out_requires_put = FALSE;
        }
 
+       g_free (new_uid);
+
        if (success && out_new_extra)
                *out_new_extra = new_extra;
        else
@@ -1372,8 +1394,13 @@ ecmb_open_sync (ECalBackendSync *sync_backend,
                e_cal_backend_set_writable (E_CAL_BACKEND (meta_backend),
                        e_cal_meta_backend_get_connected_writable (meta_backend));
        } else {
-               if (!ecmb_connect_wrapper_sync (meta_backend, cancellable, error))
+               if (!ecmb_connect_wrapper_sync (meta_backend, cancellable, error)) {
+                       g_mutex_lock (&meta_backend->priv->property_lock);
+                       meta_backend->priv->refresh_after_authenticate = TRUE;
+                       g_mutex_unlock (&meta_backend->priv->property_lock);
+
                        return;
+               }
        }
 
        ecmb_schedule_refresh (E_CAL_META_BACKEND (sync_backend));
@@ -1412,7 +1439,7 @@ ecmb_get_object_sync (ECalBackendSync *sync_backend,
        GError *local_error = NULL;
 
        g_return_if_fail (E_IS_CAL_META_BACKEND (sync_backend));
-       g_return_if_fail (uid != NULL);
+       g_return_if_fail (uid && *uid);
        g_return_if_fail (calobj != NULL);
 
        meta_backend = E_CAL_META_BACKEND (sync_backend);
@@ -1422,6 +1449,7 @@ ecmb_get_object_sync (ECalBackendSync *sync_backend,
 
        if (!e_cal_cache_get_component_as_string (cal_cache, uid, rid, calobj, cancellable, &local_error) &&
            g_error_matches (local_error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND)) {
+               gchar *loaded_uid = NULL;
                gboolean found = FALSE;
 
                g_clear_error (&local_error);
@@ -1429,12 +1457,14 @@ ecmb_get_object_sync (ECalBackendSync *sync_backend,
                /* Ignore errors here, just try whether it's on the remote side, but not in the local cache */
                if (e_backend_get_online (E_BACKEND (meta_backend)) &&
                    ecmb_connect_wrapper_sync (meta_backend, cancellable, NULL) &&
-                   ecmb_load_component_wrapper_sync (meta_backend, cal_cache, uid, NULL, NULL, cancellable, 
NULL)) {
-                       found = e_cal_cache_get_component_as_string (cal_cache, uid, rid, calobj, 
cancellable, NULL);
+                   ecmb_load_component_wrapper_sync (meta_backend, cal_cache, uid, NULL, NULL, &loaded_uid, 
cancellable, NULL)) {
+                       found = e_cal_cache_get_component_as_string (cal_cache, loaded_uid, rid, calobj, 
cancellable, NULL);
                }
 
                if (!found)
                        g_propagate_error (error, e_data_cal_create_error (ObjectNotFound, NULL));
+
+               g_free (loaded_uid);
        } else if (local_error) {
                g_propagate_error (error, e_data_cal_create_error (OtherError, local_error->message));
                g_clear_error (&local_error);
@@ -1705,7 +1735,7 @@ ecmb_create_object_sync (ECalMetaBackend *meta_backend,
                if (out_new_comp) {
                        if (new_uid) {
                                if (!e_cal_cache_get_component (cal_cache, new_uid, NULL, out_new_comp, 
cancellable, NULL))
-                                       *out_new_comp = NULL;
+                                       *out_new_comp = g_object_ref (comp);
                        } else {
                                *out_new_comp = g_object_ref (comp);
                        }
@@ -1730,7 +1760,7 @@ ecmb_create_objects_sync (ECalBackendSync *sync_backend,
        ECalMetaBackend *meta_backend;
        ECalCache *cal_cache;
        ECacheOfflineFlag offline_flag = E_CACHE_OFFLINE_UNKNOWN;
-       EConflictResolution conflict_resolution = E_CONFLICT_RESOLUTION_KEEP_LOCAL;
+       EConflictResolution conflict_resolution = E_CONFLICT_RESOLUTION_FAIL;
        icalcomponent_kind backend_kind;
        GSList *link;
        gboolean success = TRUE;
@@ -2022,7 +2052,7 @@ ecmb_modify_objects_sync (ECalBackendSync *sync_backend,
        ECalMetaBackend *meta_backend;
        ECalCache *cal_cache;
        ECacheOfflineFlag offline_flag = E_CACHE_OFFLINE_UNKNOWN;
-       EConflictResolution conflict_resolution = E_CONFLICT_RESOLUTION_KEEP_LOCAL;
+       EConflictResolution conflict_resolution = E_CONFLICT_RESOLUTION_FAIL;
        icalcomponent_kind backend_kind;
        GSList *link;
        gboolean success = TRUE;
@@ -2329,7 +2359,7 @@ ecmb_remove_objects_sync (ECalBackendSync *sync_backend,
        ECalMetaBackend *meta_backend;
        ECalCache *cal_cache;
        ECacheOfflineFlag offline_flag = E_CACHE_OFFLINE_UNKNOWN;
-       EConflictResolution conflict_resolution = E_CONFLICT_RESOLUTION_KEEP_LOCAL;
+       EConflictResolution conflict_resolution = E_CONFLICT_RESOLUTION_FAIL;
        GSList *link;
        gboolean success = TRUE;
 
@@ -2465,7 +2495,7 @@ ecmb_receive_objects_sync (ECalBackendSync *sync_backend,
 {
        ECalMetaBackend *meta_backend;
        ECacheOfflineFlag offline_flag = E_CACHE_OFFLINE_UNKNOWN;
-       EConflictResolution conflict_resolution = E_CONFLICT_RESOLUTION_KEEP_LOCAL;
+       EConflictResolution conflict_resolution = E_CONFLICT_RESOLUTION_FAIL;
        ECalCache *cal_cache;
        ECalComponent *comp;
        icalcomponent *icalcomp, *subcomp;


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