[evolution-data-server/wip/offline-cache] Make CalDAV backend derive from ECalMetaBackend + some other semi-related changes



commit d963caf8f546071a9b46be00b48501d908166412
Author: Milan Crha <mcrha redhat com>
Date:   Wed Apr 12 11:53:07 2017 +0200

    Make CalDAV backend derive from ECalMetaBackend + some other semi-related changes

 .../backends/caldav/e-cal-backend-caldav.c         | 6170 ++++----------------
 .../backends/caldav/e-cal-backend-caldav.h         |    4 +-
 .../backends/gtasks/e-cal-backend-gtasks.c         |   36 +-
 src/calendar/backends/http/e-cal-backend-http.c    |   75 +-
 src/calendar/libedata-cal/e-cal-meta-backend.c     |  435 ++-
 src/calendar/libedata-cal/e-cal-meta-backend.h     |   22 +-
 src/libedataserver/e-soup-session.c                |    6 +
 .../e-source-credentials-provider-impl-google.c    |    8 +-
 src/libedataserver/e-webdav-session.c              |  277 +-
 src/libedataserver/e-webdav-session.h              |   26 +-
 src/libedataserver/e-xml-document.c                |  126 +
 src/libedataserver/e-xml-document.h                |   19 +
 tests/libedata-cal/test-cal-meta-backend.c         |    7 +-
 13 files changed, 1841 insertions(+), 5370 deletions(-)
---
diff --git a/src/calendar/backends/caldav/e-cal-backend-caldav.c 
b/src/calendar/backends/caldav/e-cal-backend-caldav.c
index 4410112..71b3842 100644
--- a/src/calendar/backends/caldav/e-cal-backend-caldav.c
+++ b/src/calendar/backends/caldav/e-cal-backend-caldav.c
@@ -1,7 +1,6 @@
 /*
- * Evolution calendar - caldav backend
- *
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * 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
@@ -21,89 +20,30 @@
 #include "evolution-data-server-config.h"
 
 #include <string.h>
-#include <unistd.h>
-#include <glib/gstdio.h>
 #include <glib/gi18n-lib.h>
 
-/* LibXML2 includes */
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-#include <libxml/xpath.h>
-#include <libxml/xpathInternals.h>
-
-/* LibSoup includes */
-#include <libsoup/soup.h>
-
 #include <libedataserver/libedataserver.h>
 
 #include "e-cal-backend-caldav.h"
 
-#define d(x)
-
 #define E_CAL_BACKEND_CALDAV_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
        ((obj), E_TYPE_CAL_BACKEND_CALDAV, ECalBackendCalDAVPrivate))
 
-#define CALDAV_CTAG_KEY "CALDAV_CTAG"
-#define CALDAV_MAX_MULTIGET_AMOUNT 100 /* what's the maximum count of items to fetch within a multiget 
request */
-#define LOCAL_PREFIX "file://"
+#define E_CALDAV_MAX_MULTIGET_AMOUNT 100 /* what's the maximum count of items to fetch within a multiget 
request */
 
-/* in seconds */
-#define DEFAULT_REFRESH_TIME 60
+#define E_CALDAV_X_ETAG "X-EVOLUTION-CALDAV-ETAG"
 
 #define EDC_ERROR(_code) e_data_cal_create_error (_code, NULL)
 #define EDC_ERROR_EX(_code, _msg) e_data_cal_create_error (_code, _msg)
 
-typedef enum {
-
-       SLAVE_SHOULD_SLEEP,
-       SLAVE_SHOULD_WORK,
-       SLAVE_SHOULD_WORK_NO_CTAG_CHECK,
-       SLAVE_SHOULD_DIE
-
-} SlaveCommand;
-
 /* Private part of the ECalBackendHttp structure */
 struct _ECalBackendCalDAVPrivate {
-
-       /* The local disk cache */
-       ECalBackendStore *store;
-
-       /* should we sync for offline mode? */
-       gboolean do_offline;
-
-       /* TRUE after caldav_open */
-       gboolean loaded;
-       /* TRUE when server reachable */
-       gboolean opened;
-
-       /* lock to indicate a busy state */
-       GMutex busy_lock;
-
-       /* cond to synch threads */
-       GCond cond;
-
-       /* cond to know the slave gone */
-       GCond slave_gone_cond;
-
-       /* BG synch thread */
-       const GThread *synch_slave; /* just for a reference, whether thread exists */
-       SlaveCommand slave_cmd;
-       gboolean slave_busy; /* whether is slave working */
-
        /* The main soup session  */
-       SoupSession *session;
-
-       /* clandar uri */
-       gchar *uri;
-
-       /* Authentication info */
-       ENamedParameters *credentials;
-       gboolean auth_required;
+       EWebDAVSession *webdav;
 
        /* support for 'getctag' extension */
        gboolean ctag_supported;
-       gchar *ctag_to_store;
 
        /* TRUE when 'calendar-schedule' supported on the server */
        gboolean calendar_schedule;
@@ -119,4513 +59,1191 @@ struct _ECalBackendCalDAVPrivate {
 
        /* The iCloud.com requires timezone IDs as locations */
        gboolean is_icloud;
-
-       /* set to true if thread for ESource::changed is invoked */
-       gboolean updating_source;
-
-       guint refresh_id;
-
-       /* If we fail to obtain an OAuth2 access token,
-        * soup_authenticate_bearer() stashes an error
-        * here to be claimed in caldav_credentials_required_sync().
-        * This lets us propagate a more useful error
-        * message than a generic SOUP_STATUS_UNAUTHORIZED description. */
-       GError *bearer_auth_error;
-       GMutex bearer_auth_error_lock;
-       ESoupAuthBearer *using_bearer_auth;
 };
 
-/* Forward Declarations */
-static void    e_caldav_backend_initable_init
-                               (GInitableIface *interface);
-
-G_DEFINE_TYPE_WITH_CODE (
-       ECalBackendCalDAV,
-       e_cal_backend_caldav,
-       E_TYPE_CAL_BACKEND_SYNC,
-       G_IMPLEMENT_INTERFACE (
-               G_TYPE_INITABLE,
-               e_caldav_backend_initable_init))
-
-/* ************************************************************************* */
-/* Debugging */
-
-#define DEBUG_MESSAGE "message"
-#define DEBUG_MESSAGE_HEADER "message:header"
-#define DEBUG_MESSAGE_BODY "message:body"
-#define DEBUG_SERVER_ITEMS "items"
-#define DEBUG_ATTACHMENTS "attachments"
-
-static gboolean open_calendar_wrapper (ECalBackendCalDAV *cbdav,
-                                      GCancellable *cancellable,
-                                      GError **error,
-                                      gboolean first_attempt,
-                                      gboolean *know_unreachable,
-                                      gchar **out_certificate_pem,
-                                      GTlsCertificateFlags *out_certificate_errors);
-
-static void convert_to_inline_attachment (ECalBackendCalDAV *cbdav, icalcomponent *icalcomp);
-static void convert_to_url_attachment (ECalBackendCalDAV *cbdav, icalcomponent *icalcomp);
-static void remove_cached_attachment (ECalBackendCalDAV *cbdav, const gchar *uid);
-
-static gboolean caldav_debug_all = FALSE;
-static GHashTable *caldav_debug_table = NULL;
-
-static void
-add_debug_key (const gchar *start,
-               const gchar *end)
-{
-       gchar *debug_key;
-       gchar *debug_value;
-
-       if (start == end) {
-               return;
-       }
-
-       debug_key = debug_value = g_strndup (start, end - start);
-
-       debug_key = g_strchug (debug_key);
-       debug_key = g_strchomp (debug_key);
-
-       if (strlen (debug_key) == 0) {
-               g_free (debug_value);
-               return;
-       }
-
-       g_hash_table_insert (
-               caldav_debug_table,
-               debug_key,
-               debug_value);
-
-       d (g_debug ("Adding %s to enabled debugging keys", debug_key));
-}
-
-static gpointer
-caldav_debug_init_once (gpointer data)
-{
-       const gchar *dbg;
-
-       dbg = g_getenv ("CALDAV_DEBUG");
-
-       if (dbg) {
-               const gchar *ptr;
-
-               d (g_debug ("Got debug env variable: [%s]", dbg));
-
-               caldav_debug_table = g_hash_table_new (
-                       g_str_hash,
-                       g_str_equal);
-
-               ptr = dbg;
-
-               while (*ptr != '\0') {
-                       if (*ptr == ',' || *ptr == ':') {
-
-                               add_debug_key (dbg, ptr);
-
-                               if (*ptr == ',') {
-                                       dbg = ptr + 1;
-                               }
-                       }
-
-                       ptr++;
-               }
-
-               if (ptr - dbg > 0) {
-                       add_debug_key (dbg, ptr);
-               }
-
-               if (g_hash_table_lookup (caldav_debug_table, "all")) {
-                       caldav_debug_all = TRUE;
-                       g_hash_table_destroy (caldav_debug_table);
-                       caldav_debug_table = NULL;
-               }
-       }
-
-       return NULL;
-}
-
-static void
-caldav_debug_init (void)
-{
-       static GOnce debug_once = G_ONCE_INIT;
-
-       g_once (
-               &debug_once,
-               caldav_debug_init_once,
-               NULL);
-}
-
-static gboolean
-caldav_debug_show (const gchar *component)
-{
-       if (G_UNLIKELY (caldav_debug_all)) {
-               return TRUE;
-       } else if (G_UNLIKELY (caldav_debug_table != NULL) &&
-                  g_hash_table_lookup (caldav_debug_table, component)) {
-               return TRUE;
-       }
-
-       return FALSE;
-}
-
-#define DEBUG_MAX_BODY_SIZE (100 * 1024 * 1024)
-
-static void
-caldav_debug_setup (SoupSession *session)
-{
-       SoupLogger *logger;
-       SoupLoggerLogLevel level;
-
-       if (caldav_debug_show (DEBUG_MESSAGE_BODY))
-               level = SOUP_LOGGER_LOG_BODY;
-       else if (caldav_debug_show (DEBUG_MESSAGE_HEADER))
-               level = SOUP_LOGGER_LOG_HEADERS;
-       else
-               level = SOUP_LOGGER_LOG_MINIMAL;
-
-       logger = soup_logger_new (level, DEBUG_MAX_BODY_SIZE);
-       soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
-       g_object_unref (logger);
-}
-
-/* TODO Do not replicate this in every backend */
-static icaltimezone *
-resolve_tzid (const gchar *tzid,
-              gpointer user_data)
-{
-       ETimezoneCache *timezone_cache;
-
-       timezone_cache = E_TIMEZONE_CACHE (user_data);
-
-       return e_timezone_cache_get_timezone (timezone_cache, tzid);
-}
-
-static gboolean
-put_component_to_store (ECalBackendCalDAV *cbdav,
-                        ECalComponent *comp)
-{
-       time_t time_start, time_end;
-
-       e_cal_util_get_component_occur_times (
-               comp, &time_start, &time_end,
-               resolve_tzid, cbdav,  icaltimezone_get_utc_timezone (),
-               e_cal_backend_get_kind (E_CAL_BACKEND (cbdav)));
-
-       return e_cal_backend_store_put_component_with_time_range (
-               cbdav->priv->store, comp, time_start, time_end);
-}
-
-static ECalBackendSyncClass *parent_class = NULL;
-
-static void caldav_source_changed_cb (ESource *source, ECalBackendCalDAV *cbdav);
-
-static gboolean remove_comp_from_cache (ECalBackendCalDAV *cbdav, const gchar *uid, const gchar *rid);
-static gboolean put_comp_to_cache (ECalBackendCalDAV *cbdav, icalcomponent *icalcomp, const gchar *href, 
const gchar *etag);
-static void put_server_comp_to_cache (ECalBackendCalDAV *cbdav, icalcomponent *icomp, const gchar *href, 
const gchar *etag, GTree *c_uid2complist);
-
-/* ************************************************************************* */
-/* Misc. utility functions */
-
-static void
-caldav_ensure_bearer_auth_usage (ECalBackendCalDAV *cbdav,
-                                ESoupAuthBearer *bearer)
-{
-       SoupSessionFeature *feature;
-       SoupURI *soup_uri;
-       ESourceWebdav *extension;
-       ESource *source;
-
-       g_return_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav));
-
-       source = e_backend_get_source (E_BACKEND (cbdav));
-
-       /* Preload the SoupAuthManager with a valid "Bearer" token
-        * when using OAuth 2.0. This avoids an extra unauthorized
-        * HTTP round-trip, which apparently Google doesn't like. */
-
-       feature = soup_session_get_feature (cbdav->priv->session, SOUP_TYPE_AUTH_MANAGER);
-
-       if (!soup_session_feature_has_feature (feature, E_TYPE_SOUP_AUTH_BEARER)) {
-               /* Add the "Bearer" auth type to support OAuth 2.0. */
-               soup_session_feature_add_feature (feature, E_TYPE_SOUP_AUTH_BEARER);
-       }
-
-       extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
-       soup_uri = e_source_webdav_dup_soup_uri (extension);
-
-       soup_auth_manager_use_auth (
-               SOUP_AUTH_MANAGER (feature),
-               soup_uri, SOUP_AUTH (bearer));
-
-       soup_uri_free (soup_uri);
-}
-
-static gboolean
-caldav_setup_bearer_auth (ECalBackendCalDAV *cbdav,
-                         ESoupAuthBearer *bearer,
-                         GCancellable *cancellable,
-                         GError **error)
-{
-       ESource *source;
-       gchar *access_token = NULL;
-       gint expires_in_seconds = -1;
-       gboolean success = FALSE;
-
-       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
-       g_return_val_if_fail (E_IS_SOUP_AUTH_BEARER (bearer), FALSE);
-
-       source = e_backend_get_source (E_BACKEND (cbdav));
-
-       success = e_util_get_source_oauth2_access_token_sync (source, cbdav->priv->credentials,
-               &access_token, &expires_in_seconds, cancellable, error);
-
-       if (success) {
-               e_soup_auth_bearer_set_access_token (bearer, access_token, expires_in_seconds);
-               caldav_ensure_bearer_auth_usage (cbdav, bearer);
-       }
-
-       g_free (access_token);
-
-       return success;
-}
+G_DEFINE_TYPE (ECalBackendCalDAV, e_cal_backend_caldav, E_TYPE_CAL_META_BACKEND)
 
 static gboolean
-caldav_maybe_prepare_bearer_auth (ECalBackendCalDAV *cbdav,
-                                 GCancellable *cancellable,
-                                 GError **error)
+ecb_caldav_connect_sync (ECalMetaBackend *meta_backend,
+                        const ENamedParameters *credentials,
+                        ESourceAuthenticationResult *out_auth_result,
+                        gchar **out_certificate_pem,
+                        GTlsCertificateFlags *out_certificate_errors,
+                        GCancellable *cancellable,
+                        GError **error)
 {
+       ECalBackendCalDAV *cbdav;
+       GHashTable *capabilities = NULL, *allows = NULL;
        ESource *source;
-       gchar *auth_method = NULL;
        gboolean success;
+       GError *local_error = NULL;
 
-       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
-
-       source = e_backend_get_source (E_BACKEND (cbdav));
+       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
+       g_return_val_if_fail (out_auth_result != NULL, FALSE);
 
-       if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
-               ESourceAuthentication *extension;
+       cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
 
-               extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
-               auth_method = e_source_authentication_dup_method (extension);
-       } else {
+       if (cbdav->priv->webdav)
                return TRUE;
-       }
-
-       if (g_strcmp0 (auth_method, "OAuth2") != 0 && g_strcmp0 (auth_method, "Google") != 0) {
-               g_free (auth_method);
-               return TRUE;
-       }
 
-       g_free (auth_method);
+       source = e_backend_get_source (E_BACKEND (meta_backend));
 
-       if (cbdav->priv->using_bearer_auth) {
-               success = caldav_setup_bearer_auth (cbdav, cbdav->priv->using_bearer_auth, cancellable, 
error);
-       } else {
-               ESourceWebdav *extension;
-               SoupAuth *soup_auth;
-               SoupURI *soup_uri;
-
-               extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
-               soup_uri = e_source_webdav_dup_soup_uri (extension);
+       cbdav->priv->webdav = e_webdav_session_new (source);
 
-               soup_auth = g_object_new (
-                       E_TYPE_SOUP_AUTH_BEARER,
-                       SOUP_AUTH_HOST, soup_uri->host, NULL);
+       e_soup_session_setup_logging (E_SOUP_SESSION (cbdav->priv->webdav), g_getenv ("CALDAV_DEBUG"));
 
-               success = caldav_setup_bearer_auth (cbdav, E_SOUP_AUTH_BEARER (soup_auth), cancellable, 
error);
-               if (success)
-                       cbdav->priv->using_bearer_auth = g_object_ref (soup_auth);
+       e_binding_bind_property (
+               cbdav, "proxy-resolver",
+               cbdav->priv->webdav, "proxy-resolver",
+               G_BINDING_SYNC_CREATE);
 
-               g_object_unref (soup_auth);
-               soup_uri_free (soup_uri);
-       }
+       /* Thinks the 'getctag' extension is available the first time, but unset it when realizes it isn't. */
+       cbdav->priv->ctag_supported = TRUE;
 
-       return success;
-}
+       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
 
-static void
-update_slave_cmd (ECalBackendCalDAVPrivate *priv,
-                  SlaveCommand slave_cmd)
-{
-       g_return_if_fail (priv != NULL);
+       e_soup_session_set_credentials (E_SOUP_SESSION (cbdav->priv->webdav), credentials);
 
-       if (priv->slave_cmd == SLAVE_SHOULD_DIE)
-               return;
+       success = e_webdav_session_options_sync (cbdav->priv->webdav, NULL,
+               &capabilities, &allows, cancellable, &local_error);
 
-       priv->slave_cmd = slave_cmd;
-}
+       if (success) {
+               ESourceWebdav *webdav_extension;
+               ECalCache *cal_cache;
+               SoupURI *soup_uri;
+               gboolean is_writable;
+               gboolean calendar_access;
 
-#define X_E_CALDAV "X-EVOLUTION-CALDAV-"
-#define X_E_CALDAV_ATTACHMENT_NAME X_E_CALDAV "ATTACHMENT-NAME"
+               webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+               soup_uri = e_source_webdav_dup_soup_uri (webdav_extension);
+               cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
 
-static void
-icomp_x_prop_set (icalcomponent *comp,
-                  const gchar *key,
-                  const gchar *value)
-{
-       icalproperty *xprop;
+               /* 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));
 
-       /* Find the old one first */
-       xprop = icalcomponent_get_first_property (comp, ICAL_X_PROPERTY);
+               cbdav->priv->calendar_schedule = capabilities && g_hash_table_contains (capabilities, 
E_WEBDAV_CAPABILITY_CALENDAR_SCHEDULE);
+               calendar_access = capabilities && g_hash_table_contains (capabilities, 
E_WEBDAV_CAPABILITY_CALENDAR_ACCESS);
 
-       while (xprop) {
-               const gchar *str = icalproperty_get_x_name (xprop);
+               if (calendar_access) {
+                       e_cal_backend_set_writable (E_CAL_BACKEND (cbdav), is_writable);
 
-               if (!strcmp (str, key)) {
-                       if (value) {
-                               icalproperty_set_value_from_string (xprop, value, "NO");
-                       } else {
-                               icalcomponent_remove_property (comp, xprop);
-                               icalproperty_free (xprop);
-                       }
-                       break;
-               }
+                       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
 
-               xprop = icalcomponent_get_next_property (comp, ICAL_X_PROPERTY);
-       }
+                       cbdav->priv->is_google = soup_uri && soup_uri->host && (
+                               g_ascii_strcasecmp (soup_uri->host, "www.google.com") == 0 ||
+                               g_ascii_strcasecmp (soup_uri->host, "apidata.googleusercontent.com") == 0);
 
-       if (!xprop && value) {
-               xprop = icalproperty_new_x (value);
-               icalproperty_set_x_name (xprop, key);
-               icalcomponent_add_property (comp, xprop);
-       }
-}
+                       cbdav->priv->is_icloud = soup_uri && soup_uri->host &&
+                               e_util_utf8_strstrcase (soup_uri->host, ".icloud.com");
+               } else {
+                       gchar *uri;
 
-static gchar *
-icomp_x_prop_get (icalcomponent *comp,
-                  const gchar *key)
-{
-       icalproperty *xprop;
+                       uri = soup_uri_to_string (soup_uri, FALSE);
 
-       /* Find the old one first */
-       xprop = icalcomponent_get_first_property (comp, ICAL_X_PROPERTY);
+                       success = FALSE;
+                       g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+                               _("Given URL ā€œ%sā€ doesn't reference CalDAV calendar"), uri);
 
-       while (xprop) {
-               const gchar *str = icalproperty_get_x_name (xprop);
+                       g_free (uri);
 
-               if (!strcmp (str, key)) {
-                       break;
+                       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
                }
 
-               xprop = icalcomponent_get_next_property (comp, ICAL_X_PROPERTY);
-       }
-
-       if (xprop) {
-               return icalproperty_get_value_as_string_r (xprop);
+               g_clear_object (&cal_cache);
+               soup_uri_free (soup_uri);
        }
+       if (success) {
+               gchar *ctag = NULL;
 
-       return NULL;
-}
-
-/* passing NULL as 'href' removes the property */
-static void
-ecalcomp_set_href (ECalComponent *comp,
-                   const gchar *href)
-{
-       icalcomponent *icomp;
-
-       icomp = e_cal_component_get_icalcomponent (comp);
-       g_return_if_fail (icomp != NULL);
-
-       icomp_x_prop_set (icomp, X_E_CALDAV "HREF", href);
-}
-
-static gchar *
-ecalcomp_get_href (ECalComponent *comp)
-{
-       icalcomponent *icomp;
-       gchar          *str;
-
-       str = NULL;
-       icomp = e_cal_component_get_icalcomponent (comp);
-       g_return_val_if_fail (icomp != NULL, NULL);
-
-       str = icomp_x_prop_get (icomp, X_E_CALDAV "HREF");
-
-       return str;
-}
-
-/* passing NULL as 'etag' removes the property */
-static void
-ecalcomp_set_etag (ECalComponent *comp,
-                   const gchar *etag)
-{
-       icalcomponent *icomp;
-
-       icomp = e_cal_component_get_icalcomponent (comp);
-       g_return_if_fail (icomp != NULL);
-
-       icomp_x_prop_set (icomp, X_E_CALDAV "ETAG", etag);
-}
-
-static gchar *
-ecalcomp_get_etag (ECalComponent *comp)
-{
-       icalcomponent *icomp;
-       gchar          *str;
-
-       str = NULL;
-       icomp = e_cal_component_get_icalcomponent (comp);
-       g_return_val_if_fail (icomp != NULL, NULL);
-
-       str = icomp_x_prop_get (icomp, X_E_CALDAV "ETAG");
-
-       /* libical 0.48 escapes quotes, thus unescape them */
-       if (str && strchr (str, '\\')) {
-               gint ii, jj;
-
-               for (ii = 0, jj = 0; str[ii]; ii++) {
-                       if (str[ii] == '\\') {
-                               ii++;
-                               if (!str[ii])
-                                       break;
-                       }
+               /* Some servers, notably Google, allow OPTIONS when not
+                  authorized (aka without credentials), thus try something
+                  more aggressive, just in case.
 
-                       str[jj] = str[ii];
-                       jj++;
+                  The 'getctag' extension is not required, thuch check
+                  for unauthorized error only. */
+               if (!e_webdav_session_getctag_sync (cbdav->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);
                }
 
-               str[jj] = 0;
+               g_free (ctag);
        }
 
-       return str;
-}
-
-/*typedef enum {
- *
-       / * object is in synch,
-        * now isnt that ironic? :) * /
-       ECALCOMP_IN_SYNCH = 0,
- *
-       / * local changes * /
-       ECALCOMP_LOCALLY_CREATED,
-       ECALCOMP_LOCALLY_DELETED,
-       ECALCOMP_LOCALLY_MODIFIED
- *
-} ECalCompSyncState;
- *
-/ * oos = out of synch * /
-static void
-ecalcomp_set_synch_state (ECalComponent *comp,
- *                        ECalCompSyncState state)
-{
-       icalcomponent *icomp;
-       gchar          *state_string;
- *
-       icomp = e_cal_component_get_icalcomponent (comp);
- *
-       state_string = g_strdup_printf ("%d", state);
- *
-       icomp_x_prop_set (icomp, X_E_CALDAV "ETAG", state_string);
- *
-       g_free (state_string);
-}*/
-
-static gchar *
-ecalcomp_gen_href (ECalComponent *comp)
-{
-       gchar *href, *uid, *tmp;
-       icalcomponent *icomp;
-
-       icomp = e_cal_component_get_icalcomponent (comp);
-       g_return_val_if_fail (icomp != NULL, NULL);
+       if (!success) {
+               gboolean credentials_empty;
+               gboolean is_ssl_error;
 
-       uid = g_strdup (icalcomponent_get_uid (icomp));
-       if (!uid || !*uid) {
-               g_free (uid);
-               uid = e_cal_component_gen_uid ();
+               credentials_empty = !credentials || !e_named_parameters_count (credentials);
+               is_ssl_error = g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED);
 
-               tmp = uid ? strchr (uid, '@') : NULL;
-               if (tmp)
-                       *tmp = '\0';
+               *out_auth_result = E_SOURCE_AUTHENTICATION_ERROR;
 
-               tmp = NULL;
-       } else
-               tmp = isodate_from_time_t (time (NULL));
-
-       /* quite long, but ensures uniqueness quite well, without using UUIDs */
-       href = g_strconcat (uid ? uid : "no-uid", tmp ? "-" : "", tmp ? tmp : "", ".ics", NULL);
-
-       g_free (tmp);
-       g_free (uid);
-
-       icomp_x_prop_set (icomp, X_E_CALDAV "HREF", href);
-
-       return g_strdelimit (href, " /'\"`&();|<>$%{}!\\:*?#@", '_');
-}
-
-/* ensure etag is quoted (to workaround potential server bugs) */
-static gchar *
-quote_etag (const gchar *etag)
-{
-       gchar *ret;
-
-       if (etag && (strlen (etag) < 2 || etag[strlen (etag) - 1] != '\"')) {
-               ret = g_strdup_printf ("\"%s\"", etag);
-       } else {
-               ret = g_strdup (etag);
-       }
-
-       return ret;
-}
-
-/* ************************************************************************* */
-
-static gboolean
-status_code_to_result (SoupMessage *message,
-                       ECalBackendCalDAV *cbdav,
-                       gboolean is_opening,
-                       GError **perror)
-{
-       ECalBackendCalDAVPrivate *priv;
-       gchar *uri;
-
-       g_return_val_if_fail (cbdav != NULL, FALSE);
-       g_return_val_if_fail (message != NULL, FALSE);
-
-       priv = cbdav->priv;
-
-       if (SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
-               return TRUE;
-       }
-
-       if (perror && *perror)
-               return FALSE;
-
-       switch (message->status_code) {
-       case SOUP_STATUS_CANT_RESOLVE:
-       case SOUP_STATUS_CANT_RESOLVE_PROXY:
-       case SOUP_STATUS_CANT_CONNECT:
-       case SOUP_STATUS_CANT_CONNECT_PROXY:
-               g_propagate_error (
-                       perror,
-                       e_data_cal_create_error_fmt (
-                               OtherError,
-                               _("Server is unreachable (%s)"),
-                                       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"))));
-               if (priv) {
-                       priv->opened = FALSE;
-                       e_cal_backend_set_writable (
-                               E_CAL_BACKEND (cbdav), FALSE);
-               }
-               break;
-       case SOUP_STATUS_NOT_FOUND:
-               if (is_opening)
-                       g_propagate_error (perror, EDC_ERROR (NoSuchCal));
-               else
-                       g_propagate_error (perror, EDC_ERROR (ObjectNotFound));
-               break;
-
-       case SOUP_STATUS_FORBIDDEN:
-               if (cbdav->priv->using_bearer_auth && message->response_body &&
-                   message->response_body->data && message->response_body->length) {
-                       gchar *body = g_strndup (message->response_body->data, 
message->response_body->length);
-
-                       /* Do not localize this string, it is returned by the server. */
-                       if (body && (e_util_strstrcase (body, "Daily Limit") ||
-                           e_util_strstrcase (body, "https://console.developers.google.com/";))) {
-                               /* Special-case this condition and provide this error up to the UI. */
-                               g_propagate_error (perror,
-                                       e_data_cal_create_error_fmt (OtherError, _("Failed to login to the 
server: %s"), body));
-                       } else {
-                               g_propagate_error (perror, EDC_ERROR (AuthenticationRequired));
-                       }
-
-                       g_free (body);
+               /* 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_propagate_error (perror, EDC_ERROR (AuthenticationRequired));
+                       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                               _("Unknown error"));
                }
-               break;
-
-       case SOUP_STATUS_UNAUTHORIZED:
-               if (priv && priv->auth_required)
-                       g_propagate_error (perror, EDC_ERROR (AuthenticationFailed));
-               else
-                       g_propagate_error (perror, EDC_ERROR (AuthenticationRequired));
-               break;
 
-       case SOUP_STATUS_SSL_FAILED:
-               g_propagate_error (
-                       perror,
-                       e_data_cal_create_error_fmt ( OtherError,
-                       _("Failed to connect to a server using SSL/TLS: %s"),
-                       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"))));
-               if (is_opening && perror && *perror) {
-                       (*perror)->domain = SOUP_HTTP_ERROR;
-                       (*perror)->code = SOUP_STATUS_SSL_FAILED;
-               }
-               break;
+               if (is_ssl_error) {
+                       *out_auth_result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
 
-       default:
-               d (g_debug ("CalDAV:%s: Unhandled status code %d\n", G_STRFUNC, status_code));
-               uri = soup_uri_to_string (soup_message_get_uri (message), FALSE);
-               g_propagate_error (
-                       perror,
-                       e_data_cal_create_error_fmt (
-                               OtherError,
-                               _("Unexpected HTTP status code %d returned (%s) for URI: %s"),
-                                       message->status_code,
-                                       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")),
-                                       uri ? uri : "[null]"));
-               g_free (uri);
-               break;
-       }
-
-       return FALSE;
-}
-
-/* !TS, call with lock held */
-static gboolean
-check_state (ECalBackendCalDAV *cbdav,
-             gboolean *online,
-             GError **perror)
-{
-       *online = FALSE;
-
-       if (!cbdav->priv->loaded) {
-               g_propagate_error (perror, EDC_ERROR_EX (OtherError, _("CalDAV backend is not loaded yet")));
-               return FALSE;
-       }
-
-       if (!e_backend_get_online (E_BACKEND (cbdav))) {
-
-               if (!cbdav->priv->do_offline) {
-                       g_propagate_error (perror, EDC_ERROR (RepositoryOffline));
-                       return FALSE;
+                       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_SSL_FAILED);
+                       e_soup_session_get_ssl_error_details (E_SOUP_SESSION (cbdav->priv->webdav), 
out_certificate_pem, out_certificate_errors);
+               } else {
+                       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
                }
-
-       } else {
-               *online = TRUE;
        }
 
-       return TRUE;
-}
+       if (capabilities)
+               g_hash_table_destroy (capabilities);
+       if (allows)
+               g_hash_table_destroy (allows);
 
-/* ************************************************************************* */
-/* XML Parsing code */
+       if (!success)
+               g_clear_object (&cbdav->priv->webdav);
 
-static xmlXPathObjectPtr
-xpath_eval (xmlXPathContextPtr ctx,
-            const gchar *format,
-            ...)
-{
-       xmlXPathObjectPtr  result;
-       va_list            args;
-       gchar              *expr;
-
-       if (ctx == NULL) {
-               return NULL;
-       }
-
-       va_start (args, format);
-       expr = g_strdup_vprintf (format, args);
-       va_end (args);
-
-       result = xmlXPathEvalExpression ((xmlChar *) expr, ctx);
-       g_free (expr);
-
-       if (result == NULL) {
-               return NULL;
-       }
-
-       if (result->type == XPATH_NODESET &&
-           xmlXPathNodeSetIsEmpty (result->nodesetval)) {
-               xmlXPathFreeObject (result);
-               return NULL;
-       }
-
-       return result;
+       return success;
 }
 
-#if 0
 static gboolean
-parse_status_node (xmlNodePtr node,
-                   guint *status_code)
-{
-       xmlChar  *content;
-       gboolean  res;
-
-       content = xmlNodeGetContent (node);
-
-       res = soup_headers_parse_status_line (
-               (gchar *) content,
-               NULL,
-               status_code,
-               NULL);
-       xmlFree (content);
-
-       return res;
-}
-#endif
-
-static gchar *
-xp_object_get_string (xmlXPathObjectPtr result)
+ecb_caldav_disconnect_sync (ECalMetaBackend *meta_backend,
+                           GCancellable *cancellable,
+                           GError **error)
 {
-       gchar *ret = NULL;
-
-       if (result == NULL)
-               return ret;
+       ECalBackendCalDAV *cbdav;
+       ESource *source;
 
-       if (result->type == XPATH_STRING) {
-               ret = g_strdup ((gchar *) result->stringval);
-       }
+       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
 
-       xmlXPathFreeObject (result);
-       return ret;
-}
+       cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
 
-/* like get_string but will quote the etag if necessary */
-static gchar *
-xp_object_get_etag (xmlXPathObjectPtr result)
-{
-       gchar *ret = NULL;
-       gchar *str;
+       if (cbdav->priv->webdav)
+               soup_session_abort (SOUP_SESSION (cbdav->priv->webdav));
 
-       if (result == NULL)
-               return ret;
+       g_clear_object (&cbdav->priv->webdav);
 
-       if (result->type == XPATH_STRING) {
-               str = (gchar *) result->stringval;
-
-               ret = quote_etag (str);
-       }
+       source = e_backend_get_source (E_BACKEND (meta_backend));
+       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
 
-       xmlXPathFreeObject (result);
-       return ret;
+       return TRUE;
 }
 
-static guint
-xp_object_get_status (xmlXPathObjectPtr result)
+static const gchar *
+ecb_caldav_get_vcalendar_uid (icalcomponent *vcalendar)
 {
-       gboolean res;
-       guint    ret = 0;
+       const gchar *uid = NULL;
+       icalcomponent *subcomp;
 
-       if (result == NULL)
-               return ret;
+       g_return_val_if_fail (vcalendar != NULL, NULL);
+       g_return_val_if_fail (icalcomponent_isa (vcalendar) == ICAL_VCALENDAR_COMPONENT, NULL);
 
-       if (result->type == XPATH_STRING) {
-               res = soup_headers_parse_status_line (
-                       (gchar *) result->stringval,
-                       NULL,
-                       &ret,
-                       NULL);
+       for (subcomp = icalcomponent_get_first_component (vcalendar, ICAL_ANY_COMPONENT);
+            subcomp && !uid;
+            subcomp = icalcomponent_get_next_component (vcalendar, ICAL_ANY_COMPONENT)) {
+               icalcomponent_kind kind = icalcomponent_isa (subcomp);
 
-               if (!res) {
-                       ret = 0;
+               if (kind == ICAL_VEVENT_COMPONENT ||
+                   kind == ICAL_VJOURNAL_COMPONENT ||
+                   kind == ICAL_VTODO_COMPONENT) {
+                       uid = icalcomponent_get_uid (subcomp);
+                       if (uid && !*uid)
+                               uid = NULL;
                }
        }
 
-       xmlXPathFreeObject (result);
-       return ret;
-}
-
-#if 0
-static gint
-xp_object_get_number (xmlXPathObjectPtr result)
-{
-       gint ret = -1;
-
-       if (result == NULL)
-               return ret;
-
-       if (result->type == XPATH_STRING) {
-               ret = result->boolval;
-       }
-
-       xmlXPathFreeObject (result);
-       return ret;
+       return uid;
 }
-#endif
-
-/*** *** *** *** *** *** */
-#define XPATH_HREF "string(/D:multistatus/D:response[%d]/D:href)"
-#define XPATH_STATUS "string(/D:multistatus/D:response[%d]/D:propstat/D:status)"
-#define XPATH_GETETAG_STATUS 
"string(/D:multistatus/D:response[%d]/D:propstat/D:prop/D:getetag/../../D:status)"
-#define XPATH_GETETAG "string(/D:multistatus/D:response[%d]/D:propstat/D:prop/D:getetag)"
-#define XPATH_CALENDAR_DATA "string(/D:multistatus/D:response[%d]/D:propstat/D:prop/C:calendar-data)"
-#define XPATH_GETCTAG_STATUS "string(/D:multistatus/D:response/D:propstat/D:prop/CS:getctag/../../D:status)"
-#define XPATH_GETCTAG "string(/D:multistatus/D:response/D:propstat/D:prop/CS:getctag)"
-#define XPATH_OWNER_STATUS 
"string(/D:multistatus/D:response/D:propstat/D:prop/D:owner/D:href/../../../D:status)"
-#define XPATH_OWNER "string(/D:multistatus/D:response/D:propstat/D:prop/D:owner/D:href)"
-#define XPATH_SCHEDULE_OUTBOX_URL_STATUS 
"string(/D:multistatus/D:response/D:propstat/D:prop/C:schedule-outbox-URL/D:href/../../../D:status)"
-#define XPATH_SCHEDULE_OUTBOX_URL 
"string(/D:multistatus/D:response/D:propstat/D:prop/C:schedule-outbox-URL/D:href)"
-
-typedef struct _CalDAVObject CalDAVObject;
-
-struct _CalDAVObject {
-
-       gchar *href;
-       gchar *etag;
-
-       guint status;
-
-       gchar *cdata;
-};
 
 static void
-caldav_object_free (CalDAVObject *object,
-                    gboolean free_object_itself)
+ecb_caldav_update_nfo_with_vcalendar (ECalMetaBackendInfo *nfo,
+                                     icalcomponent *vcalendar,
+                                     const gchar *etag)
 {
-       g_free (object->href);
-       g_free (object->etag);
-       g_free (object->cdata);
+       icalcomponent *subcomp;
+       const gchar *uid;
 
-       if (free_object_itself) {
-               g_free (object);
-       }
-}
-
-static gboolean
-parse_report_response (SoupMessage *soup_message,
-                       CalDAVObject **objs,
-                       gint *len)
-{
-       xmlXPathContextPtr xpctx;
-       xmlXPathObjectPtr  result;
-       xmlDocPtr          doc;
-       gint                i, n;
-       gboolean           res;
-
-       g_return_val_if_fail (soup_message != NULL, FALSE);
-       g_return_val_if_fail (objs != NULL || len != NULL, FALSE);
-
-       res = TRUE;
-       doc = xmlReadMemory (
-               soup_message->response_body->data,
-               soup_message->response_body->length,
-               "response.xml",
-               NULL,
-               0);
-
-       if (doc == NULL) {
-               return FALSE;
-       }
+       g_return_if_fail (nfo != NULL);
+       g_return_if_fail (vcalendar != NULL);
 
-       xpctx = xmlXPathNewContext (doc);
+       uid = ecb_caldav_get_vcalendar_uid (vcalendar);
 
-       xmlXPathRegisterNs (
-               xpctx, (xmlChar *) "D",
-               (xmlChar *) "DAV:");
+       if (!etag || !*etag)
+               etag = nfo->revision;
 
-       xmlXPathRegisterNs (
-               xpctx, (xmlChar *) "C",
-               (xmlChar *) "urn:ietf:params:xml:ns:caldav");
+       for (subcomp = icalcomponent_get_first_component (vcalendar, ICAL_ANY_COMPONENT);
+            subcomp;
+            subcomp = icalcomponent_get_next_component (vcalendar, ICAL_ANY_COMPONENT)) {
+               icalcomponent_kind kind = icalcomponent_isa (subcomp);
 
-       result = xpath_eval (xpctx, "/D:multistatus/D:response");
-
-       if (result == NULL || result->type != XPATH_NODESET) {
-               *len = 0;
-               res = FALSE;
-               goto out;
-       }
-
-       n = xmlXPathNodeSetGetLength (result->nodesetval);
-       *len = n;
-
-       *objs = g_new0 (CalDAVObject, n);
-
-       for (i = 0; i < n; i++) {
-               CalDAVObject *object;
-               xmlXPathObjectPtr xpres;
-
-               object = *objs + i;
-               /* see if we got a status child in the response element */
-
-               xpres = xpath_eval (xpctx, XPATH_HREF, i + 1);
-               /* use full path from a href, to let calendar-multiget work properly */
-               object->href = xp_object_get_string (xpres);
-
-               xpres = xpath_eval (xpctx,XPATH_STATUS , i + 1);
-               object->status = xp_object_get_status (xpres);
-
-               if (object->status && object->status != 200) {
-                       continue;
+               if (kind == ICAL_VEVENT_COMPONENT ||
+                   kind == ICAL_VJOURNAL_COMPONENT ||
+                   kind == ICAL_VTODO_COMPONENT) {
+                       e_cal_util_set_x_property (subcomp, E_CALDAV_X_ETAG, etag);
                }
-
-               xpres = xpath_eval (xpctx, XPATH_GETETAG_STATUS, i + 1);
-               object->status = xp_object_get_status (xpres);
-
-               if (object->status != 200) {
-                       continue;
-               }
-
-               xpres = xpath_eval (xpctx, XPATH_GETETAG, i + 1);
-               object->etag = xp_object_get_etag (xpres);
-
-               xpres = xpath_eval (xpctx, XPATH_CALENDAR_DATA, i + 1);
-               object->cdata = xp_object_get_string (xpres);
-       }
-
-out:
-       if (result != NULL)
-               xmlXPathFreeObject (result);
-       xmlXPathFreeContext (xpctx);
-       xmlFreeDoc (doc);
-       return res;
-}
-
-/* returns whether was able to read the xpath_value from the server's response; *value contains the result */
-static gboolean
-parse_propfind_response (SoupMessage *message,
-                         const gchar *xpath_status,
-                         const gchar *xpath_value,
-                         gchar **value)
-{
-       xmlXPathContextPtr xpctx;
-       xmlDocPtr          doc;
-       gboolean           res = FALSE;
-
-       g_return_val_if_fail (message != NULL, FALSE);
-       g_return_val_if_fail (value != NULL, FALSE);
-
-       doc = xmlReadMemory (
-               message->response_body->data,
-               message->response_body->length,
-               "response.xml",
-               NULL,
-               0);
-
-       if (doc == NULL) {
-               return FALSE;
-       }
-
-       xpctx = xmlXPathNewContext (doc);
-       xmlXPathRegisterNs (xpctx, (xmlChar *) "D", (xmlChar *) "DAV:");
-       xmlXPathRegisterNs (xpctx, (xmlChar *) "C", (xmlChar *) "urn:ietf:params:xml:ns:caldav");
-       xmlXPathRegisterNs (xpctx, (xmlChar *) "CS", (xmlChar *) "http://calendarserver.org/ns/";);
-
-       if (xpath_status == NULL || xp_object_get_status (xpath_eval (xpctx, xpath_status)) == 200) {
-               gchar *txt = xp_object_get_string (xpath_eval (xpctx, xpath_value));
-
-               if (txt && *txt) {
-                       gint len = strlen (txt);
-
-                       if (*txt == '\"' && len > 2 && txt[len - 1] == '\"') {
-                               /* dequote */
-                               *value = g_strndup (txt + 1, len - 2);
-                       } else {
-                               *value = txt;
-                               txt = NULL;
-                       }
-
-                       res = (*value) != NULL;
-               }
-
-               g_free (txt);
-       }
-
-       xmlXPathFreeContext (xpctx);
-       xmlFreeDoc (doc);
-
-       return res;
-}
-
-/* ************************************************************************* */
-/* Authentication helpers for libsoup */
-
-static void
-soup_authenticate_bearer (SoupSession *session,
-                          SoupMessage *message,
-                          SoupAuth *auth,
-                          ECalBackendCalDAV *cbdav)
-{
-       GError *local_error = NULL;
-
-       caldav_setup_bearer_auth (cbdav, E_SOUP_AUTH_BEARER (auth), NULL, &local_error);
-
-       /* Stash the error to be picked up by caldav_credentials_required_sync().
-        * There's no way to explicitly propagate a GError directly
-        * through libsoup, so we have to work around it. */
-       if (local_error != NULL) {
-               g_mutex_lock (&cbdav->priv->bearer_auth_error_lock);
-
-               /* Warn about an unclaimed error before we clear it.
-                * This is just to verify the errors we set here are
-                * actually making it back to the user. */
-               g_warn_if_fail (cbdav->priv->bearer_auth_error == NULL);
-               g_clear_error (&cbdav->priv->bearer_auth_error);
-
-               g_propagate_error (
-                       &cbdav->priv->bearer_auth_error, local_error);
-
-               g_mutex_unlock (&cbdav->priv->bearer_auth_error_lock);
-       }
-}
-
-static void
-soup_authenticate (SoupSession *session,
-                   SoupMessage *msg,
-                   SoupAuth *auth,
-                   gboolean retrying,
-                   gpointer data)
-{
-       ECalBackendCalDAV *cbdav;
-       ESourceAuthentication *auth_extension;
-       ESource *source;
-       const gchar *extension_name;
-
-       cbdav = E_CAL_BACKEND_CALDAV (data);
-
-       source = e_backend_get_source (E_BACKEND (data));
-       extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
-       auth_extension = e_source_get_extension (source, extension_name);
-
-       if (E_IS_SOUP_AUTH_BEARER (auth)) {
-               g_object_ref (auth);
-               g_warn_if_fail ((gpointer) cbdav->priv->using_bearer_auth == (gpointer) auth);
-               g_clear_object (&cbdav->priv->using_bearer_auth);
-               cbdav->priv->using_bearer_auth = E_SOUP_AUTH_BEARER (auth);
        }
 
-       if (retrying)
-               return;
-
-       if (cbdav->priv->using_bearer_auth) {
-               soup_authenticate_bearer (session, msg, auth, cbdav);
-
-       /* do not send same password twice, but keep it for later use */
-       } else {
-               gchar *auth_user;
-               const gchar *username;
-
-               auth_user = e_source_authentication_dup_user (auth_extension);
-
-               username = cbdav->priv->credentials ? e_named_parameters_get (cbdav->priv->credentials, 
E_SOURCE_CREDENTIAL_USERNAME) : NULL;
-               if (!username || !*username)
-                       username = auth_user;
+       g_warn_if_fail (nfo->object == NULL);
+       nfo->object = icalcomponent_as_ical_string_r (vcalendar);
 
-               if (!username || !*username || !cbdav->priv->credentials ||
-                   !e_named_parameters_exists (cbdav->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD))
-                       soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
-               else
-                       soup_auth_authenticate (auth, username, e_named_parameters_get 
(cbdav->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD));
-
-               g_free (auth_user);
+       if (!nfo->uid || !*(nfo->uid)) {
+               g_free (nfo->uid);
+               nfo->uid = g_strdup (uid);
        }
-}
-
-/* ************************************************************************* */
-/* direct CalDAV server access functions */
-
-static void
-redirect_handler (SoupMessage *msg,
-                  gpointer user_data)
-{
-       if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
-               SoupSession *soup_session = user_data;
-               SoupURI *new_uri;
-               const gchar *new_loc;
-
-               new_loc = soup_message_headers_get_list (msg->response_headers, "Location");
-               if (!new_loc)
-                       return;
-
-               new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
-               if (!new_uri) {
-                       soup_message_set_status_full (
-                               msg,
-                               SOUP_STATUS_MALFORMED,
-                               _("Invalid Redirect URL"));
-                       return;
-               }
-
-               if (new_uri->host && g_str_has_suffix (new_uri->host, "yahoo.com")) {
-                       /* yahoo! returns port 7070, which is unreachable;
-                        * it also requires https being used (below call resets port as well) */
-                       soup_uri_set_scheme (new_uri, SOUP_URI_SCHEME_HTTPS);
-               }
 
-               soup_message_set_uri (msg, new_uri);
-               soup_session_requeue_message (soup_session, msg);
+       if (g_strcmp0 (etag, nfo->revision) != 0) {
+               gchar *copy = g_strdup (etag);
 
-               soup_uri_free (new_uri);
+               g_free (nfo->revision);
+               nfo->revision = copy;
        }
 }
 
-static void
-send_and_handle_redirection (ECalBackendCalDAV *cbdav,
-                             SoupMessage *msg,
-                             gchar **new_location,
-                             GCancellable *cancellable,
-                             GError **error)
+static gboolean
+ecb_caldav_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)
 {
-       gchar *old_uri = NULL;
-
-       g_return_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav));
-       g_return_if_fail (msg != NULL);
-
-       if (new_location)
-               old_uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
-
-       e_soup_ssl_trust_connect (msg, e_backend_get_source (E_BACKEND (cbdav)));
-
-       if (cbdav->priv->using_bearer_auth &&
-           e_soup_auth_bearer_is_expired (cbdav->priv->using_bearer_auth)) {
-               GError *local_error = NULL;
-
-               if (!caldav_setup_bearer_auth (cbdav, cbdav->priv->using_bearer_auth, cancellable, 
&local_error)) {
-                       if (local_error) {
-                               soup_message_set_status_full (msg, SOUP_STATUS_BAD_REQUEST, 
local_error->message);
-                               g_propagate_error (error, local_error);
-                       } else {
-                               soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
-                       }
-                       return;
-               }
-       }
-
-       soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
-       soup_message_add_header_handler (msg, "got_body", "Location", G_CALLBACK (redirect_handler), 
cbdav->priv->session);
-       soup_message_headers_append (msg->request_headers, "Connection", "close");
-       soup_session_send_message (cbdav->priv->session, msg);
+       GSList **from_link = user_data;
 
-       if (new_location) {
-               gchar *new_loc = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
+       g_return_val_if_fail (from_link != NULL, FALSE);
 
-               if (new_loc && old_uri && !g_str_equal (new_loc, old_uri))
-                       *new_location = new_loc;
-               else
-                       g_free (new_loc);
-       }
+       if (!xpath_prop_prefix) {
+               e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CALDAV, NULL);
+       } else if (status_code == SOUP_STATUS_OK) {
+               gchar *calendar_data, *etag;
 
-       g_free (old_uri);
-
-       if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
-               e_backend_ensure_source_status_connected (E_BACKEND (cbdav));
-}
-
-static gchar *
-caldav_generate_uri (ECalBackendCalDAV *cbdav,
-                     const gchar *target)
-{
-       gchar *uri;
-       const gchar *slash;
+               g_return_val_if_fail (href != NULL, FALSE);
 
-       slash = strrchr (target, '/');
-       if (slash)
-               target = slash + 1;
+               calendar_data = e_xml_xpath_eval_as_string (xpath_ctx, "%s/C:calendar-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));
 
-       /* uri *have* trailing slash already */
-       uri = g_strconcat (cbdav->priv->uri, target, NULL);
+               if (calendar_data) {
+                       icalcomponent *vcalendar;
 
-       return uri;
-}
+                       vcalendar = icalcomponent_new_from_string (calendar_data);
+                       if (vcalendar) {
+                               const gchar *uid;
 
-static gboolean
-caldav_server_open_calendar (ECalBackendCalDAV *cbdav,
-                             gboolean *server_unreachable,
-                            gchar **out_certificate_pem,
-                            GTlsCertificateFlags *out_certificate_errors,
-                             GCancellable *cancellable,
-                             GError **perror)
-{
-       SoupMessage *message;
-       const gchar *header;
-       gboolean calendar_access;
-       gboolean put_allowed;
-       gboolean delete_allowed;
-       ESource *source;
+                               uid = ecb_caldav_get_vcalendar_uid (vcalendar);
+                               if (uid) {
+                                       GSList *link;
 
-       g_return_val_if_fail (cbdav != NULL, FALSE);
-       g_return_val_if_fail (server_unreachable != NULL, FALSE);
+                                       for (link = *from_link; link; link = g_slist_next (link)) {
+                                               ECalMetaBackendInfo *nfo = link->data;
 
-       message = soup_message_new (SOUP_METHOD_OPTIONS, cbdav->priv->uri);
-       if (message == NULL) {
-               g_propagate_error (perror, EDC_ERROR (NoSuchCal));
-               return FALSE;
-       }
+                                               if (!nfo)
+                                                       continue;
 
-       soup_message_headers_append (
-               message->request_headers,
-               "User-Agent", "Evolution/" VERSION);
+                                               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);
 
-       source = e_backend_get_source (E_BACKEND (cbdav));
-       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
+                                                       ecb_caldav_update_nfo_with_vcalendar (nfo, vcalendar, 
etag);
 
-       send_and_handle_redirection (cbdav, message, NULL, cancellable, perror);
-
-       if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
-               e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
-
-               switch (message->status_code) {
-               case SOUP_STATUS_CANT_RESOLVE:
-               case SOUP_STATUS_CANT_RESOLVE_PROXY:
-               case SOUP_STATUS_CANT_CONNECT:
-               case SOUP_STATUS_CANT_CONNECT_PROXY:
-                       *server_unreachable = TRUE;
-                       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);
+                                                       break;
+                                               }
+                                       }
                                }
+
+                               icalcomponent_free (vcalendar);
                        }
-                       break;
                }
 
-               status_code_to_result (message, cbdav, TRUE, perror);
-
-               g_object_unref (message);
-               return FALSE;
-       }
-
-       /* parse the dav header, we are intreseted in the
-        * calendar-access bit only at the moment */
-       header = soup_message_headers_get_list (message->response_headers, "DAV");
-       if (header) {
-               calendar_access = soup_header_contains (header, "calendar-access");
-               cbdav->priv->calendar_schedule = soup_header_contains (header, "calendar-schedule");
-       } else {
-               calendar_access = FALSE;
-               cbdav->priv->calendar_schedule = FALSE;
-       }
-
-       /* parse the Allow header and look for PUT, DELETE at the
-        * moment (maybe we should check more here, for REPORT eg) */
-       header = soup_message_headers_get_list (message->response_headers, "Allow");
-       if (header) {
-               /* The POST added for FastMail servers, which doesn't advertise PUT on collections. */
-               put_allowed = soup_header_contains (header, "PUT") || soup_header_contains (header, "POST");
-               delete_allowed = soup_header_contains (header, "DELETE");
-       } else
-               put_allowed = delete_allowed = FALSE;
-
-       g_object_unref (message);
-
-       if (calendar_access) {
-               e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
-               e_cal_backend_set_writable (
-                       E_CAL_BACKEND (cbdav),
-                       put_allowed && delete_allowed);
-               return TRUE;
+               g_free (calendar_data);
+               g_free (etag);
        }
 
-       e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
-       g_propagate_error (perror, EDC_ERROR (PermissionDenied));
-       return FALSE;
-}
-
-static gpointer
-caldav_unref_thread (gpointer cbdav)
-{
-       g_object_unref (cbdav);
-
-       return NULL;
-}
-
-static void
-caldav_unref_in_thread (ECalBackendCalDAV *cbdav)
-{
-       GThread *thread;
-
-       g_return_if_fail (cbdav != NULL);
-
-       thread = g_thread_new (NULL, caldav_unref_thread, cbdav);
-       g_thread_unref (thread);
+       return TRUE;
 }
 
 static gboolean
-caldav_credentials_required_sync (ECalBackendCalDAV *cbdav,
-                                 gboolean ref_cbdav,
-                                 gboolean first_attempt,
-                                 GCancellable *cancellable,
-                                 GError **error)
-{
+ecb_caldav_multiget_from_sets_sync (ECalBackendCalDAV *cbdav,
+                                   GSList **in_link,
+                                   GSList **set2,
+                                   GCancellable *cancellable,
+                                   GError **error)
+{
+       EXmlDocument *xml;
+       gint left_to_go = E_CALDAV_MAX_MULTIGET_AMOUNT;
+       GSList *link;
        gboolean success = TRUE;
 
-       if (ref_cbdav)
-               g_object_ref (cbdav);
-
-       /* This function is called when we receive a 4xx response code for
-        * authentication failures.  If we're using Bearer authentication,
-        * there should be a GError available.  Return the GError to avoid
-        * inappropriately prompting for a password. */
-       g_mutex_lock (&cbdav->priv->bearer_auth_error_lock);
-       if (cbdav->priv->bearer_auth_error != NULL) {
-               g_propagate_error (error, cbdav->priv->bearer_auth_error);
-               cbdav->priv->bearer_auth_error = NULL;
-               success = FALSE;
-       }
-       g_mutex_unlock (&cbdav->priv->bearer_auth_error_lock);
+       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 (success) {
-               success = e_backend_credentials_required_sync (E_BACKEND (cbdav),
-                       (first_attempt || !cbdav->priv->credentials ||
-                        !e_named_parameters_exists (cbdav->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD))
-                       ? E_SOURCE_CREDENTIALS_REASON_REQUIRED :
-                       E_SOURCE_CREDENTIALS_REASON_REJECTED,
-                       NULL, 0, NULL, cancellable, error);
-       }
+       xml = e_xml_document_new (E_WEBDAV_NS_CALDAV, "calendar-multiget");
+       g_return_val_if_fail (xml != NULL, FALSE);
 
-       if (ref_cbdav)
-               caldav_unref_in_thread (cbdav);
+       e_xml_document_add_namespaces (xml, "D", E_WEBDAV_NS_DAV, NULL);
 
-       return success;
-}
+       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_CALDAV, "calendar-data");
+       e_xml_document_end_element (xml); /* prop */
 
-static gconstpointer
-compat_libxml_output_buffer_get_content (xmlOutputBufferPtr buf,
-                                         gsize *out_len)
-{
-#ifdef LIBXML2_NEW_BUFFER
-       *out_len = xmlOutputBufferGetSize (buf);
-       return xmlOutputBufferGetContent (buf);
-#else
-       *out_len = buf->buffer->use;
-       return buf->buffer->content;
-#endif
-}
+       link = *in_link;
 
-/* Returns whether calendar changed on the server. This works only when server
- * supports 'getctag' extension. */
-static gboolean
-check_calendar_changed_on_server (ECalBackendCalDAV *cbdav,
-                                 gboolean save_ctag,
-                                 GCancellable *cancellable)
-{
-       xmlOutputBufferPtr        buf;
-       SoupMessage              *message;
-       xmlDocPtr                 doc;
-       xmlNodePtr                root, node;
-       xmlNsPtr                  ns, nsdav;
-       gconstpointer             buf_content;
-       gsize                     buf_size;
-       gboolean                  result = TRUE;
-
-       g_return_val_if_fail (cbdav != NULL, TRUE);
-
-       /* no support for 'getctag', thus update cache */
-       if (!cbdav->priv->ctag_supported)
-               return TRUE;
-
-       /* Prepare the soup message */
-       message = soup_message_new ("PROPFIND", cbdav->priv->uri);
-       if (message == NULL)
-               return FALSE;
+       while (link && left_to_go > 0) {
+               ECalMetaBackendInfo *nfo = link->data;
 
-       doc = xmlNewDoc ((xmlChar *) "1.0");
-       root = xmlNewDocNode (doc, NULL, (xmlChar *) "propfind", NULL);
-       xmlDocSetRootElement (doc, root);
-       nsdav = xmlNewNs (root, (xmlChar *) "DAV:", NULL);
-       ns = xmlNewNs (root, (xmlChar *) "http://calendarserver.org/ns/";, (xmlChar *) "CS");
-
-       node = xmlNewTextChild (root, nsdav, (xmlChar *) "prop", NULL);
-       node = xmlNewTextChild (node, nsdav, (xmlChar *) "getctag", NULL);
-       xmlSetNs (node, ns);
-
-       buf = xmlAllocOutputBuffer (NULL);
-       xmlNodeDumpOutput (buf, doc, root, 0, 1, NULL);
-       xmlOutputBufferFlush (buf);
-
-       soup_message_headers_append (
-               message->request_headers,
-               "User-Agent", "Evolution/" VERSION);
-       soup_message_headers_append (
-               message->request_headers,
-               "Depth", "0");
-
-       buf_content = compat_libxml_output_buffer_get_content (buf, &buf_size);
-       soup_message_set_request (
-               message,
-               "application/xml",
-               SOUP_MEMORY_COPY,
-               buf_content, buf_size);
-
-       /* Send the request now */
-       send_and_handle_redirection (cbdav, message, NULL, cancellable, NULL);
-
-       /* Clean up the memory */
-       xmlOutputBufferClose (buf);
-       xmlFreeDoc (doc);
-
-       /* Check the result */
-       if (message->status_code == SOUP_STATUS_UNAUTHORIZED || message->status_code == 
SOUP_STATUS_FORBIDDEN) {
-               caldav_credentials_required_sync (cbdav, TRUE, FALSE, NULL, NULL);
-       } else if (message->status_code != SOUP_STATUS_MULTI_STATUS) {
-               /* does not support it, but report calendar changed to update cache */
-               cbdav->priv->ctag_supported = FALSE;
-       } else {
-               gchar *ctag = NULL;
-
-               if (parse_propfind_response (message, XPATH_GETCTAG_STATUS, XPATH_GETCTAG, &ctag)) {
-                       const gchar *my_ctag;
-
-                       my_ctag = e_cal_backend_store_get_key_value (
-                               cbdav->priv->store, CALDAV_CTAG_KEY);
-
-                       if (ctag && my_ctag && g_str_equal (ctag, my_ctag)) {
-                               /* ctag is same, no change in the calendar */
-                               result = FALSE;
-                       } else if (save_ctag) {
-                               /* do not store ctag now, do it rather after complete sync */
-                               g_free (cbdav->priv->ctag_to_store);
-                               cbdav->priv->ctag_to_store = ctag;
-                               ctag = NULL;
-                       }
-
-                       g_free (ctag);
-               } else {
-                       cbdav->priv->ctag_supported = FALSE;
+               link = g_slist_next (link);
+               if (!link) {
+                       link = *set2;
+                       *set2 = NULL;
                }
-       }
 
-       g_object_unref (message);
+               if (!nfo)
+                       continue;
 
-       return result;
-}
+               left_to_go--;
 
-/* only_hrefs is a list of requested objects to fetch; it has precedence from
- * start_time/end_time, which are used only when both positive.
- * Times are supposed to be in UTC, if set.
- */
-static gboolean
-caldav_server_list_objects (ECalBackendCalDAV *cbdav,
-                            CalDAVObject **objs,
-                            gint *len,
-                            GSList *only_hrefs,
-                            time_t start_time,
-                            time_t end_time,
-                           GCancellable *cancellable)
-{
-       xmlOutputBufferPtr   buf;
-       SoupMessage         *message;
-       xmlNodePtr           node;
-       xmlNodePtr           sn;
-       xmlNodePtr           root;
-       xmlDocPtr            doc;
-       xmlNsPtr             nsdav;
-       xmlNsPtr             nscd;
-       gconstpointer        buf_content;
-       gsize                buf_size;
-       gboolean             result;
-
-       /* Allocate the soup message */
-       message = soup_message_new ("REPORT", cbdav->priv->uri);
-       if (message == NULL)
-               return FALSE;
+               /* iCloud returns broken calendar-multiget responses, with
+                  empty <DAV:href> elements, thus read one-by-one for it.
+                  This is confirmed as of 2017-04-11. */
+               if (cbdav->priv->is_icloud) {
+                       gchar *calendar_data = NULL, *etag = NULL;
 
-       /* Maybe we should just do a g_strdup_printf here? */
-       /* Prepare request body */
-       doc = xmlNewDoc ((xmlChar *) "1.0");
-       if (!only_hrefs)
-               root = xmlNewDocNode (doc, NULL, (xmlChar *) "calendar-query", NULL);
-       else
-               root = xmlNewDocNode (doc, NULL, (xmlChar *) "calendar-multiget", NULL);
-       nscd = xmlNewNs (root, (xmlChar *) "urn:ietf:params:xml:ns:caldav", (xmlChar *) "C");
-       xmlSetNs (root, nscd);
-       xmlDocSetRootElement (doc, root);
-
-       /* Add webdav tags */
-       nsdav = xmlNewNs (root, (xmlChar *) "DAV:", (xmlChar *) "D");
-       node = xmlNewTextChild (root, nsdav, (xmlChar *) "prop", NULL);
-       xmlNewTextChild (node, nsdav, (xmlChar *) "getetag", NULL);
-       if (only_hrefs) {
-               GSList *l;
-
-               xmlNewTextChild (node, nscd, (xmlChar *) "calendar-data", NULL);
-               for (l = only_hrefs; l; l = l->next) {
-                       if (l->data) {
-                               xmlNewTextChild (root, nsdav, (xmlChar *) "href", (xmlChar *) l->data);
-                       }
-               }
-       } else {
-               node = xmlNewTextChild (root, nscd, (xmlChar *) "filter", NULL);
-               node = xmlNewTextChild (node, nscd, (xmlChar *) "comp-filter", NULL);
-               xmlSetProp (node, (xmlChar *) "name", (xmlChar *) "VCALENDAR");
-
-               sn = xmlNewTextChild (node, nscd, (xmlChar *) "comp-filter", NULL);
-               switch (e_cal_backend_get_kind (E_CAL_BACKEND (cbdav))) {
-                       default:
-                       case ICAL_VEVENT_COMPONENT:
-                               xmlSetProp (sn, (xmlChar *) "name", (xmlChar *) "VEVENT");
-                               break;
-                       case ICAL_VJOURNAL_COMPONENT:
-                               xmlSetProp (sn, (xmlChar *) "name", (xmlChar *) "VJOURNAL");
-                               break;
-                       case ICAL_VTODO_COMPONENT:
-                               xmlSetProp (sn, (xmlChar *) "name", (xmlChar *) "VTODO");
-                               break;
-               }
-
-               if (start_time > 0 || end_time > 0) {
-                       gchar *tmp;
+                       success = e_webdav_session_get_data_sync (cbdav->priv->webdav,
+                               nfo->extra, NULL, &etag, &calendar_data, NULL, cancellable, error);
 
-                       sn = xmlNewTextChild (sn, nscd, (xmlChar *) "time-range", NULL);
+                       if (success && calendar_data) {
+                               icalcomponent *vcalendar;
 
-                       if (start_time > 0) {
-                               tmp = isodate_from_time_t (start_time);
-                               xmlSetProp (sn, (xmlChar *) "start", (xmlChar *) tmp);
-                               g_free (tmp);
+                               vcalendar = icalcomponent_new_from_string (calendar_data);
+                               if (vcalendar) {
+                                       ecb_caldav_update_nfo_with_vcalendar (nfo, vcalendar, etag);
+                                       icalcomponent_free (vcalendar);
+                               }
                        }
 
-                       if (end_time > 0) {
-                               tmp = isodate_from_time_t (end_time);
-                               xmlSetProp (sn, (xmlChar *) "end", (xmlChar *) tmp);
-                               g_free (tmp);
-                       }
-               }
-       }
+                       g_free (calendar_data);
+                       g_free (etag);
 
-       buf = xmlAllocOutputBuffer (NULL);
-       xmlNodeDumpOutput (buf, doc, root, 0, 1, NULL);
-       xmlOutputBufferFlush (buf);
-
-       /* Prepare the soup message */
-       soup_message_headers_append (
-               message->request_headers,
-               "User-Agent", "Evolution/" VERSION);
-       soup_message_headers_append (
-               message->request_headers,
-               "Depth", "1");
-
-       buf_content = compat_libxml_output_buffer_get_content (buf, &buf_size);
-       soup_message_set_request (
-               message,
-               "application/xml",
-               SOUP_MEMORY_COPY,
-               buf_content, buf_size);
-
-       /* Send the request now */
-       send_and_handle_redirection (cbdav, message, NULL, cancellable, NULL);
-
-       /* Clean up the memory */
-       xmlOutputBufferClose (buf);
-       xmlFreeDoc (doc);
-
-       /* Check the result */
-       if (message->status_code != SOUP_STATUS_MULTI_STATUS) {
-               switch (message->status_code) {
-               case SOUP_STATUS_CANT_RESOLVE:
-               case SOUP_STATUS_CANT_RESOLVE_PROXY:
-               case SOUP_STATUS_CANT_CONNECT:
-               case SOUP_STATUS_CANT_CONNECT_PROXY:
-                       cbdav->priv->opened = FALSE;
-                       update_slave_cmd (cbdav->priv, SLAVE_SHOULD_SLEEP);
-                       e_cal_backend_set_writable (
-                               E_CAL_BACKEND (cbdav), FALSE);
-                       break;
-               case SOUP_STATUS_UNAUTHORIZED:
-               case SOUP_STATUS_FORBIDDEN:
-                       caldav_credentials_required_sync (cbdav, TRUE, FALSE, NULL, NULL);
-                       break;
-               default:
-                       g_warning ("Server did not response with SOUP_STATUS_MULTI_STATUS, but with code %d 
(%s)", message->status_code, soup_status_get_phrase (message->status_code) ? soup_status_get_phrase 
(message->status_code) : "Unknown code");
-                       break;
+                       if (!success)
+                               break;
+               } else {
+                       e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "href");
+                       e_xml_document_write_string (xml, nfo->extra);
+                       e_xml_document_end_element (xml); /* href */
                }
-
-               g_object_unref (message);
-               return FALSE;
        }
 
-       /* Parse the response body */
-       result = parse_report_response (message, objs, len);
-
-       g_object_unref (message);
-       return result;
-}
-
-static gboolean
-caldav_server_download_attachment (ECalBackendCalDAV *cbdav,
-                                   const gchar *attachment_uri,
-                                   gchar **content,
-                                   gsize *len,
-                                   GError **error)
-{
-       SoupMessage *message;
-
-       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
-       g_return_val_if_fail (attachment_uri != NULL, FALSE);
-       g_return_val_if_fail (content != NULL, FALSE);
-       g_return_val_if_fail (len != NULL, FALSE);
+       if (left_to_go != E_CALDAV_MAX_MULTIGET_AMOUNT &&
+           !cbdav->priv->is_icloud && success) {
+               GSList *from_link = *in_link;
 
-       message = soup_message_new (SOUP_METHOD_GET, attachment_uri);
-       if (message == NULL) {
-               g_propagate_error (error, EDC_ERROR (InvalidObject));
-               return FALSE;
+               success = e_webdav_session_report_sync (cbdav->priv->webdav, NULL, NULL, xml,
+                       ecb_caldav_multiget_response_cb, &from_link, NULL, NULL, cancellable, error);
        }
 
-       soup_message_headers_append (message->request_headers, "User-Agent", "Evolution/" VERSION);
-       send_and_handle_redirection (cbdav, message, NULL, NULL, NULL);
-
-       if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
-               status_code_to_result (message, cbdav, FALSE, error);
+       g_object_unref (xml);
 
-               if (message->status_code == SOUP_STATUS_UNAUTHORIZED || message->status_code == 
SOUP_STATUS_FORBIDDEN)
-                       caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
+       *in_link = link;
 
-               g_object_unref (message);
-               return FALSE;
-       }
-
-       *len = message->response_body->length;
-       *content = g_memdup (message->response_body->data, *len);
-
-       g_object_unref (message);
-
-       return TRUE;
+       return success;
 }
 
 static gboolean
-caldav_server_get_object (ECalBackendCalDAV *cbdav,
-                          CalDAVObject *object,
-                          GCancellable *cancellable,
-                          GError **perror)
+ecb_caldav_get_calendar_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)
 {
-       SoupMessage              *message;
-       const gchar               *hdr;
-       gchar                     *uri;
-
-       g_return_val_if_fail (object != NULL && object->href != NULL, FALSE);
+       GHashTable *known_items = user_data; /* gchar *href ~> ECalMetaBackendInfo * */
 
-       uri = caldav_generate_uri (cbdav, object->href);
-       message = soup_message_new (SOUP_METHOD_GET, uri);
-       if (message == NULL) {
-               g_free (uri);
-               g_propagate_error (perror, EDC_ERROR (NoSuchCal));
-               return FALSE;
-       }
+       g_return_val_if_fail (xpath_ctx != NULL, FALSE);
+       g_return_val_if_fail (known_items != NULL, FALSE);
 
-       soup_message_headers_append (
-               message->request_headers,
-               "User-Agent", "Evolution/" VERSION);
+       if (!xpath_prop_prefix) {
+               e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CALDAV, NULL);
+       } else if (status_code == SOUP_STATUS_OK) {
+               ECalMetaBackendInfo *nfo;
+               gchar *etag;
 
-       send_and_handle_redirection (cbdav, message, NULL, cancellable, perror);
+               g_return_val_if_fail (href != NULL, FALSE);
 
-       if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
-               status_code_to_result (message, cbdav, FALSE, perror);
+               /* 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))
+                       return TRUE;
 
-               if (message->status_code == SOUP_STATUS_UNAUTHORIZED || message->status_code == 
SOUP_STATUS_FORBIDDEN)
-                       caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
-               else if (message->status_code != SOUP_STATUS_NOT_FOUND)
-                       g_warning ("Could not fetch object '%s' from server, status:%d (%s)", uri, 
message->status_code, soup_status_get_phrase (message->status_code) ? soup_status_get_phrase 
(message->status_code) : "Unknown code");
-               g_object_unref (message);
-               g_free (uri);
-               return FALSE;
-       }
+               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);
 
-       hdr = soup_message_headers_get_list (message->response_headers, "Content-Type");
-
-       if (hdr == NULL || g_ascii_strncasecmp (hdr, "text/calendar", 13)) {
-               g_propagate_error (perror, EDC_ERROR (InvalidObject));
-               g_object_unref (message);
-               g_warning ("Object to fetch '%s' not of type text/calendar", uri);
-               g_free (uri);
-               return FALSE;
-       }
+               /* UID is unknown at this moment */
+               nfo = e_cal_meta_backend_info_new ("", etag, NULL, href);
 
-       hdr = soup_message_headers_get_list (message->response_headers, "ETag");
+               g_free (etag);
+               g_return_val_if_fail (nfo != NULL, FALSE);
 
-       if (hdr != NULL) {
-               g_free (object->etag);
-               object->etag = quote_etag (hdr);
-       } else if (!object->etag) {
-               g_warning ("UUHH no ETag, now that's bad! (at '%s')", uri);
+               g_hash_table_insert (known_items, g_strdup (href), nfo);
        }
-       g_free (uri);
-
-       g_free (object->cdata);
-       object->cdata = g_strdup (message->response_body->data);
-
-       g_object_unref (message);
 
        return TRUE;
 }
 
-static void
-caldav_post_freebusy (ECalBackendCalDAV *cbdav,
-                      const gchar *url,
-                      gchar **post_fb,
-                      GCancellable *cancellable,
-                      GError **error)
-{
-       SoupMessage *message;
-
-       message = soup_message_new (SOUP_METHOD_POST, url);
-       if (message == NULL) {
-               g_propagate_error (error, EDC_ERROR (NoSuchCal));
-               return;
-       }
-
-       soup_message_headers_append (message->request_headers, "User-Agent", "Evolution/" VERSION);
-       soup_message_set_request (
-               message,
-               "text/calendar; charset=utf-8",
-               SOUP_MEMORY_COPY,
-               *post_fb, strlen (*post_fb));
-
-       send_and_handle_redirection (cbdav, message, NULL, cancellable, error);
-
-       if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
-               status_code_to_result (message, cbdav, FALSE, error);
-               if (message->status_code == SOUP_STATUS_UNAUTHORIZED || message->status_code == 
SOUP_STATUS_FORBIDDEN)
-                       caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
-               else
-                       g_warning ("Could not post free/busy request to '%s', status:%d (%s)", url, 
message->status_code, soup_status_get_phrase (message->status_code) ? soup_status_get_phrase 
(message->status_code) : "Unknown code");
-
-               g_object_unref (message);
-
-               return;
-       }
-
-       g_free (*post_fb);
-       *post_fb = g_strdup (message->response_body->data);
-
-       g_object_unref (message);
-}
-
-static gchar *
-caldav_gen_file_from_uid (ECalBackendCalDAV *cbdav,
-                         const gchar *uid)
-{
-       gchar *filename, *res;
-
-       if (!uid)
-               return NULL;
-
-       filename = g_strconcat (uid, ".ics", NULL);
-       res = soup_uri_encode (filename, NULL);
-       g_free (filename);
-
-       return res;
-}
-
-static gchar *
-caldav_gen_file_from_uid_cal (ECalBackendCalDAV *cbdav,
-                              icalcomponent *icalcomp)
-{
-       icalcomponent_kind my_kind;
-       const gchar *uid = NULL;
-
-       g_return_val_if_fail (cbdav != NULL, NULL);
-       g_return_val_if_fail (icalcomp != NULL, NULL);
-
-       my_kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbdav));
-       if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) {
-               icalcomponent *subcomp;
-
-               for (subcomp = icalcomponent_get_first_component (icalcomp, my_kind);
-                    subcomp;
-                    subcomp = icalcomponent_get_next_component (icalcomp, my_kind)) {
-                       uid = icalcomponent_get_uid (subcomp);
-                       if (uid && *uid)
-                               break;
-               }
-       } else if (icalcomponent_isa (icalcomp) == my_kind) {
-               uid = icalcomponent_get_uid (icalcomp);
-       }
-
-       return caldav_gen_file_from_uid (cbdav, uid);
-}
+typedef struct _CalDAVChangesData {
+       gboolean is_repeat;
+       GSList **out_modified_objects;
+       GSList **out_removed_objects;
+       GHashTable *known_items; /* gchar *href ~> ECalMetaBackendInfo * */
+} CalDAVChangesData;
 
 static gboolean
-caldav_server_put_object (ECalBackendCalDAV *cbdav,
-                          CalDAVObject *object,
-                          icalcomponent *icalcomp,
-                          GCancellable *cancellable,
-                          GError **perror)
-{
-       SoupMessage              *message;
-       const gchar               *hdr;
-       gchar                     *uri;
-
-       hdr = NULL;
-
-       g_return_val_if_fail (object != NULL && object->cdata != NULL, FALSE);
-
-       uri = caldav_generate_uri (cbdav, object->href);
-       message = soup_message_new (SOUP_METHOD_PUT, uri);
-       g_free (uri);
-       if (message == NULL) {
-               g_propagate_error (perror, EDC_ERROR (NoSuchCal));
-               return FALSE;
-       }
-
-       soup_message_headers_append (
-               message->request_headers,
-               "User-Agent", "Evolution/" VERSION);
-
-       /* For new items we use the If-None-Match so we don't
-        * acidently override resources, for item updates we
-        * use the If-Match header to avoid the Lost-update
-        * problem */
-       if (object->etag == NULL) {
-               soup_message_headers_append (message->request_headers, "If-None-Match", "*");
-       } else {
-               soup_message_headers_append (
-                       message->request_headers,
-                       "If-Match", object->etag);
-       }
-
-       soup_message_set_request (
-               message,
-               "text/calendar; charset=utf-8",
-               SOUP_MEMORY_COPY,
-               object->cdata,
-               strlen (object->cdata));
-
-       uri = NULL;
-       send_and_handle_redirection (cbdav, message, &uri, cancellable, perror);
-
-       if (uri) {
-               gchar *file = strrchr (uri, '/');
-
-               /* there was a redirect, update href properly */
-               if (file) {
-                       gchar *decoded;
-
-                       g_free (object->href);
-
-                       decoded = soup_uri_decode (file + 1);
-                       object->href = soup_uri_encode (decoded ? decoded : (file + 1), NULL);
-
-                       g_free (decoded);
-               }
-
-               g_free (uri);
-       }
-
-       if (status_code_to_result (message, cbdav, FALSE, perror)) {
-               GError *local_error = NULL;
-
-               hdr = soup_message_headers_get_list (message->response_headers, "ETag");
-               if (hdr != NULL) {
-                       g_free (object->etag);
-                       object->etag = quote_etag (hdr);
-               }
-
-               /* "201 Created" can contain a Location with a link where the component was saved */
-               hdr = soup_message_headers_get_list (message->response_headers, "Location");
-               if (hdr) {
-                       /* reflect possible href change */
-                       gchar *file = strrchr (hdr, '/');
-
-                       if (file) {
-                               gchar *decoded;
-
-                               g_free (object->href);
-
-                               decoded = soup_uri_decode (file + 1);
-                               object->href = soup_uri_encode (decoded ? decoded : (file + 1), NULL);
-
-                               g_free (decoded);
-                       }
-               }
-
-               if (!caldav_server_get_object (cbdav, object, cancellable, &local_error)) {
-                       if (g_error_matches (local_error, E_DATA_CAL_ERROR, ObjectNotFound)) {
-                               gchar *file;
-
-                               /* OK, the event was properly created, but cannot be found on the place
-                                * where it was PUT - why didn't server tell us where it saved it? */
-                               g_clear_error (&local_error);
-
-                               /* try whether it's saved as its UID.ics file */
-                               file = caldav_gen_file_from_uid_cal (cbdav, icalcomp);
-                               if (file) {
-                                       g_free (object->href);
-                                       object->href = file;
+ecb_caldav_search_changes_cb (ECalCache *cal_cache,
+                             const gchar *uid,
+                             const gchar *rid,
+                             const gchar *revision,
+                             const gchar *object,
+                             const gchar *extra,
+                             EOfflineState offline_state,
+                             gpointer user_data)
+{
+       CalDAVChangesData *ccd = user_data;
+
+       g_return_val_if_fail (ccd != NULL, FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
 
-                                       if (!caldav_server_get_object (cbdav, object, cancellable, 
&local_error)) {
-                                               if (g_error_matches (local_error, E_DATA_CAL_ERROR, 
ObjectNotFound)) {
-                                                       g_clear_error (&local_error);
+       /* Can be NULL for added components in offline mode */
+       if (extra && *extra && (!rid || !*rid)) {
+               ECalMetaBackendInfo *nfo;
 
-                                                       /* not sure what can happen, but do not need to guess 
for ever,
-                                                        * thus report success and update the calendar to get 
fresh info */
-                                                       update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
-                                                       g_cond_signal (&cbdav->priv->cond);
-                                               }
-                                       }
+               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 (!local_error) {
-                       icalcomponent *use_comp = NULL;
+                               *(ccd->out_modified_objects) = g_slist_prepend (*(ccd->out_modified_objects),
+                                       e_cal_meta_backend_info_copy (nfo));
 
-                       if (object->cdata) {
-                               /* maybe server also modified component, thus rather store the server's */
-                               use_comp = icalparser_parse_string (object->cdata);
+                               g_hash_table_remove (ccd->known_items, extra);
                        }
-
-                       if (!use_comp)
-                               use_comp = icalcomp;
-
-                       put_comp_to_cache (cbdav, use_comp, object->href, object->etag);
-
-                       if (use_comp != icalcomp)
-                               icalcomponent_free (use_comp);
-               } else {
-                       g_propagate_error (perror, local_error);
+               } else if (ccd->is_repeat) {
+                       *(ccd->out_removed_objects) = g_slist_prepend (*(ccd->out_removed_objects),
+                               e_cal_meta_backend_info_new (uid, revision, object, extra));
                }
        }
 
-       if (message->status_code == SOUP_STATUS_UNAUTHORIZED || message->status_code == 
SOUP_STATUS_FORBIDDEN) {
-               caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
-       }
-
-       g_object_unref (message);
-
        return TRUE;
 }
 
-static void
-caldav_server_delete_object (ECalBackendCalDAV *cbdav,
-                             CalDAVObject *object,
-                             GCancellable *cancellable,
-                             GError **perror)
-{
-       SoupMessage              *message;
-       gchar                     *uri;
-
-       g_return_if_fail (object != NULL && object->href != NULL);
-
-       uri = caldav_generate_uri (cbdav, object->href);
-       message = soup_message_new (SOUP_METHOD_DELETE, uri);
-       g_free (uri);
-       if (message == NULL) {
-               g_propagate_error (perror, EDC_ERROR (NoSuchCal));
-               return;
-       }
-
-       soup_message_headers_append (
-               message->request_headers,
-               "User-Agent", "Evolution/" VERSION);
-
-       if (object->etag != NULL) {
-               soup_message_headers_append (
-                       message->request_headers,
-                       "If-Match", object->etag);
-       }
-
-       send_and_handle_redirection (cbdav, message, NULL, cancellable, perror);
-
-       status_code_to_result (message, cbdav, FALSE, perror);
-
-       if (message->status_code == SOUP_STATUS_UNAUTHORIZED || message->status_code == SOUP_STATUS_FORBIDDEN)
-               caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
-
-       g_object_unref (message);
-}
-
 static gboolean
-caldav_receive_schedule_outbox_url (ECalBackendCalDAV *cbdav,
-                                    GCancellable *cancellable,
-                                    GError **error)
+ecb_caldav_get_changes_sync (ECalMetaBackend *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)
 {
-       SoupMessage *message;
-       xmlOutputBufferPtr buf;
-       xmlDocPtr doc;
-       xmlNodePtr root, node;
-       xmlNsPtr nsdav;
-       gconstpointer buf_content;
-       gsize buf_size;
-       gchar *owner = NULL;
-
-       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
-       g_return_val_if_fail (cbdav->priv->schedule_outbox_url == NULL, TRUE);
-
-       /* Prepare the soup message */
-       message = soup_message_new ("PROPFIND", cbdav->priv->uri);
-       if (message == NULL)
-               return FALSE;
-
-       doc = xmlNewDoc ((xmlChar *) "1.0");
-       root = xmlNewDocNode (doc, NULL, (xmlChar *) "propfind", NULL);
-       xmlDocSetRootElement (doc, root);
-       nsdav = xmlNewNs (root, (xmlChar *) "DAV:", NULL);
-
-       node = xmlNewTextChild (root, nsdav, (xmlChar *) "prop", NULL);
-       xmlNewTextChild (node, nsdav, (xmlChar *) "owner", NULL);
-
-       buf = xmlAllocOutputBuffer (NULL);
-       xmlNodeDumpOutput (buf, doc, root, 0, 1, NULL);
-       xmlOutputBufferFlush (buf);
-
-       soup_message_headers_append (message->request_headers, "User-Agent", "Evolution/" VERSION);
-       soup_message_headers_append (message->request_headers, "Depth", "0");
-
-       buf_content = compat_libxml_output_buffer_get_content (buf, &buf_size);
-       soup_message_set_request (
-               message,
-               "application/xml",
-               SOUP_MEMORY_COPY,
-               buf_content, buf_size);
-
-       /* Send the request now */
-       send_and_handle_redirection (cbdav, message, NULL, cancellable, error);
-
-       /* Clean up the memory */
-       xmlOutputBufferClose (buf);
-       xmlFreeDoc (doc);
-
-       /* Check the result */
-       if (message->status_code == SOUP_STATUS_MULTI_STATUS && parse_propfind_response (message, 
XPATH_OWNER_STATUS, XPATH_OWNER, &owner) && owner && *owner) {
-               xmlNsPtr nscd;
-               SoupURI *suri;
-
-               g_object_unref (message);
-
-               /* owner is a full path to the user's URL, thus change it in
-                * calendar's uri when asking for schedule-outbox-URL */
-               suri = soup_uri_new (cbdav->priv->uri);
-               soup_uri_set_path (suri, owner);
-               g_free (owner);
-               owner = soup_uri_to_string (suri, FALSE);
-               soup_uri_free (suri);
-
-               message = soup_message_new ("PROPFIND", owner);
-               if (message == NULL) {
-                       g_free (owner);
-                       return FALSE;
-               }
-
-               doc = xmlNewDoc ((xmlChar *) "1.0");
-               root = xmlNewDocNode (doc, NULL, (xmlChar *) "propfind", NULL);
-               xmlDocSetRootElement (doc, root);
-               nsdav = xmlNewNs (root, (xmlChar *) "DAV:", NULL);
-               nscd = xmlNewNs (root, (xmlChar *) "urn:ietf:params:xml:ns:caldav", (xmlChar *) "C");
-
-               node = xmlNewTextChild (root, nsdav, (xmlChar *) "prop", NULL);
-               xmlNewTextChild (node, nscd, (xmlChar *) "schedule-outbox-URL", NULL);
-
-               buf = xmlAllocOutputBuffer (NULL);
-               xmlNodeDumpOutput (buf, doc, root, 0, 1, NULL);
-               xmlOutputBufferFlush (buf);
-
-               soup_message_headers_append (message->request_headers, "User-Agent", "Evolution/" VERSION);
-               soup_message_headers_append (message->request_headers, "Depth", "0");
-
-               buf_content = compat_libxml_output_buffer_get_content (buf, &buf_size);
-               soup_message_set_request (
-                       message,
-                       "application/xml",
-                       SOUP_MEMORY_COPY,
-                       buf_content, buf_size);
-
-               /* Send the request now */
-               send_and_handle_redirection (cbdav, message, NULL, cancellable, error);
-
-               if (message->status_code == SOUP_STATUS_MULTI_STATUS && parse_propfind_response (message, 
XPATH_SCHEDULE_OUTBOX_URL_STATUS, XPATH_SCHEDULE_OUTBOX_URL, &cbdav->priv->schedule_outbox_url)) {
-                       if (!*cbdav->priv->schedule_outbox_url) {
-                               g_free (cbdav->priv->schedule_outbox_url);
-                               cbdav->priv->schedule_outbox_url = NULL;
-                       } else {
-                               /* make it a full URI */
-                               suri = soup_uri_new (cbdav->priv->uri);
-                               soup_uri_set_path (suri, cbdav->priv->schedule_outbox_url);
-                               g_free (cbdav->priv->schedule_outbox_url);
-                               cbdav->priv->schedule_outbox_url = soup_uri_to_string (suri, FALSE);
-                               soup_uri_free (suri);
-                       }
-               }
+       ECalBackendCalDAV *cbdav;
+       EXmlDocument *xml;
+       GHashTable *known_items; /* gchar *href ~> ECalMetaBackendInfo * */
+       GHashTableIter iter;
+       gpointer key = NULL, value = NULL;
+       gboolean success;
 
-               /* Clean up the memory */
-               xmlOutputBufferClose (buf);
-               xmlFreeDoc (doc);
-       } else if (message->status_code == SOUP_STATUS_UNAUTHORIZED || message->status_code == 
SOUP_STATUS_FORBIDDEN) {
-               caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
+       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
+       g_return_val_if_fail (out_repeat, 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;
+
+       cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
+
+       if (cbdav->priv->ctag_supported) {
+               gchar *new_sync_tag = NULL;
+
+               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)
+                               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;
+                       return TRUE;
+               }
+
+               /* Do not advertise the new ctag in the first go, otherwise a failure
+                  in the second go might hide some events. */
+               if (is_repeat)
+                       *out_new_sync_tag = new_sync_tag;
+               else
+                       g_free (new_sync_tag);
        }
 
-       if (message)
-               g_object_unref (message);
-
-       g_free (owner);
-
-       return cbdav->priv->schedule_outbox_url != NULL;
-}
-
-/* ************************************************************************* */
-/* Synchronization foo */
+       xml = e_xml_document_new (E_WEBDAV_NS_CALDAV, "calendar-query");
+       g_return_val_if_fail (xml != NULL, FALSE);
 
-static gboolean extract_timezones (ECalBackendCalDAV *cbdav, icalcomponent *icomp);
+       e_xml_document_add_namespaces (xml, "D", E_WEBDAV_NS_DAV, NULL);
 
-struct cache_comp_list
-{
-       GSList *slist;
-};
+       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_end_element (xml); /* prop */
 
-static gboolean
-remove_complist_from_cache_and_notify_cb (gpointer key,
-                                          gpointer value,
-                                          gpointer data)
-{
-       GSList *l;
-       struct cache_comp_list *ccl = value;
-       ECalBackendCalDAV *cbdav = data;
-
-       for (l = ccl->slist; l; l = l->next) {
-               ECalComponent *old_comp = l->data;
-               ECalComponentId *id;
-
-               id = e_cal_component_get_id (old_comp);
-               if (!id) {
-                       continue;
-               }
-
-               if (e_cal_backend_store_remove_component (cbdav->priv->store, id->uid, id->rid)) {
-                       e_cal_backend_notify_component_removed ((ECalBackend *) cbdav, id, old_comp, NULL);
-               }
+       e_xml_document_start_element (xml, NULL, "filter");
+       e_xml_document_start_element (xml, NULL, "comp-filter");
+       e_xml_document_add_attribute (xml, NULL, "name", "VCALENDAR");
+       e_xml_document_start_element (xml, NULL, "comp-filter");
 
-               e_cal_component_free_id (id);
+       switch (e_cal_backend_get_kind (E_CAL_BACKEND (cbdav))) {
+       default:
+       case ICAL_VEVENT_COMPONENT:
+               e_xml_document_add_attribute (xml, NULL, "name", "VEVENT");
+               break;
+       case ICAL_VJOURNAL_COMPONENT:
+               e_xml_document_add_attribute (xml, NULL, "name", "VJOURNAL");
+               break;
+       case ICAL_VTODO_COMPONENT:
+               e_xml_document_add_attribute (xml, NULL, "name", "VTODO");
+               break;
        }
-       remove_cached_attachment (cbdav, (const gchar *) key);
-
-       return FALSE;
-}
-
-static void
-free_comp_list (gpointer cclist)
-{
-       struct cache_comp_list *ccl = cclist;
 
-       g_return_if_fail (ccl != NULL);
-
-       g_slist_foreach (ccl->slist, (GFunc) g_object_unref, NULL);
-       g_slist_free (ccl->slist);
-       g_free (ccl);
-}
+       if (!is_repeat) {
+               icaltimezone *utc = icaltimezone_get_utc_timezone ();
+               time_t now;
+               gchar *tmp;
 
-#define etags_match(_tag1, _tag2) ((_tag1 == _tag2) ? TRUE : \
-                                  g_str_equal (_tag1 != NULL ? _tag1 : "", \
-                                               _tag2 != NULL ? _tag2 : ""))
+               time (&now);
 
-/* start_time/end_time is an interval for checking changes. If both greater than zero,
- * only the interval is checked and the removed items are not notified, as they can
- * be still there.
-*/
-static void
-caldav_synchronize_cache (ECalBackendCalDAV *cbdav,
-                         time_t start_time,
-                         time_t end_time,
-                         gboolean can_check_ctag,
-                         GCancellable *cancellable)
-{
-       CalDAVObject *sobjs, *object;
-       GSList *c_objs, *c_iter; /* list of all items known from our cache */
-       GTree *c_uid2complist;  /* cache components list (with detached instances) sorted by (master's) uid */
-       GHashTable *c_href2uid; /* connection between href and a (master's) uid */
-       GSList *hrefs_to_update, *htu; /* list of href-s to update */
-       gint i, len;
-
-       /* intentionally do server-side checking first, and then the bool test,
-          to store actual ctag value first, and then update the content, to not
-          do it again the next time this function is called */
-       if (!check_calendar_changed_on_server (cbdav, start_time == (time_t) 0, cancellable) && 
can_check_ctag) {
-               /* no changes on the server, no update required */
-               return;
-       }
+               *out_repeat = TRUE;
 
-       len = 0;
-       sobjs = NULL;
+               /* Check for events in the month before/after today first,
+                  to show user actual data as soon as possible. */
+               e_xml_document_start_element (xml, NULL, "time-range");
 
-       /* get list of server objects */
-       if (!caldav_server_list_objects (cbdav, &sobjs, &len, NULL, start_time, end_time, cancellable))
-               return;
+               tmp = isodate_from_time_t (time_add_week_with_zone (now, -5, utc));
+               e_xml_document_add_attribute (xml, NULL, "start", tmp);
+               g_free (tmp);
 
-       c_objs = e_cal_backend_store_get_components (cbdav->priv->store);
+               tmp = isodate_from_time_t (time_add_week_with_zone (now, +5, utc));
+               e_xml_document_add_attribute (xml, NULL, "end", tmp);
+               g_free (tmp);
 
-       if (caldav_debug_show (DEBUG_SERVER_ITEMS)) {
-               printf ("CalDAV - found %d objects on the server, locally stored %d objects\n", len, 
g_slist_length (c_objs)); fflush (stdout);
+               e_xml_document_end_element (xml); /* time-range */
        }
 
-       /* do not store changes in cache immediately - makes things significantly quicker */
-       e_cal_backend_store_freeze_changes (cbdav->priv->store);
+       e_xml_document_end_element (xml); /* comp-filter / VEVENT|VJOURNAL|VTODO */
+       e_xml_document_end_element (xml); /* comp-filter / VCALENDAR*/
+       e_xml_document_end_element (xml); /* filter */
 
-       c_uid2complist = g_tree_new_full ((GCompareDataFunc) g_strcmp0, NULL, g_free, free_comp_list);
-       c_href2uid = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+       known_items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, e_cal_meta_backend_info_free);
 
-       /* fill indexed hash and tree with cached components */
-       for (c_iter = c_objs; c_iter; c_iter = g_slist_next (c_iter)) {
-               ECalComponent *ccomp = E_CAL_COMPONENT (c_iter->data);
-               const gchar *uid = NULL;
-               struct cache_comp_list *ccl;
-               gchar *href;
+       success = e_webdav_session_report_sync (cbdav->priv->webdav, NULL, E_WEBDAV_DEPTH_THIS_AND_CHILDREN, 
xml,
+               ecb_caldav_get_calendar_items_cb, known_items, NULL, NULL, cancellable, error);
 
-               e_cal_component_get_uid (ccomp, &uid);
-               if (!uid) {
-                       g_warning ("broken component with NULL Id");
-                       continue;
-               }
+       g_object_unref (xml);
 
-               href = ecalcomp_get_href (ccomp);
-
-               if (href == NULL) {
-                       g_warning ("href of object NULL :(");
-                       continue;
-               }
+       if (success) {
+               ECalCache *cal_cache;
+               CalDAVChangesData ccd;
 
-               ccl = g_tree_lookup (c_uid2complist, uid);
-               if (ccl) {
-                       ccl->slist = g_slist_prepend (ccl->slist, g_object_ref (ccomp));
-               } else {
-                       ccl = g_new0 (struct cache_comp_list, 1);
-                       ccl->slist = g_slist_append (NULL, g_object_ref (ccomp));
+               ccd.is_repeat = is_repeat;
+               ccd.out_modified_objects = out_modified_objects;
+               ccd.out_removed_objects = out_removed_objects;
+               ccd.known_items = known_items;
 
-                       /* make a copy, which will be used in the c_href2uid too */
-                       uid = g_strdup (uid);
+               cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
 
-                       g_tree_insert (c_uid2complist, (gpointer) uid, ccl);
-               }
+               success = e_cal_cache_search_with_callback (cal_cache, NULL, ecb_caldav_search_changes_cb, 
&ccd, cancellable, error);
 
-               if (g_hash_table_lookup (c_href2uid, href) == NULL) {
-                       /* uid is from a component or c_uid2complist key, thus will not be
-                        * freed before a removal from c_uid2complist, thus do not duplicate it,
-                        * rather save memory */
-                       g_hash_table_insert (c_href2uid, href, (gpointer) uid);
-               } else {
-                       g_free (href);
-               }
+               g_clear_object (&cal_cache);
        }
 
-       /* clear it now, we do not need it later */
-       g_slist_foreach (c_objs, (GFunc) g_object_unref, NULL);
-       g_slist_free (c_objs);
-       c_objs = NULL;
-
-       hrefs_to_update = NULL;
-
-       /* see if we have to update or add some objects */
-       for (i = 0, object = sobjs; i < len && cbdav->priv->slave_cmd == SLAVE_SHOULD_WORK; i++, object++) {
-               ECalComponent *ccomp = NULL;
-               gchar *etag = NULL;
-               const gchar *uid;
-               struct cache_comp_list *ccl;
-
-               if (object->status != 200) {
-                       /* just continue here, so that the object
-                        * doesnt get removed from the cobjs list
-                        * - therefore it will be removed */
-                       continue;
-               }
-
-               uid = g_hash_table_lookup (c_href2uid, object->href);
-               if (uid) {
-                       ccl = g_tree_lookup (c_uid2complist, uid);
-                       if (ccl) {
-                               GSList *sl;
-                               for (sl = ccl->slist; sl && !etag; sl = sl->next) {
-                                       ccomp = sl->data;
-                                       if (ccomp)
-                                               etag = ecalcomp_get_etag (ccomp);
-                               }
-
-                               if (!etag)
-                                       ccomp = NULL;
-                       }
-               }
-
-               if (!etag || !etags_match (etag, object->etag)) {
-                       hrefs_to_update = g_slist_prepend (hrefs_to_update, object->href);
-               } else if (uid && ccl) {
-                       /* all components cover by this uid are up-to-date */
-                       GSList *p;
-
-                       for (p = ccl->slist; p; p = p->next) {
-                               g_object_unref (p->data);
-                       }
-
-                       g_slist_free (ccl->slist);
-                       ccl->slist = NULL;
-               }
-
-               g_free (etag);
+       if (!success) {
+               g_hash_table_destroy (known_items);
+               return FALSE;
        }
 
-       /* free hash table, as it is not used anymore */
-       g_hash_table_destroy (c_href2uid);
-       c_href2uid = NULL;
-
-       if (caldav_debug_show (DEBUG_SERVER_ITEMS)) {
-               printf ("CalDAV - recognized %d items to update\n", g_slist_length (hrefs_to_update)); fflush 
(stdout);
+       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_cal_meta_backend_info_copy 
(value));
        }
 
-       htu = hrefs_to_update;
-       while (htu && cbdav->priv->slave_cmd == SLAVE_SHOULD_WORK) {
-               gint count = 0;
-               GSList *to_fetch = NULL;
+       g_hash_table_destroy (known_items);
 
-               while (count < CALDAV_MAX_MULTIGET_AMOUNT && htu) {
-                       to_fetch = g_slist_prepend (to_fetch, htu->data);
-                       htu = htu->next;
-                       count++;
-               }
-
-               if (to_fetch && cbdav->priv->slave_cmd == SLAVE_SHOULD_WORK) {
-                       CalDAVObject *up_sobjs = NULL;
-
-                       if (caldav_debug_show (DEBUG_SERVER_ITEMS)) {
-                               printf ("CalDAV - going to fetch %d items\n", g_slist_length (to_fetch)); 
fflush (stdout);
-                       }
+       if (*out_created_objects || *out_modified_objects) {
+               GSList *link, *set2 = *out_modified_objects;
 
-                       count = 0;
-                       if (!caldav_server_list_objects (cbdav, &up_sobjs, &count, to_fetch, 0, 0, 
cancellable)) {
-                               fprintf (stderr, "CalDAV - failed to retrieve bunch of items\n"); fflush 
(stderr);
-                               break;
-                       }
-
-                       if (caldav_debug_show (DEBUG_SERVER_ITEMS)) {
-                               printf ("CalDAV - fetched bunch of %d items\n", count); fflush (stdout);
-                       }
-
-                       /* we are going to update cache */
-                       /* they are downloaded, so process them */
-                       for (i = 0, object = up_sobjs; i < count /*&& cbdav->priv->slave_cmd == 
SLAVE_SHOULD_WORK */; i++, object++) {
-                               if (object->status == 200 && object->href && object->etag && object->cdata && 
*object->cdata) {
-                                       icalcomponent *icomp = icalparser_parse_string (object->cdata);
-
-                                       if (icomp) {
-                                               put_server_comp_to_cache (cbdav, icomp, object->href, 
object->etag, c_uid2complist);
-                                               icalcomponent_free (icomp);
-                                       }
-                               }
-
-                               /* these free immediately */
-                               caldav_object_free (object, FALSE);
-                       }
-
-                       /* cache update done for fetched items */
-                       g_free (up_sobjs);
-               }
-
-               /* do not free 'data' itself, it's part of 'sobjs' */
-               g_slist_free (to_fetch);
-       }
-
-       /* if not interrupted and not using the time range... */
-       if (cbdav->priv->slave_cmd == SLAVE_SHOULD_WORK && (!start_time || !end_time)) {
-               /* ...remove old (not on server anymore) items from our cache and notify of a removal */
-               g_tree_foreach (c_uid2complist, remove_complist_from_cache_and_notify_cb, cbdav);
-       }
-
-       if (cbdav->priv->ctag_to_store) {
-               /* store only when wasn't interrupted */
-               if (cbdav->priv->slave_cmd == SLAVE_SHOULD_WORK && start_time == 0 && end_time == 0) {
-                       e_cal_backend_store_put_key_value (cbdav->priv->store, CALDAV_CTAG_KEY, 
cbdav->priv->ctag_to_store);
+               if (*out_created_objects) {
+                       link = *out_created_objects;
+               } else {
+                       link = set2;
+                       set2 = NULL;
                }
 
-               g_free (cbdav->priv->ctag_to_store);
-               cbdav->priv->ctag_to_store = NULL;
-       }
-
-       /* save cache changes to disk finally */
-       e_cal_backend_store_thaw_changes (cbdav->priv->store);
-
-       for (i = 0, object = sobjs; i < len; i++, object++) {
-               caldav_object_free (object, FALSE);
+               do {
+                       success = ecb_caldav_multiget_from_sets_sync (cbdav, &link, &set2, cancellable, 
error);
+               } while (success && link);
        }
 
-       g_tree_destroy (c_uid2complist);
-       g_slist_free (hrefs_to_update);
-       g_free (sobjs);
-}
-
-static void
-check_server_tweaks (ECalBackendCalDAV *cbdav)
-{
-       SoupURI *suri;
-
-       g_return_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav));
-
-       cbdav->priv->is_google = FALSE;
-       cbdav->priv->is_icloud = FALSE;
-
-       g_return_if_fail (cbdav->priv->uri);
-
-       suri = soup_uri_new (cbdav->priv->uri);
-       g_return_if_fail (suri != NULL);
-
-       cbdav->priv->is_google = suri->host && (
-               g_ascii_strcasecmp (suri->host, "www.google.com") == 0 ||
-               g_ascii_strcasecmp (suri->host, "apidata.googleusercontent.com") == 0);
-
-       cbdav->priv->is_icloud = suri->host && e_util_utf8_strstrcase (suri->host, ".icloud.com");
-
-       soup_uri_free (suri);
-}
-
-static void
-time_to_refresh_caldav_calendar_cb (ESource *source,
-                                    gpointer user_data)
-{
-       ECalBackendCalDAV *cbdav = user_data;
-
-       g_return_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav));
-
-       g_cond_signal (&cbdav->priv->cond);
+       return success;
 }
 
-/* ************************************************************************* */
-
-static gpointer
-caldav_synch_slave_loop (gpointer data)
+static gboolean
+ecb_caldav_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)
 {
-       ECalBackendCalDAV *cbdav;
-       time_t now;
-       icaltimezone *utc = icaltimezone_get_utc_timezone ();
-       gboolean know_unreachable;
+       GSList **out_existing_objects = user_data;
 
-       cbdav = E_CAL_BACKEND_CALDAV (data);
+       g_return_val_if_fail (out_existing_objects != NULL, FALSE);
 
-       g_mutex_lock (&cbdav->priv->busy_lock);
+       if (!xpath_prop_prefix) {
+               e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CALDAV, NULL);
+       } else if (status_code == SOUP_STATUS_OK) {
+               gchar *etag;
+               gchar *calendar_data;
 
-       know_unreachable = !cbdav->priv->opened;
+               g_return_val_if_fail (href != NULL, FALSE);
 
-       while (cbdav->priv->slave_cmd != SLAVE_SHOULD_DIE) {
-               gboolean can_check_ctag = TRUE;
+               etag = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:getetag", xpath_prop_prefix);
+               calendar_data = e_xml_xpath_eval_as_string (xpath_ctx, "%s/C:calendar-data", 
xpath_prop_prefix);
 
-               if (cbdav->priv->slave_cmd == SLAVE_SHOULD_SLEEP) {
-                       /* just sleep until we get woken up again */
-                       g_cond_wait (&cbdav->priv->cond, &cbdav->priv->busy_lock);
+               if (calendar_data) {
+                       icalcomponent *vcalendar;
 
-                       /* This means to honor SLAVE_SHOULD_SLEEP only if the backend is opened */
-                       if (cbdav->priv->slave_cmd == SLAVE_SHOULD_DIE ||
-                           cbdav->priv->opened) {
-                               /* check if we should die, work or sleep again */
-                               continue;
-                       }
-               }
-
-               /* Ok here we go, do some real work
-                * Synch it baby one more time ...
-                */
-               cbdav->priv->slave_busy = TRUE;
-               if (cbdav->priv->slave_cmd == SLAVE_SHOULD_WORK_NO_CTAG_CHECK) {
-                       cbdav->priv->slave_cmd = SLAVE_SHOULD_WORK;
-                       can_check_ctag = FALSE;
-               }
+                       vcalendar = icalcomponent_new_from_string (calendar_data);
+                       if (vcalendar) {
+                               const gchar *uid;
 
-               if (!cbdav->priv->opened) {
-                       gchar *certificate_pem = NULL;
-                       GTlsCertificateFlags certificate_errors = 0;
-                       GError *local_error = NULL;
-
-                       if (open_calendar_wrapper (cbdav, NULL, &local_error, TRUE, &know_unreachable, 
&certificate_pem, &certificate_errors)) {
-                               cbdav->priv->opened = TRUE;
-                               update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
-                               g_cond_signal (&cbdav->priv->cond);
-
-                               check_server_tweaks (cbdav);
-                               know_unreachable = FALSE;
-                       } else {
-                               ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
-                               GError *local_error2 = NULL;
+                               uid = ecb_caldav_get_vcalendar_uid (vcalendar);
 
-                               if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
-                                       reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED;
+                               if (uid) {
+                                       etag = e_webdav_session_util_maybe_dequote (etag);
+                                       *out_existing_objects = g_slist_prepend (*out_existing_objects,
+                                               e_cal_meta_backend_info_new (uid, etag, NULL, href));
                                }
 
-                               if (!e_backend_credentials_required_sync (E_BACKEND (cbdav), reason, 
certificate_pem, certificate_errors,
-                                       local_error, NULL, &local_error2)) {
-                                       g_warning ("%s: Failed to call credentials required: %s", G_STRFUNC, 
local_error2 ? local_error2->message : "Unknown error");
-                               }
-
-                               g_clear_error (&local_error2);
+                               icalcomponent_free (vcalendar);
                        }
-
-                       g_clear_error (&local_error);
-                       g_free (certificate_pem);
                }
 
-               if (cbdav->priv->opened) {
-                       time (&now);
-                       /* check for events in the month before/after today first,
-                        * to show user actual data as soon as possible */
-                       caldav_synchronize_cache (cbdav, time_add_week_with_zone (now, -5, utc), 
time_add_week_with_zone (now, +5, utc), can_check_ctag, NULL);
-
-                       if (cbdav->priv->slave_cmd != SLAVE_SHOULD_SLEEP) {
-                               /* and then check for changes in a whole calendar */
-                               caldav_synchronize_cache (cbdav, 0, 0, can_check_ctag, NULL);
-                       }
-
-                       if (caldav_debug_show (DEBUG_SERVER_ITEMS)) {
-                               GSList *c_objs;
-
-                               c_objs = e_cal_backend_store_get_components (cbdav->priv->store);
-
-                               printf ("CalDAV - finished syncing with %d items in a cache\n", 
g_slist_length (c_objs)); fflush (stdout);
-
-                               g_slist_foreach (c_objs, (GFunc) g_object_unref, NULL);
-                               g_slist_free (c_objs);
-                       }
-               }
-
-               cbdav->priv->slave_busy = FALSE;
-
-               /* puhh that was hard, get some rest :) */
-               g_cond_wait (&cbdav->priv->cond, &cbdav->priv->busy_lock);
+               g_free (calendar_data);
+               g_free (etag);
        }
 
-       cbdav->priv->synch_slave = NULL;
-
-       /* signal we are done */
-       g_cond_signal (&cbdav->priv->slave_gone_cond);
-
-       /* we got killed ... */
-       g_mutex_unlock (&cbdav->priv->busy_lock);
-       return NULL;
-}
-
-static gchar *
-maybe_append_email_domain (const gchar *username,
-                           const gchar *may_append)
-{
-       if (!username || !*username)
-               return NULL;
-
-       if (strchr (username, '@'))
-               return g_strdup (username);
-
-       return g_strconcat (username, may_append, NULL);
+       return TRUE;
 }
 
-static gchar *
-get_usermail (ECalBackend *backend)
+static gboolean
+ecb_caldav_list_existing_sync (ECalMetaBackend *meta_backend,
+                              gchar **out_new_sync_tag,
+                              GSList **out_existing_objects,
+                              GCancellable *cancellable,
+                              GError **error)
 {
        ECalBackendCalDAV *cbdav;
-       ESource *source;
-       ESourceAuthentication *auth_extension;
-       ESourceWebdav *webdav_extension;
-       const gchar *extension_name;
-       gchar *usermail;
-       gchar *username;
-       gchar *res = NULL;
-
-       g_return_val_if_fail (backend != NULL, NULL);
-
-       source = e_backend_get_source (E_BACKEND (backend));
-
-       extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
-       webdav_extension = e_source_get_extension (source, extension_name);
+       icalcomponent_kind kind;
+       EXmlDocument *xml;
+       gboolean success;
 
-       /* This will never return an empty string. */
-       usermail = e_source_webdav_dup_email_address (webdav_extension);
+       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
+       g_return_val_if_fail (out_existing_objects != NULL, FALSE);
 
-       if (usermail != NULL)
-               return usermail;
+       *out_existing_objects = NULL;
 
-       cbdav = E_CAL_BACKEND_CALDAV (backend);
+       cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
+       kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbdav));
 
-       extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
-       auth_extension = e_source_get_extension (source, extension_name);
-       username = e_source_authentication_dup_user (auth_extension);
+       xml = e_xml_document_new (E_WEBDAV_NS_CALDAV, "calendar-query");
+       g_return_val_if_fail (xml != NULL, FALSE);
 
-       if (cbdav->priv && cbdav->priv->is_google)
-               res = maybe_append_email_domain (username, "@gmail.com");
+       e_xml_document_add_namespaces (xml, "D", E_WEBDAV_NS_DAV, NULL);
 
-       g_free (username);
+       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_CALDAV, "calendar-data");
+       e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "comp");
+       e_xml_document_add_attribute (xml, NULL, "name", "VCALENDAR");
+       e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "comp");
+       if (kind == ICAL_VEVENT_COMPONENT)
+               e_xml_document_add_attribute (xml, NULL, "name", "VEVENT");
+       else if (kind == ICAL_VJOURNAL_COMPONENT)
+               e_xml_document_add_attribute (xml, NULL, "name", "VJOURNAL");
+       else if (kind == ICAL_VTODO_COMPONENT)
+               e_xml_document_add_attribute (xml, NULL, "name", "VTODO");
+       else
+               g_warn_if_reached ();
+       e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "prop");
+       e_xml_document_add_attribute (xml, NULL, "name", "UID");
+       e_xml_document_end_element (xml); /* prop / UID */
+       e_xml_document_end_element (xml); /* comp / VEVENT|VJOURNAL|VTODO */
+       e_xml_document_end_element (xml); /* comp / VCALENDAR */
+       e_xml_document_end_element (xml); /* calendar-data */
+       e_xml_document_end_element (xml); /* prop */
+
+       e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "filter");
+       e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "comp-filter");
+       e_xml_document_add_attribute (xml, NULL, "name", "VCALENDAR");
+       e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "comp-filter");
+       if (kind == ICAL_VEVENT_COMPONENT)
+               e_xml_document_add_attribute (xml, NULL, "name", "VEVENT");
+       else if (kind == ICAL_VJOURNAL_COMPONENT)
+               e_xml_document_add_attribute (xml, NULL, "name", "VJOURNAL");
+       else if (kind == ICAL_VTODO_COMPONENT)
+               e_xml_document_add_attribute (xml, NULL, "name", "VTODO");
+       e_xml_document_end_element (xml); /* comp-filter / VEVENT|VJOURNAL|VTODO */
+       e_xml_document_end_element (xml); /* comp-filter / VCALENDAR */
+       e_xml_document_end_element (xml); /* filter */
+
+       success = e_webdav_session_report_sync (cbdav->priv->webdav, NULL, E_WEBDAV_DEPTH_THIS, xml,
+               ecb_caldav_extract_existing_cb, out_existing_objects, NULL, NULL, cancellable, error);
+
+       g_object_unref (xml);
+
+       if (success)
+               *out_existing_objects = g_slist_reverse (*out_existing_objects);
 
-       return res;
+       return success;
 }
 
-/* ************************************************************************* */
-/* ********** ECalBackendSync virtual function implementation *************  */
-
 static gchar *
-caldav_get_backend_property (ECalBackend *backend,
-                             const gchar *prop_name)
-{
-       g_return_val_if_fail (prop_name != NULL, FALSE);
-
-       if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
-               ESourceWebdav *extension;
-               ESource *source;
-               GString *caps;
-               gchar *usermail;
-               const gchar *extension_name;
-
-               caps = g_string_new (
-                       CAL_STATIC_CAPABILITY_NO_THISANDPRIOR ","
-                       CAL_STATIC_CAPABILITY_REFRESH_SUPPORTED);
-
-               usermail = get_usermail (E_CAL_BACKEND (backend));
-               if (!usermail || !*usermail)
-                       g_string_append (caps, "," CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS);
-               g_free (usermail);
-
-               source = e_backend_get_source (E_BACKEND (backend));
-
-               extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
-               extension = e_source_get_extension (source, extension_name);
-
-               if (e_source_webdav_get_calendar_auto_schedule (extension)) {
-                       g_string_append (
-                               caps,
-                               "," CAL_STATIC_CAPABILITY_CREATE_MESSAGES
-                               "," CAL_STATIC_CAPABILITY_SAVE_SCHEDULES);
-               }
-
-               return g_string_free (caps, FALSE);
-
-       } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS) ||
-                  g_str_equal (prop_name, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) {
-               return get_usermail (E_CAL_BACKEND (backend));
-
-       } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_DEFAULT_OBJECT)) {
-               ECalComponent *comp;
-               gchar *prop_value;
-
-               comp = e_cal_component_new ();
-
-               switch (e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
-               case ICAL_VEVENT_COMPONENT:
-                       e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
-                       break;
-               case ICAL_VTODO_COMPONENT:
-                       e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
-                       break;
-               case ICAL_VJOURNAL_COMPONENT:
-                       e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_JOURNAL);
-                       break;
-               default:
-                       g_object_unref (comp);
-                       return NULL;
-               }
-
-               prop_value = e_cal_component_get_as_string (comp);
-
-               g_object_unref (comp);
-
-               return prop_value;
-       }
-
-       /* Chain up to parent's get_backend_property() method. */
-       return E_CAL_BACKEND_CLASS (e_cal_backend_caldav_parent_class)->
-               get_backend_property (backend, prop_name);
-}
-
-static void
-caldav_shutdown (ECalBackend *backend)
+ecb_caldav_uid_to_uri (ECalBackendCalDAV *cbdav,
+                      const gchar *uid,
+                      const gchar *extension)
 {
-       ECalBackendCalDAVPrivate *priv;
-       ESource *source;
-
-       priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (backend);
-
-       /* Chain up to parent's shutdown() method. */
-       E_CAL_BACKEND_CLASS (e_cal_backend_caldav_parent_class)->shutdown (backend);
-
-       /* tell the slave to stop before acquiring a lock,
-        * as it can work at the moment, and lock can be locked */
-       update_slave_cmd (priv, SLAVE_SHOULD_DIE);
-
-       g_mutex_lock (&priv->busy_lock);
-
-       /* XXX Not sure if this really needs to be part of
-        *     shutdown or if we can just do it in dispose(). */
-       source = e_backend_get_source (E_BACKEND (backend));
-       if (source) {
-               g_signal_handlers_disconnect_by_func (G_OBJECT (source), caldav_source_changed_cb, backend);
-
-               if (priv->refresh_id) {
-                       e_source_refresh_remove_timeout (source, priv->refresh_id);
-                       priv->refresh_id = 0;
-               }
-       }
-
-       /* stop the slave  */
-       while (priv->synch_slave) {
-               g_cond_signal (&priv->cond);
-
-               /* wait until the slave died */
-               g_cond_wait (&priv->slave_gone_cond, &priv->busy_lock);
-       }
-
-       g_mutex_unlock (&priv->busy_lock);
-}
-
-static gboolean
-initialize_backend (ECalBackendCalDAV *cbdav,
-                    GError **perror)
-{
-       ESourceAuthentication    *auth_extension;
-       ESourceOffline           *offline_extension;
-       ESourceWebdav            *webdav_extension;
-       ECalBackend              *backend;
-       SoupURI                  *soup_uri;
-       ESource                  *source;
-       gsize                     len;
-       const gchar              *cache_dir;
-       const gchar              *extension_name;
-
-       backend = E_CAL_BACKEND (cbdav);
-       cache_dir = e_cal_backend_get_cache_dir (backend);
-       source = e_backend_get_source (E_BACKEND (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);
-
-       if (!g_signal_handler_find (G_OBJECT (source), G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, 
caldav_source_changed_cb, cbdav))
-               g_signal_connect (G_OBJECT (source), "changed", G_CALLBACK (caldav_source_changed_cb), cbdav);
+       ESourceWebdav *webdav_extension;
+       SoupURI *soup_uri;
+       gchar *uri, *tmp, *filename;
 
-       cbdav->priv->loaded = TRUE;
-       cbdav->priv->do_offline = e_source_offline_get_stay_synchronized (offline_extension);
-       cbdav->priv->auth_required = e_source_authentication_required (auth_extension);
+       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), NULL);
+       g_return_val_if_fail (uid != NULL, NULL);
 
+       webdav_extension = e_source_get_extension (e_backend_get_source (E_BACKEND (cbdav)), 
E_SOURCE_EXTENSION_WEBDAV_BACKEND);
        soup_uri = e_source_webdav_dup_soup_uri (webdav_extension);
+       g_return_val_if_fail (soup_uri != NULL, NULL);
 
-       /* properly encode uri */
-       if (soup_uri != NULL && soup_uri->path != NULL) {
-               gchar *tmp, *path;
-
-               if (strchr (soup_uri->path, '%')) {
-                       /* If path contains anything already encoded, then
-                        * decode it first, thus it'll be managed properly.
-                        * For example, the '#' in a path is in URI shown as
-                        * %23 and not doing this decode makes it being like
-                        * %2523, which is not what is wanted here. */
-                       tmp = soup_uri_decode (soup_uri->path);
-                       soup_uri_set_path (soup_uri, tmp);
-                       g_free (tmp);
-               }
-
-               tmp = soup_uri_encode (soup_uri->path, NULL);
-               path = soup_uri_normalize (tmp, "/");
-
-               soup_uri_set_path (soup_uri, path);
-
-               g_free (tmp);
-               g_free (path);
-       }
-
-       g_free (cbdav->priv->uri);
-       cbdav->priv->uri = soup_uri_to_string (soup_uri, FALSE);
-
-       soup_uri_free (soup_uri);
-
-       g_return_val_if_fail (cbdav->priv->uri != NULL, FALSE);
-
-       /* remove trailing slashes... */
-       if (cbdav->priv->uri != NULL) {
-               len = strlen (cbdav->priv->uri);
-               while (len--) {
-                       if (cbdav->priv->uri[len] == '/') {
-                               cbdav->priv->uri[len] = '\0';
-                       } else {
-                               break;
-                       }
-               }
-       }
-
-       /* ...and append exactly one slash */
-       if (cbdav->priv->uri && *cbdav->priv->uri) {
-               gchar *tmp = cbdav->priv->uri;
-
-               cbdav->priv->uri = g_strconcat (cbdav->priv->uri, "/", NULL);
-
+       if (extension) {
+               tmp = g_strconcat (uid, extension, NULL);
+               filename = soup_uri_encode (tmp, NULL);
                g_free (tmp);
+       } else {
+               filename = soup_uri_encode (uid, NULL);
        }
 
-       if (cbdav->priv->store == NULL) {
-               /* remove the old cache while migrating to ECalBackendStore */
-               e_cal_backend_cache_remove (cache_dir, "cache.xml");
-               cbdav->priv->store = e_cal_backend_store_new (
-                       cache_dir, E_TIMEZONE_CACHE (cbdav));
-               e_cal_backend_store_load (cbdav->priv->store);
-       }
+       if (soup_uri->path) {
+               gchar *slash = strrchr (soup_uri->path, '/');
 
-       /* Set the local attachment store */
-       if (g_mkdir_with_parents (cache_dir, 0700) < 0) {
-               g_propagate_error (perror, e_data_cal_create_error_fmt (OtherError, _("Cannot create local 
cache folder ā€œ%sā€"), cache_dir));
-               return FALSE;
+               if (slash && !slash[1])
+                       *slash = '\0';
        }
 
-       if (!cbdav->priv->synch_slave) {
-               GThread *slave;
-
-               update_slave_cmd (cbdav->priv, SLAVE_SHOULD_SLEEP);
-               slave = g_thread_new (NULL, caldav_synch_slave_loop, cbdav);
-
-               cbdav->priv->synch_slave = slave;
-               g_thread_unref (slave);
-       }
-
-       if (cbdav->priv->refresh_id == 0) {
-               cbdav->priv->refresh_id = e_source_refresh_add_timeout (
-                       source, NULL, time_to_refresh_caldav_calendar_cb, cbdav, NULL);
-       }
-
-       return TRUE;
-}
-
-static gboolean
-caldav_was_ever_connected (ECalBackendCalDAV *cbdav)
-{
-       gboolean has_components;
-       GSList *uids;
-
-       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
-
-       if (!cbdav->priv->store)
-               return FALSE;
-
-       uids = e_cal_backend_store_get_component_ids (cbdav->priv->store);
+       tmp = g_strconcat (soup_uri->path && *soup_uri->path ? soup_uri->path : "", "/", filename, NULL);
+       soup_uri_set_path (soup_uri, tmp);
+       g_free (tmp);
 
-       /* Assume the calendar was connected if it has any events stored;
-          obviously, empty calendars will fail this check. */
-       has_components = uids != NULL;
+       uri = soup_uri_to_string (soup_uri, FALSE);
 
-       g_slist_free_full (uids, (GDestroyNotify) e_cal_component_free_id);
+       soup_uri_free (soup_uri);
+       g_free (filename);
 
-       return has_components;
+       return uri;
 }
 
 static gboolean
-open_calendar_wrapper (ECalBackendCalDAV *cbdav,
-                      GCancellable *cancellable,
-                      GError **error,
-                      gboolean first_attempt,
-                      gboolean *know_unreachable,
-                      gchar **out_certificate_pem,
-                      GTlsCertificateFlags *out_certificate_errors)
-{
-       gboolean server_unreachable = FALSE;
-       gboolean awaiting_credentials = FALSE;
-       gboolean success;
-       GError *local_error = NULL;
-
-       g_return_val_if_fail (cbdav != NULL, FALSE);
-
-       if (!cbdav->priv->loaded && !initialize_backend (cbdav, error))
-               return FALSE;
-
-       if (!caldav_maybe_prepare_bearer_auth (cbdav, cancellable, error))
-               return FALSE;
-
-       success = caldav_server_open_calendar (cbdav, &server_unreachable, out_certificate_pem, 
out_certificate_errors, cancellable, &local_error);
-
-       if (first_attempt && g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed)) {
-               g_clear_error (&local_error);
-               awaiting_credentials = TRUE;
-               success = caldav_credentials_required_sync (cbdav, FALSE, first_attempt, cancellable, 
&local_error);
-       }
-
-       if (success) {
-               check_server_tweaks (cbdav);
-
-               if (!awaiting_credentials) {
-                       update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
-                       g_cond_signal (&cbdav->priv->cond);
-               }
-       } else if (server_unreachable) {
-               cbdav->priv->opened = FALSE;
-               e_cal_backend_set_writable (E_CAL_BACKEND (cbdav), FALSE);
-               if (local_error) {
-                       if (know_unreachable && !*know_unreachable) {
-                               gchar *msg = g_strdup_printf (_("Server is unreachable, calendar is opened in 
read-only mode.\nError message: %s"), local_error->message);
-                               e_cal_backend_notify_error (E_CAL_BACKEND (cbdav), msg);
-                               g_free (msg);
-                               g_clear_error (&local_error);
-
-                               *know_unreachable = TRUE;
-                       } else if (caldav_was_ever_connected (cbdav)) {
-                               /* This allows to open the calendar in read-only mode, which can be done
-                                  if it was ever connected to the server. */
-                               g_clear_error (&local_error);
-                               success = TRUE;
-                       }
-               }
-       }
-
-       if (local_error != NULL)
-               g_propagate_error (error, local_error);
-
-       return success;
-}
-
-static void
-caldav_do_open (ECalBackendSync *backend,
-                EDataCal *cal,
-                GCancellable *cancellable,
-                gboolean only_if_exists,
-                GError **perror)
+ecb_caldav_load_component_sync (ECalMetaBackend *meta_backend,
+                               const gchar *uid,
+                               const gchar *extra,
+                               icalcomponent **out_component,
+                               gchar **out_extra,
+                               GCancellable *cancellable,
+                               GError **error)
 {
        ECalBackendCalDAV *cbdav;
-       ESourceWebdav *webdav_extension;
-       ESourceAuthentication *auth_extension;
-       ESource *source;
-       gboolean online;
-
-       cbdav = E_CAL_BACKEND_CALDAV (backend);
+       gchar *uri = NULL, *href = NULL, *etag = NULL, *bytes = NULL;
+       gsize length = -1;
+       gboolean success = FALSE;
 
-       g_mutex_lock (&cbdav->priv->busy_lock);
+       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+       g_return_val_if_fail (out_component != NULL, FALSE);
 
-       source = e_backend_get_source (E_BACKEND (cbdav));
-       webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
-       auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
-       e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
+       cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
 
-       /* let it decide the 'getctag' extension availability again */
-       cbdav->priv->ctag_supported = TRUE;
-
-       if (!cbdav->priv->loaded && !initialize_backend (cbdav, perror)) {
-               g_mutex_unlock (&cbdav->priv->busy_lock);
-               return;
-       }
+       if (extra && *extra) {
+               uri = g_strdup (extra);
 
-       online = e_backend_get_online (E_BACKEND (backend));
+               success = e_webdav_session_get_data_sync (cbdav->priv->webdav, uri, &href, &etag, &bytes, 
&length, cancellable, error);
 
-       if (!cbdav->priv->do_offline && !online) {
-               g_mutex_unlock (&cbdav->priv->busy_lock);
-               g_propagate_error (perror, EDC_ERROR (RepositoryOffline));
-               return;
+               if (!success) {
+                       g_free (uri);
+                       uri = NULL;
+               }
        }
 
-       cbdav->priv->opened = TRUE;
-       cbdav->priv->is_google = FALSE;
-
-       if (online) {
-               gchar *certificate_pem = NULL, *auth_method;
-               GTlsCertificateFlags certificate_errors = 0;
+       if (!success) {
                GError *local_error = NULL;
 
-               auth_method = e_source_authentication_dup_method (auth_extension);
-
-               if ((g_strcmp0 (auth_method, "Google") == 0 ||
-                   !open_calendar_wrapper (cbdav, cancellable, &local_error, TRUE, NULL, &certificate_pem, 
&certificate_errors)) &&
-                   !g_cancellable_is_cancelled (cancellable)) {
-                       ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
-                       GError *local_error2 = NULL;
+               uri = ecb_caldav_uid_to_uri (cbdav, uid, NULL);
+               g_return_val_if_fail (uri != NULL, FALSE);
 
-                       if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
-                               reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED;
-                       }
-
-                       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");
-                       }
+               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)) {
+                       g_free (uri);
+                       uri = ecb_caldav_uid_to_uri (cbdav, uid, ".ics");
 
-                       if (!local_error2 && (
-                           g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) ||
-                           g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationRequired) ||
-                           g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed))) {
-                               /* These errors are treated through the authentication */
+                       if (uri) {
                                g_clear_error (&local_error);
-                       } else {
-                               if (local_error)
-                                       g_propagate_error (perror, local_error);
-                               local_error = NULL;
-                       }
-                       g_clear_error (&local_error2);
-               }
-
-               g_clear_error (&local_error);
-               g_free (certificate_pem);
-               g_free (auth_method);
-       } else {
-               e_cal_backend_set_writable (E_CAL_BACKEND (cbdav), FALSE);
-       }
-
-       g_mutex_unlock (&cbdav->priv->busy_lock);
-}
-
-static void
-caldav_refresh (ECalBackendSync *backend,
-                EDataCal *cal,
-                GCancellable *cancellable,
-                GError **perror)
-{
-       ECalBackendCalDAV        *cbdav;
-       gboolean                  online;
-
-       cbdav = E_CAL_BACKEND_CALDAV (backend);
-
-       g_mutex_lock (&cbdav->priv->busy_lock);
 
-       if (!cbdav->priv->loaded
-           || cbdav->priv->slave_cmd == SLAVE_SHOULD_DIE) {
-               g_mutex_unlock (&cbdav->priv->busy_lock);
-               return;
-       }
-
-       if (!e_backend_get_online (E_BACKEND (backend)) &&
-           e_backend_is_destination_reachable (E_BACKEND (backend), cancellable, NULL)) {
-               e_backend_set_online (E_BACKEND (backend), TRUE);
-       }
-
-       if (!check_state (cbdav, &online, NULL) || !online) {
-               g_mutex_unlock (&cbdav->priv->busy_lock);
-               return;
-       }
-
-       update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK_NO_CTAG_CHECK);
-
-       /* wake it up */
-       g_cond_signal (&cbdav->priv->cond);
-       g_mutex_unlock (&cbdav->priv->busy_lock);
-}
-
-static void
-remove_comp_from_cache_cb (gpointer value,
-                           gpointer user_data)
-{
-       ECalComponent *comp = value;
-       ECalBackendStore *store = user_data;
-       ECalComponentId *id;
-
-       g_return_if_fail (comp != NULL);
-       g_return_if_fail (store != NULL);
-
-       id = e_cal_component_get_id (comp);
-       g_return_if_fail (id != NULL);
-
-       e_cal_backend_store_remove_component (store, id->uid, id->rid);
-       e_cal_component_free_id (id);
-}
-
-static gboolean
-remove_comp_from_cache (ECalBackendCalDAV *cbdav,
-                        const gchar *uid,
-                        const gchar *rid)
-{
-       gboolean res = FALSE;
-
-       if (!rid || !*rid) {
-               /* get with detached instances */
-               GSList *objects = e_cal_backend_store_get_components_by_uid (cbdav->priv->store, uid);
-
-               if (objects) {
-                       g_slist_foreach (objects, (GFunc) remove_comp_from_cache_cb, cbdav->priv->store);
-                       g_slist_foreach (objects, (GFunc) g_object_unref, NULL);
-                       g_slist_free (objects);
-
-                       res = TRUE;
-               }
-       } else {
-               res = e_cal_backend_store_remove_component (cbdav->priv->store, uid, rid);
-       }
-
-       return res;
-}
-
-static void
-add_detached_recur_to_vcalendar_cb (gpointer value,
-                                    gpointer user_data)
-{
-       icalcomponent *recurrence = e_cal_component_get_icalcomponent (value);
-       icalcomponent *vcalendar = user_data;
-
-       icalcomponent_add_component (
-               vcalendar,
-               icalcomponent_new_clone (recurrence));
-}
-
-static gint
-sort_master_first (gconstpointer a,
-                   gconstpointer b)
-{
-       icalcomponent *ca, *cb;
-
-       ca = e_cal_component_get_icalcomponent ((ECalComponent *) a);
-       cb = e_cal_component_get_icalcomponent ((ECalComponent *) b);
-
-       if (!ca) {
-               if (!cb)
-                       return 0;
-               else
-                       return -1;
-       } else if (!cb) {
-               return 1;
-       }
-
-       return icaltime_compare (icalcomponent_get_recurrenceid (ca), icalcomponent_get_recurrenceid (cb));
-}
-
-/* Returns new icalcomponent, with all detached instances stored in a cache.
- * The cache lock should be locked when called this function.
-*/
-static icalcomponent *
-get_comp_from_cache (ECalBackendCalDAV *cbdav,
-                     const gchar *uid,
-                     const gchar *rid,
-                     gchar **href,
-                     gchar **etag)
-{
-       icalcomponent *icalcomp = NULL;
-
-       if (rid == NULL || !*rid) {
-               /* get with detached instances */
-               GSList *objects = e_cal_backend_store_get_components_by_uid (cbdav->priv->store, uid);
-
-               if (!objects) {
-                       return NULL;
-               }
-
-               if (g_slist_length (objects) == 1) {
-                       ECalComponent *comp = objects->data;
-
-                       /* will be unreffed a bit later */
-                       if (comp)
-                               icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp));
-               } else {
-                       /* if we have detached recurrences, return a VCALENDAR */
-                       icalcomp = e_cal_util_new_top_level ();
-
-                       objects = g_slist_sort (objects, sort_master_first);
-
-                       /* add all detached recurrences and the master object */
-                       g_slist_foreach (objects, add_detached_recur_to_vcalendar_cb, icalcomp);
-               }
-
-               /* every component has set same href and etag, thus it doesn't matter where it will be read */
-               if (href)
-                       *href = ecalcomp_get_href (objects->data);
-               if (etag)
-                       *etag = ecalcomp_get_etag (objects->data);
-
-               g_slist_foreach (objects, (GFunc) g_object_unref, NULL);
-               g_slist_free (objects);
-       } else {
-               /* get the exact object */
-               ECalComponent *comp = e_cal_backend_store_get_component (cbdav->priv->store, uid, rid);
-
-               if (comp) {
-                       icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp));
-                       if (href)
-                               *href = ecalcomp_get_href (comp);
-                       if (etag)
-                               *etag = ecalcomp_get_etag (comp);
-                       g_object_unref (comp);
+                               success = e_webdav_session_get_data_sync (cbdav->priv->webdav, uri, &href, 
&etag, &bytes, &length, cancellable, error);
+                       }
+               } else if (local_error) {
+                       g_propagate_error (error, local_error);
                }
        }
 
-       return icalcomp;
-}
-
-static void
-put_server_comp_to_cache (ECalBackendCalDAV *cbdav,
-                          icalcomponent *icomp,
-                          const gchar *href,
-                          const gchar *etag,
-                          GTree *c_uid2complist)
-{
-       icalcomponent_kind kind;
-       ECalBackend *cal_backend;
-
-       g_return_if_fail (cbdav != NULL);
-       g_return_if_fail (icomp != NULL);
-
-       cal_backend = E_CAL_BACKEND (cbdav);
-       kind = icalcomponent_isa (icomp);
-       extract_timezones (cbdav, icomp);
-
-       if (kind == ICAL_VCALENDAR_COMPONENT) {
-               icalcomponent *subcomp;
-
-               kind = e_cal_backend_get_kind (cal_backend);
-
-               for (subcomp = icalcomponent_get_first_component (icomp, kind);
-                    subcomp;
-                    subcomp = icalcomponent_get_next_component (icomp, kind)) {
-                       ECalComponent *new_comp, *old_comp;
-
-                       convert_to_url_attachment (cbdav, subcomp);
-                       new_comp = e_cal_component_new ();
-                       if (e_cal_component_set_icalcomponent (new_comp, icalcomponent_new_clone (subcomp))) {
-                               const gchar *uid = NULL;
-                               struct cache_comp_list *ccl;
-
-                               e_cal_component_get_uid (new_comp, &uid);
-                               if (!uid) {
-                                       g_warning ("%s: no UID on component!", G_STRFUNC);
-                                       g_object_unref (new_comp);
-                                       continue;
-                               }
-
-                               if (href)
-                                       ecalcomp_set_href (new_comp, href);
-                               if (etag)
-                                       ecalcomp_set_etag (new_comp, etag);
-
-                               old_comp = NULL;
-                               if (c_uid2complist) {
-                                       ccl = g_tree_lookup (c_uid2complist, uid);
-                                       if (ccl) {
-                                               gchar *nc_rid = e_cal_component_get_recurid_as_string 
(new_comp);
-                                               GSList *p;
+       if (success) {
+               *out_component = NULL;
 
-                                               for (p = ccl->slist; p && !old_comp; p = p->next) {
-                                                       gchar *oc_rid;
+               if (href && etag && bytes && length != ((gsize) -1)) {
+                       icalcomponent *icalcomp, *subcomp;
 
-                                                       old_comp = p->data;
+                       icalcomp = icalcomponent_new_from_string (bytes);
+                       if (icalcomp) {
+                               e_cal_util_set_x_property (icalcomp, E_CALDAV_X_ETAG, etag);
 
-                                                       oc_rid = e_cal_component_get_recurid_as_string 
(old_comp);
-                                                       if (g_strcmp0 (nc_rid, oc_rid) != 0) {
-                                                               old_comp = NULL;
-                                                       }
+                               for (subcomp = icalcomponent_get_first_component (icalcomp, 
ICAL_ANY_COMPONENT);
+                                    subcomp;
+                                    subcomp = icalcomponent_get_next_component (icalcomp, 
ICAL_ANY_COMPONENT)) {
+                                       icalcomponent_kind kind = icalcomponent_isa (subcomp);
 
-                                                       g_free (oc_rid);
-                                               }
-
-                                               g_free (nc_rid);
+                                       if (kind == ICAL_VEVENT_COMPONENT ||
+                                           kind == ICAL_VJOURNAL_COMPONENT ||
+                                           kind == ICAL_VTODO_COMPONENT) {
+                                               e_cal_util_set_x_property (subcomp, E_CALDAV_X_ETAG, etag);
                                        }
                                }
 
-                               put_component_to_store (cbdav, new_comp);
-
-                               if (old_comp == NULL) {
-                                       e_cal_backend_notify_component_created (cal_backend, new_comp);
-                               } else {
-                                       e_cal_backend_notify_component_modified (cal_backend, old_comp, 
new_comp);
-
-                                       if (ccl)
-                                               ccl->slist = g_slist_remove (ccl->slist, old_comp);
-                                       g_object_unref (old_comp);
-                               }
+                               *out_component = icalcomp;
                        }
-
-                       g_object_unref (new_comp);
                }
-       }
-}
 
-static gboolean
-put_comp_to_cache (ECalBackendCalDAV *cbdav,
-                   icalcomponent *icalcomp,
-                   const gchar *href,
-                   const gchar *etag)
-{
-       icalcomponent_kind my_kind;
-       ECalComponent *comp;
-       gboolean res = FALSE;
-
-       g_return_val_if_fail (cbdav != NULL, FALSE);
-       g_return_val_if_fail (icalcomp != NULL, FALSE);
-
-       my_kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbdav));
-       comp = e_cal_component_new ();
-
-       if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) {
-               icalcomponent *subcomp;
-
-               /* remove all old components from the cache first */
-               for (subcomp = icalcomponent_get_first_component (icalcomp, my_kind);
-                    subcomp;
-                    subcomp = icalcomponent_get_next_component (icalcomp, my_kind)) {
-                       remove_comp_from_cache (cbdav, icalcomponent_get_uid (subcomp), NULL);
-               }
-
-               /* then put new. It's because some detached instances could be removed on the server. */
-               for (subcomp = icalcomponent_get_first_component (icalcomp, my_kind);
-                    subcomp;
-                    subcomp = icalcomponent_get_next_component (icalcomp, my_kind)) {
-                       /* because reusing the same comp doesn't clear recur_id member properly */
-                       g_object_unref (comp);
-                       comp = e_cal_component_new ();
-
-                       if (e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (subcomp))) {
-                               if (href)
-                                       ecalcomp_set_href (comp, href);
-                               if (etag)
-                                       ecalcomp_set_etag (comp, etag);
-
-                               if (put_component_to_store (cbdav, comp))
-                                       res = TRUE;
-                       }
-               }
-       } else if (icalcomponent_isa (icalcomp) == my_kind) {
-               remove_comp_from_cache (cbdav, icalcomponent_get_uid (icalcomp), NULL);
-
-               if (e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp))) {
-                       if (href)
-                               ecalcomp_set_href (comp, href);
-                       if (etag)
-                               ecalcomp_set_etag (comp, etag);
-
-                       res = put_component_to_store (cbdav, comp);
+               if (!*out_component) {
+                       success = FALSE;
+                       g_propagate_error (error, EDC_ERROR (InvalidObject));
                }
        }
 
-       g_object_unref (comp);
-
-       return res;
-}
-
-static void
-remove_property (gpointer prop,
-                 gpointer icomp)
-{
-       icalcomponent_remove_property (icomp, prop);
-       icalproperty_free (prop);
-}
-
-static void
-strip_unneeded_x_props (icalcomponent *icomp)
-{
-       icalproperty *prop;
-       GSList *to_remove = NULL;
-
-       g_return_if_fail (icomp != NULL);
-       g_return_if_fail (icalcomponent_isa (icomp) != ICAL_VCALENDAR_COMPONENT);
-
-       for (prop = icalcomponent_get_first_property (icomp, ICAL_X_PROPERTY);
-            prop;
-            prop = icalcomponent_get_next_property (icomp, ICAL_X_PROPERTY)) {
-               if (g_str_has_prefix (icalproperty_get_x_name (prop), X_E_CALDAV)) {
-                       to_remove = g_slist_prepend (to_remove, prop);
-               }
-       }
-
-       for (prop = icalcomponent_get_first_property (icomp, ICAL_XLICERROR_PROPERTY);
-            prop;
-            prop = icalcomponent_get_next_property (icomp, ICAL_XLICERROR_PROPERTY)) {
-               to_remove = g_slist_prepend (to_remove, prop);
-       }
+       g_free (uri);
+       g_free (href);
+       g_free (etag);
+       g_free (bytes);
 
-       g_slist_foreach (to_remove, remove_property, icomp);
-       g_slist_free (to_remove);
+       return success;
 }
 
 static gboolean
-is_stored_on_server (ECalBackendCalDAV *cbdav,
-                     const gchar *uri)
+ecb_caldav_save_component_sync (ECalMetaBackend *meta_backend,
+                               gboolean overwrite_existing,
+                               EConflictResolution conflict_resolution,
+                               const GSList *instances,
+                               const gchar *extra,
+                               gchar **out_new_uid,
+                               gchar **out_new_extra,
+                               GCancellable *cancellable,
+                               GError **error)
 {
-       SoupURI *my_uri, *test_uri;
-       gboolean res;
-
-       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
-       g_return_val_if_fail (cbdav->priv->uri != NULL, FALSE);
-       g_return_val_if_fail (uri != NULL, FALSE);
-
-       my_uri = soup_uri_new (cbdav->priv->uri);
-       g_return_val_if_fail (my_uri != NULL, FALSE);
-
-       test_uri = soup_uri_new (uri);
-       if (!test_uri) {
-               soup_uri_free (my_uri);
-               return FALSE;
-       }
-
-       res = my_uri->host && test_uri->host && g_ascii_strcasecmp (my_uri->host, test_uri->host) == 0;
-
-       soup_uri_free (my_uri);
-       soup_uri_free (test_uri);
-
-       return res;
-}
+       ECalBackendCalDAV *cbdav;
+       icalcomponent *vcalendar, *subcomp;
+       gchar *href = NULL, *etag = NULL, *uid = NULL;
+       gchar *ical_string = NULL;
+       gboolean success;
 
-static void
-convert_to_inline_attachment (ECalBackendCalDAV *cbdav,
-                              icalcomponent *icalcomp)
-{
-       icalcomponent *cclone;
-       icalproperty *p;
-       GSList *to_remove = NULL;
+       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
+       g_return_val_if_fail (instances != NULL, FALSE);
+       g_return_val_if_fail (out_new_uid, FALSE);
+       g_return_val_if_fail (out_new_extra, FALSE);
 
-       g_return_if_fail (icalcomp != NULL);
+       cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
 
-       cclone = icalcomponent_new_clone (icalcomp);
+       vcalendar = e_cal_meta_backend_merge_instances (meta_backend, instances, cbdav->priv->is_icloud);
+       g_return_val_if_fail (vcalendar != NULL, FALSE);
 
-       /* Remove local url attachments first */
-       for (p = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY);
-            p;
-            p = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) {
-               icalattach *attach;
+       for (subcomp = icalcomponent_get_first_component (vcalendar, ICAL_ANY_COMPONENT);
+            subcomp;
+            subcomp = icalcomponent_get_next_component (vcalendar, ICAL_ANY_COMPONENT)) {
+               icalcomponent_kind kind = icalcomponent_isa (subcomp);
 
-               attach = icalproperty_get_attach ((const icalproperty *) p);
-               if (icalattach_get_is_url (attach)) {
-                       const gchar *url;
+               if (kind == ICAL_VEVENT_COMPONENT ||
+                   kind == ICAL_VJOURNAL_COMPONENT ||
+                   kind == ICAL_VTODO_COMPONENT) {
+                       if (!etag)
+                               etag = e_cal_util_dup_x_property (subcomp, E_CALDAV_X_ETAG);
+                       if (!uid)
+                               uid = g_strdup (icalcomponent_get_uid (subcomp));
 
-                       url = icalattach_get_url (attach);
-                       if (g_str_has_prefix (url, LOCAL_PREFIX))
-                               to_remove = g_slist_prepend (to_remove, p);
+                       e_cal_util_remove_x_property (subcomp, E_CALDAV_X_ETAG);
                }
        }
-       g_slist_foreach (to_remove, remove_property, icalcomp);
-       g_slist_free (to_remove);
-
-       /* convert local url attachments to inline attachments now */
-       for (p = icalcomponent_get_first_property (cclone, ICAL_ATTACH_PROPERTY);
-            p;
-            p = icalcomponent_get_next_property (cclone, ICAL_ATTACH_PROPERTY)) {
-               icalattach *attach;
-               GFile *file;
-               GError *error = NULL;
-               const gchar *uri;
-               gchar *basename;
-               gchar *content;
-               gsize len;
-
-               attach = icalproperty_get_attach ((const icalproperty *) p);
-               if (!icalattach_get_is_url (attach))
-                       continue;
 
-               uri = icalattach_get_url (attach);
-               if (!g_str_has_prefix (uri, LOCAL_PREFIX))
-                       continue;
-
-               file = g_file_new_for_uri (uri);
-               basename = g_file_get_basename (file);
-               if (g_file_load_contents (file, NULL, &content, &len, NULL, &error)) {
-                       icalproperty *prop;
-                       icalparameter *param;
-                       gchar *encoded;
-
-                       /*
-                        * do a base64 encoding so it can
-                        * be embedded in a soap message
-                        */
-                       encoded = g_base64_encode ((guchar *) content, len);
-                       attach = icalattach_new_from_data (encoded, NULL, NULL);
-                       g_free (content);
-                       g_free (encoded);
-
-                       prop = icalproperty_new_attach (attach);
-                       icalattach_unref (attach);
+       ical_string = icalcomponent_as_ical_string_r (vcalendar);
+       icalcomponent_free (vcalendar);
 
-                       param = icalparameter_new_value (ICAL_VALUE_BINARY);
-                       icalproperty_add_parameter (prop, param);
+       if (uid && ical_string && (!overwrite_existing || (extra && *extra))) {
+               gboolean force_write = FALSE;
 
-                       param = icalparameter_new_encoding (ICAL_ENCODING_BASE64);
-                       icalproperty_add_parameter (prop, param);
-
-                       param = icalparameter_new_x (basename);
-                       icalparameter_set_xname (param, X_E_CALDAV_ATTACHMENT_NAME);
-                       icalproperty_add_parameter (prop, param);
-
-                       icalcomponent_add_property (icalcomp, prop);
-               } else {
-                       g_warning ("%s\n", error->message);
-                       g_clear_error (&error);
-               }
-               g_free (basename);
-               g_object_unref (file);
-       }
-       icalcomponent_free (cclone);
-}
+               if (!extra)
+                       href = ecb_caldav_uid_to_uri (cbdav, uid, ".ics");
 
-static void
-convert_to_url_attachment (ECalBackendCalDAV *cbdav,
-                           icalcomponent *icalcomp)
-{
-       ECalBackend *backend;
-       GSList *to_remove = NULL, *to_remove_after_download = NULL;
-       icalcomponent *cclone;
-       icalproperty *p;
-       gint fileindex;
-
-       g_return_if_fail (cbdav != NULL);
-       g_return_if_fail (icalcomp != NULL);
-
-       backend = E_CAL_BACKEND (cbdav);
-       cclone = icalcomponent_new_clone (icalcomp);
-
-       /* Remove all inline attachments first */
-       for (p = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY);
-            p;
-            p = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) {
-               icalattach *attach;
-
-               attach = icalproperty_get_attach ((const icalproperty *) p);
-               if (!icalattach_get_is_url (attach))
-                       to_remove = g_slist_prepend (to_remove, p);
-               else if (is_stored_on_server (cbdav, icalattach_get_url (attach)))
-                       to_remove_after_download = g_slist_prepend (to_remove_after_download, p);
-       }
-       g_slist_foreach (to_remove, remove_property, icalcomp);
-       g_slist_free (to_remove);
-
-       /* convert inline attachments to url attachments now */
-       for (p = icalcomponent_get_first_property (cclone, ICAL_ATTACH_PROPERTY), fileindex = 0;
-            p;
-            p = icalcomponent_get_next_property (cclone, ICAL_ATTACH_PROPERTY), fileindex++) {
-               icalattach *attach;
-               gsize len = -1;
-               gchar *decoded = NULL;
-               gchar *basename, *local_filename;
-
-               attach = icalproperty_get_attach ((const icalproperty *) p);
-               if (icalattach_get_is_url (attach)) {
-                       const gchar *attach_url = icalattach_get_url (attach);
-                       GError *error = NULL;
-
-                       if (!is_stored_on_server (cbdav, attach_url))
-                               continue;
-
-                       if (!caldav_server_download_attachment (cbdav, attach_url, &decoded, &len, &error)) {
-                               if (caldav_debug_show (DEBUG_ATTACHMENTS))
-                                       g_print ("CalDAV::%s: Failed to download from a server: %s\n", 
G_STRFUNC, error ? error->message : "Unknown error");
-                               continue;
+               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;
                        }
                }
 
-               basename = icalproperty_get_parameter_as_string_r (p, X_E_CALDAV_ATTACHMENT_NAME);
-               local_filename = e_cal_backend_create_cache_filename (backend, icalcomponent_get_uid 
(icalcomp), basename, fileindex);
-               g_free (basename);
+               success = e_webdav_session_put_data_sync (cbdav->priv->webdav, (extra && *extra) ? extra : 
href,
+                       force_write ? "" : overwrite_existing ? etag : NULL, E_WEBDAV_CONTENT_TYPE_CALENDAR,
+                       ical_string, -1, out_new_extra, NULL, cancellable, error);
 
-               if (local_filename) {
-                       GError *error = NULL;
-
-                       if (decoded == NULL) {
-                               gchar *content;
-
-                               content = (gchar *) icalattach_get_data (attach);
-                               decoded = (gchar *) g_base64_decode (content, &len);
-                       }
-
-                       if (g_file_set_contents (local_filename, decoded, len, &error)) {
-                               icalproperty *prop;
-                               gchar *url;
-
-                               url = g_filename_to_uri (local_filename, NULL, NULL);
-                               attach = icalattach_new_from_url (url);
-                               prop = icalproperty_new_attach (attach);
-                               icalattach_unref (attach);
-                               icalcomponent_add_property (icalcomp, prop);
-                               g_free (url);
-                       } else {
-                               g_warning ("%s\n", error->message);
-                               g_clear_error (&error);
-                       }
-
-                       g_free (local_filename);
-               }
+               /* 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, EDC_ERROR (InvalidObject));
        }
 
-       icalcomponent_free (cclone);
-
-       g_slist_foreach (to_remove_after_download, remove_property, icalcomp);
-       g_slist_free (to_remove_after_download);
-}
-
-static void
-remove_files (const gchar *dir,
-              const gchar *fileprefix)
-{
-       GDir *d;
-
-       g_return_if_fail (dir != NULL);
-       g_return_if_fail (fileprefix != NULL);
-       g_return_if_fail (*fileprefix != '\0');
-
-       d = g_dir_open (dir, 0, NULL);
-       if (d) {
-               const gchar *entry;
-               gint len = strlen (fileprefix);
-
-               while ((entry = g_dir_read_name (d)) != NULL) {
-                       if (entry && strncmp (entry, fileprefix, len) == 0) {
-                               gchar *path;
+       g_free (ical_string);
+       g_free (href);
+       g_free (etag);
+       g_free (uid);
 
-                               path = g_build_filename (dir, entry, NULL);
-                               if (!g_file_test (path, G_FILE_TEST_IS_DIR))
-                                       g_unlink (path);
-                               g_free (path);
-                       }
-               }
-               g_dir_close (d);
-       }
+       return success;
 }
 
-static void
-remove_cached_attachment (ECalBackendCalDAV *cbdav,
-                          const gchar *uid)
+static gboolean
+ecb_caldav_remove_component_sync (ECalMetaBackend *meta_backend,
+                                 EConflictResolution conflict_resolution,
+                                 const gchar *uid,
+                                 const gchar *extra,
+                                 const gchar *object,
+                                 GCancellable *cancellable,
+                                 GError **error)
 {
-       GSList *l;
-       guint len;
-       gchar *dir;
-       gchar *fileprefix;
-
-       g_return_if_fail (cbdav != NULL);
-       g_return_if_fail (uid != NULL);
-
-       l = e_cal_backend_store_get_components_by_uid (cbdav->priv->store, uid);
-       len = g_slist_length (l);
-       g_slist_foreach (l, (GFunc) g_object_unref, NULL);
-       g_slist_free (l);
-       if (len > 0)
-               return;
-
-       dir = e_cal_backend_create_cache_filename (E_CAL_BACKEND (cbdav), uid, "a", 0);
-       if (!dir)
-               return;
-
-       fileprefix = g_strrstr (dir, G_DIR_SEPARATOR_S);
-       if (fileprefix) {
-               *fileprefix = '\0';
-               fileprefix++;
-
-               if (*fileprefix)
-                       fileprefix[strlen (fileprefix) - 1] = '\0';
-
-               remove_files (dir, fileprefix);
-       }
-
-       g_free (dir);
-}
-
-/* callback for icalcomponent_foreach_tzid */
-typedef struct {
-       ECalBackendStore *store;
-       icalcomponent *vcal_comp;
+       ECalBackendCalDAV *cbdav;
        icalcomponent *icalcomp;
-} ForeachTzidData;
-
-static void
-add_timezone_cb (icalparameter *param,
-                 gpointer data)
-{
-       icaltimezone *tz;
-       const gchar *tzid;
-       icalcomponent *vtz_comp;
-       ForeachTzidData *f_data = (ForeachTzidData *) data;
-       ETimezoneCache *cache;
-
-       tzid = icalparameter_get_tzid (param);
-       if (!tzid)
-               return;
-
-       tz = icalcomponent_get_timezone (f_data->vcal_comp, tzid);
-       if (tz)
-               return;
-
-       cache = e_cal_backend_store_ref_timezone_cache (f_data->store);
-
-       tz = icalcomponent_get_timezone (f_data->icalcomp, tzid);
-       if (tz == NULL)
-               tz = icaltimezone_get_builtin_timezone_from_tzid (tzid);
-       if (tz == NULL)
-               tz = e_timezone_cache_get_timezone (cache, tzid);
-
-       vtz_comp = icaltimezone_get_component (tz);
-
-       if (tz != NULL && vtz_comp != NULL)
-               icalcomponent_add_component (
-                       f_data->vcal_comp,
-                       icalcomponent_new_clone (vtz_comp));
-
-       g_object_unref (cache);
-}
-
-static void
-add_timezones_from_component (ECalBackendCalDAV *cbdav,
-                              icalcomponent *vcal_comp,
-                              icalcomponent *icalcomp)
-{
-       ForeachTzidData f_data;
-
-       g_return_if_fail (cbdav != NULL);
-       g_return_if_fail (vcal_comp != NULL);
-       g_return_if_fail (icalcomp != NULL);
-
-       f_data.store = cbdav->priv->store;
-       f_data.vcal_comp = vcal_comp;
-       f_data.icalcomp = icalcomp;
-
-       icalcomponent_foreach_tzid (icalcomp, add_timezone_cb, &f_data);
-}
-
-/* also removes X-EVOLUTION-CALDAV from all the components */
-static gchar *
-pack_cobj (ECalBackendCalDAV *cbdav,
-           icalcomponent *icomp)
-{
-       icalcomponent *calcomp;
-       gchar          *objstr;
+       gchar *etag = NULL;
+       gboolean success;
 
-       if (icalcomponent_isa (icomp) != ICAL_VCALENDAR_COMPONENT) {
-               icalcomponent *cclone;
+       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+       g_return_val_if_fail (object != NULL, FALSE);
 
-               calcomp = e_cal_util_new_top_level ();
+       cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
 
-               cclone = icalcomponent_new_clone (icomp);
-               strip_unneeded_x_props (cclone);
-               convert_to_inline_attachment (cbdav, cclone);
-               icalcomponent_add_component (calcomp, cclone);
-               add_timezones_from_component (cbdav, calcomp, cclone);
-       } else {
-               icalcomponent *subcomp;
-               icalcomponent_kind my_kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbdav));
-
-               calcomp = icalcomponent_new_clone (icomp);
-               for (subcomp = icalcomponent_get_first_component (calcomp, my_kind);
-                    subcomp;
-                    subcomp = icalcomponent_get_next_component (calcomp, my_kind)) {
-                       strip_unneeded_x_props (subcomp);
-                       convert_to_inline_attachment (cbdav, subcomp);
-                       add_timezones_from_component (cbdav, calcomp, subcomp);
-               }
+       if (!extra || !*extra) {
+               g_propagate_error (error, EDC_ERROR (InvalidObject));
+               return FALSE;
        }
 
-       objstr = icalcomponent_as_ical_string_r (calcomp);
-       icalcomponent_free (calcomp);
-
-       g_return_val_if_fail (objstr != NULL, NULL);
-
-       return objstr;
-
-}
-
-static void
-maybe_correct_tzid (ECalBackendCalDAV *cbdav,
-                   ECalComponentDateTime *dt)
-{
-       icaltimezone *zone;
-
-       zone = e_timezone_cache_get_timezone (E_TIMEZONE_CACHE (cbdav), dt->tzid);
-       if (!zone) {
-               g_free ((gchar *) dt->tzid);
-               dt->tzid = g_strdup ("UTC");
-       } else if (cbdav->priv->is_icloud && !dt->value->is_date) {
-               const gchar *location = icaltimezone_get_location (zone);
-
-               if (location && *location) {
-                       g_free ((gchar *) dt->tzid);
-                       dt->tzid = g_strdup (location);
-               } else {
-                       /* No location available for this timezone, convert to UTC */
-                       dt->value->zone = zone;
-                       *dt->value = icaltime_convert_to_zone (*dt->value, icaltimezone_get_utc_timezone ());
-                       g_free ((gchar *) dt->tzid);
-                       dt->tzid = g_strdup ("UTC");
-               }
+       icalcomp = icalcomponent_new_from_string (object);
+       if (!icalcomp) {
+               g_propagate_error (error, EDC_ERROR (InvalidObject));
+               return FALSE;
        }
-}
 
-static void
-sanitize_component (ECalBackendCalDAV *cbdav,
-                    ECalComponent *comp)
-{
-       ECalComponentDateTime dt;
-
-       /* Check dtstart, dtend and due's timezone, and convert it to local
-        * default timezone if the timezone is not in our builtin timezone
-        * list */
-       e_cal_component_get_dtstart (comp, &dt);
-       if (dt.value && dt.tzid) {
-               maybe_correct_tzid (cbdav, &dt);
-               e_cal_component_set_dtstart (comp, &dt);
-       }
-       e_cal_component_free_datetime (&dt);
+       if (conflict_resolution != E_CONFLICT_RESOLUTION_FAIL)
+               etag = e_cal_util_dup_x_property (icalcomp, E_CALDAV_X_ETAG);
 
-       e_cal_component_get_dtend (comp, &dt);
-       if (dt.value && dt.tzid) {
-               maybe_correct_tzid (cbdav, &dt);
-               e_cal_component_set_dtend (comp, &dt);
-       }
-       e_cal_component_free_datetime (&dt);
+       success = e_webdav_session_delete_sync (cbdav->priv->webdav, extra,
+               E_WEBDAV_DEPTH_THIS, etag, cancellable, error);
 
-       e_cal_component_get_due (comp, &dt);
-       if (dt.value && dt.tzid) {
-               maybe_correct_tzid (cbdav, &dt);
-               e_cal_component_set_due (comp, &dt);
-       }
-       e_cal_component_free_datetime (&dt);
+       icalcomponent_free (icalcomp);
+       g_free (etag);
 
-       e_cal_component_abort_sequence (comp);
+       return success;
 }
 
 static gboolean
-cache_contains (ECalBackendCalDAV *cbdav,
-                const gchar *uid,
-                const gchar *rid)
-{
-       gboolean res;
-       ECalComponent *comp;
-
-       g_return_val_if_fail (cbdav != NULL, FALSE);
-       g_return_val_if_fail (uid != NULL, FALSE);
-
-       g_return_val_if_fail (cbdav->priv->store != NULL, FALSE);
-
-       comp = e_cal_backend_store_get_component (cbdav->priv->store, uid, rid);
-       res = comp != NULL;
-
-       if (comp)
-               g_object_unref (comp);
-
-       return res;
-}
-
-/* Returns subcomponent of icalcomp, which is a master object, or icalcomp itself, if it's not a VCALENDAR;
- * Do not free returned pointer, it'll be freed together with the icalcomp.
-*/
-static icalcomponent *
-get_master_comp (ECalBackendCalDAV *cbdav,
-                 icalcomponent *icalcomp)
+ecb_caldav_propfind_get_owner_cb (EWebDAVSession *webdav,
+                                 xmlXPathContextPtr xpath_ctx,
+                                 const gchar *xpath_prop_prefix,
+                                 const SoupURI *request_uri,
+                                 const gchar *href,
+                                 guint status_code,
+                                 gpointer user_data)
 {
-       icalcomponent *master = icalcomp;
+       gchar **out_owner_href = user_data;
 
-       g_return_val_if_fail (cbdav != NULL, NULL);
-       g_return_val_if_fail (icalcomp != NULL, NULL);
+       g_return_val_if_fail (out_owner_href != NULL, FALSE);
 
-       if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) {
-               icalcomponent *subcomp;
-               icalcomponent_kind my_kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbdav));
+       if (xpath_prop_prefix &&
+           status_code == SOUP_STATUS_OK) {
+               gchar *tmp = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:owner/D:href", xpath_prop_prefix);
 
-               master = NULL;
+               if (tmp && *tmp)
+                       *out_owner_href = e_webdav_session_ensure_full_uri (webdav, request_uri, tmp);
 
-               for (subcomp = icalcomponent_get_first_component (icalcomp, my_kind);
-                    subcomp;
-                    subcomp = icalcomponent_get_next_component (icalcomp, my_kind)) {
-                       struct icaltimetype sub_rid = icalcomponent_get_recurrenceid (subcomp);
+               g_free (tmp);
 
-                       if (icaltime_is_null_time (sub_rid)) {
-                               master = subcomp;
-                               break;
-                       }
-               }
+               return FALSE;
        }
 
-       return master;
+       return TRUE;
 }
 
 static gboolean
-remove_instance (ECalBackendCalDAV *cbdav,
-                 icalcomponent *icalcomp,
-                 struct icaltimetype rid,
-                 ECalObjModType mod,
-                 gboolean also_exdate)
-{
-       icalcomponent *master = icalcomp;
-       struct icaltimetype master_dtstart;
-       gboolean res = FALSE;
-
-       g_return_val_if_fail (icalcomp != NULL, res);
-       g_return_val_if_fail (!icaltime_is_null_time (rid), res);
-
-       master_dtstart = icalcomponent_get_dtstart (master);
-       if (master_dtstart.zone && master_dtstart.zone != rid.zone)
-               rid = icaltime_convert_to_zone (rid, (icaltimezone *) master_dtstart.zone);
-
-       rid = icaltime_convert_to_zone (rid, icaltimezone_get_utc_timezone ());
-
-       /* remove an instance only */
-       if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) {
-               icalcomponent *subcomp;
-               icalcomponent_kind my_kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbdav));
-               gint left = 0;
-               gboolean start_first = FALSE;
-
-               master = NULL;
-
-               /* remove old instance first */
-               for (subcomp = icalcomponent_get_first_component (icalcomp, my_kind);
-                    subcomp;
-                    subcomp = start_first ? icalcomponent_get_first_component (icalcomp, my_kind) : 
icalcomponent_get_next_component (icalcomp, my_kind)) {
-                       struct icaltimetype sub_rid = icalcomponent_get_recurrenceid (subcomp);
-
-                       start_first = FALSE;
-
-                       if (icaltime_is_null_time (sub_rid)) {
-                               master = subcomp;
-                               left++;
-                       } else if (icaltime_compare (sub_rid, rid) == 0) {
-                               icalcomponent_remove_component (icalcomp, subcomp);
-                               icalcomponent_free (subcomp);
-                               if (master) {
-                                       break;
-                               } else {
-                                       /* either no master or master not as the first component, thus rescan 
*/
-                                       left = 0;
-                                       start_first = TRUE;
-                               }
-                       } else {
-                               left++;
-                       }
-               }
-
-               /* whether left at least one instance or a master object */
-               res = left > 0;
-       } else {
-               res = TRUE;
-       }
-
-       if (master && also_exdate) {
-               e_cal_util_remove_instances (master, rid, mod);
-       }
-
-       return res;
-}
-
-static icalcomponent *
-replace_master (ECalBackendCalDAV *cbdav,
-                icalcomponent *old_comp,
-                icalcomponent *new_master)
-{
-       icalcomponent *old_master;
-       if (icalcomponent_isa (old_comp) != ICAL_VCALENDAR_COMPONENT) {
-               icalcomponent_free (old_comp);
-               return new_master;
-       }
-
-       old_master = get_master_comp (cbdav, old_comp);
-       if (!old_master) {
-               /* no master, strange */
-               icalcomponent_free (new_master);
-       } else {
-               icalcomponent_remove_component (old_comp, old_master);
-               icalcomponent_free (old_master);
-
-               icalcomponent_add_component (old_comp, new_master);
-       }
-
-       return old_comp;
-}
-
-/* the resulting component should be unreffed when done with it;
- * the fallback_comp is cloned, if used */
-static ECalComponent *
-get_ecalcomp_master_from_cache_or_fallback (ECalBackendCalDAV *cbdav,
-                                            const gchar *uid,
-                                            const gchar *rid,
-                                            ECalComponent *fallback_comp)
+ecb_caldav_propfind_get_schedule_outbox_url_cb (EWebDAVSession *webdav,
+                                               xmlXPathContextPtr xpath_ctx,
+                                               const gchar *xpath_prop_prefix,
+                                               const SoupURI *request_uri,
+                                               const gchar *href,
+                                               guint status_code,
+                                               gpointer user_data)
 {
-       ECalComponent *comp = NULL;
-       icalcomponent *icalcomp;
-
-       g_return_val_if_fail (cbdav != NULL, NULL);
-       g_return_val_if_fail (uid != NULL, NULL);
+       gchar **out_schedule_outbox_url = user_data;
 
-       icalcomp = get_comp_from_cache (cbdav, uid, rid, NULL, NULL);
-       if (icalcomp) {
-               icalcomponent *master = get_master_comp (cbdav, icalcomp);
-
-               if (master) {
-                       comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master));
-               }
-
-               icalcomponent_free (icalcomp);
-       }
+       g_return_val_if_fail (out_schedule_outbox_url != NULL, FALSE);
 
-       if (!comp && fallback_comp)
-               comp = e_cal_component_clone (fallback_comp);
+       if (!xpath_prop_prefix) {
+               e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CALDAV, NULL);
+       } else if (status_code == SOUP_STATUS_OK) {
+               gchar *tmp = e_xml_xpath_eval_as_string (xpath_ctx, "%s/C:schedule-outbox-URL/D:href", 
xpath_prop_prefix);
 
-       return comp;
-}
-
-/* a busy_lock is supposed to be locked already, when calling this function */
-static void
-do_create_objects (ECalBackendCalDAV *cbdav,
-                   const GSList *in_calobjs,
-                   GSList **uids,
-                   GSList **new_components,
-                   GCancellable *cancellable,
-                   GError **perror)
-{
-       ECalComponent            *comp;
-       gboolean                  online, did_put = FALSE;
-       struct icaltimetype current;
-       icalcomponent *icalcomp;
-       const gchar *in_calobj = in_calobjs->data;
-       const gchar *comp_uid;
-
-       if (!check_state (cbdav, &online, perror))
-               return;
-
-       /* We make the assumption that the in_calobjs list we're passed is always exactly one element long, 
since we haven't specified "bulk-adds"
-        * in our static capability list. This simplifies a lot of the logic, especially around asynchronous 
results. */
-       if (in_calobjs->next != NULL) {
-               g_propagate_error (perror, e_data_cal_create_error (UnsupportedMethod, _("CalDAV does not 
support bulk additions")));
-               return;
-       }
-
-       comp = e_cal_component_new_from_string (in_calobj);
-
-       if (comp == NULL) {
-               g_propagate_error (perror, EDC_ERROR (InvalidObject));
-               return;
-       }
-
-       icalcomp = e_cal_component_get_icalcomponent (comp);
-       if (icalcomp == NULL) {
-               g_object_unref (comp);
-               g_propagate_error (perror, EDC_ERROR (InvalidObject));
-               return;
-       }
-
-       comp_uid = icalcomponent_get_uid (icalcomp);
-       if (!comp_uid) {
-               gchar *new_uid;
-
-               new_uid = e_cal_component_gen_uid ();
-               if (!new_uid) {
-                       g_object_unref (comp);
-                       g_propagate_error (perror, EDC_ERROR (InvalidObject));
-                       return;
-               }
+               if (tmp && *tmp)
+                       *out_schedule_outbox_url = e_webdav_session_ensure_full_uri (webdav, request_uri, 
tmp);
 
-               icalcomponent_set_uid (icalcomp, new_uid);
-               comp_uid = icalcomponent_get_uid (icalcomp);
-
-               g_free (new_uid);
-       }
-
-       /* check the object is not in our cache */
-       if (cache_contains (cbdav, comp_uid, NULL)) {
-               g_object_unref (comp);
-               g_propagate_error (perror, EDC_ERROR (ObjectIdAlreadyExists));
-               return;
-       }
-
-       /* Set the created and last modified times on the component */
-       current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
-       e_cal_component_set_created (comp, &current);
-       e_cal_component_set_last_modified (comp, &current);
-
-       /* sanitize the component*/
-       sanitize_component (cbdav, comp);
-
-       if (online) {
-               CalDAVObject object;
-
-               object.href = ecalcomp_gen_href (comp);
-               object.etag = NULL;
-               object.cdata = pack_cobj (cbdav, icalcomp);
-
-               did_put = caldav_server_put_object (cbdav, &object, icalcomp, cancellable, perror);
-
-               caldav_object_free (&object, FALSE);
-       } else {
-               /* mark component as out of synch */
-               /*ecalcomp_set_synch_state (comp, ECALCOMP_LOCALLY_CREATED); */
-       }
-
-       if (did_put) {
-               if (uids)
-                       *uids = g_slist_prepend (*uids, g_strdup (comp_uid));
+               g_free (tmp);
 
-               if (new_components)
-                       *new_components = g_slist_prepend(*new_components, 
get_ecalcomp_master_from_cache_or_fallback (cbdav, comp_uid, NULL, comp));
+               return FALSE;
        }
 
-       g_object_unref (comp);
+       return TRUE;
 }
 
-/* a busy_lock is supposed to be locked already, when calling this function */
-static void
-do_modify_objects (ECalBackendCalDAV *cbdav,
-                   const GSList *calobjs,
-                   ECalObjModType mod,
-                   GSList **old_components,
-                   GSList **new_components,
-                   GCancellable *cancellable,
-                   GError **error)
+static gboolean
+ecb_caldav_receive_schedule_outbox_url_sync (ECalBackendCalDAV *cbdav,
+                                            GCancellable *cancellable,
+                                            GError **error)
 {
-       ECalComponent            *comp;
-       icalcomponent            *cache_comp, *master_comp;
-       gboolean                  online, did_put = FALSE, success = TRUE;
-       ECalComponentId          *id;
-       struct icaltimetype current;
-       gchar *href = NULL, *etag = NULL;
-       const gchar *calobj = calobjs->data;
-
-       if (new_components)
-               *new_components = NULL;
-
-       if (!check_state (cbdav, &online, error))
-               return;
-
-       /* We make the assumption that the calobjs list we're passed is always exactly one element long, 
since we haven't specified "bulk-modifies"
-        * in our static capability list. This simplifies a lot of the logic, especially around asynchronous 
results. */
-       if (calobjs->next != NULL) {
-               g_propagate_error (error, e_data_cal_create_error (UnsupportedMethod, _("CalDAV does not 
support bulk modifications")));
-               return;
-       }
-
-       comp = e_cal_component_new_from_string (calobj);
-
-       if (comp == NULL) {
-               g_propagate_error (error, EDC_ERROR (InvalidObject));
-               return;
-       }
+       EXmlDocument *xml;
+       gchar *owner_href = NULL, *schedule_outbox_url = NULL;
+       gboolean success;
 
-       if (!e_cal_component_get_icalcomponent (comp) ||
-           icalcomponent_isa (e_cal_component_get_icalcomponent (comp)) != e_cal_backend_get_kind 
(E_CAL_BACKEND (cbdav))) {
-               g_object_unref (comp);
-               g_propagate_error (error, EDC_ERROR (InvalidObject));
-               return;
-       }
+       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
+       g_return_val_if_fail (cbdav->priv->schedule_outbox_url == NULL, TRUE);
 
-       /* Set the last modified time on the component */
-       current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
-       e_cal_component_set_last_modified (comp, &current);
+       xml = e_xml_document_new (E_WEBDAV_NS_DAV, "propfind");
+       g_return_val_if_fail (xml != NULL, FALSE);
 
-       /* sanitize the component */
-       sanitize_component (cbdav, comp);
+       e_xml_document_start_element (xml, NULL, "prop");
+       e_xml_document_add_empty_element (xml, NULL, "owner");
+       e_xml_document_end_element (xml); /* prop */
 
-       id = e_cal_component_get_id (comp);
-       if (id == NULL) {
-               g_set_error_literal (
-                       error, E_CAL_CLIENT_ERROR,
-                       E_CAL_CLIENT_ERROR_INVALID_OBJECT,
-                       e_cal_client_error_to_string (
-                       E_CAL_CLIENT_ERROR_INVALID_OBJECT));
-               return;
-       }
+       success = e_webdav_session_propfind_sync (cbdav->priv->webdav, NULL, E_WEBDAV_DEPTH_THIS, xml,
+               ecb_caldav_propfind_get_owner_cb, &owner_href, cancellable, error);
 
-       /* fetch full component from cache, it will be pushed to the server */
-       cache_comp = get_comp_from_cache (cbdav, id->uid, NULL, &href, &etag);
+       g_object_unref (xml);
 
-       if (cache_comp == NULL) {
-               e_cal_component_free_id (id);
-               g_object_unref (comp);
-               g_free (href);
-               g_free (etag);
-               g_propagate_error (error, EDC_ERROR (ObjectNotFound));
-               return;
-       }
-
-       if (!online) {
-               /* mark component as out of synch */
-               /*ecalcomp_set_synch_state (comp, ECALCOMP_LOCALLY_MODIFIED);*/
+       if (!success || !owner_href || !*owner_href) {
+               g_free (owner_href);
+               return FALSE;
        }
 
-       if (old_components) {
-               *old_components = NULL;
-
-               if (e_cal_component_is_instance (comp)) {
-                       /* set detached instance as the old object, if any */
-                       ECalComponent *old_instance = e_cal_backend_store_get_component (cbdav->priv->store, 
id->uid, id->rid);
-
-                       /* This will give a reference to 'old_component' */
-                       if (old_instance) {
-                               *old_components = g_slist_prepend (*old_components, e_cal_component_clone 
(old_instance));
-                               g_object_unref (old_instance);
-                       }
-               }
-
-               if (!*old_components) {
-                       icalcomponent *master = get_master_comp (cbdav, cache_comp);
-
-                       if (master) {
-                               /* set full component as the old object */
-                               *old_components = g_slist_prepend (*old_components, 
e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master)));
-                       }
-               }
+       xml = e_xml_document_new (E_WEBDAV_NS_DAV, "propfind");
+       if (!xml) {
+               g_warn_if_fail (xml != NULL);
+               g_free (owner_href);
+               return FALSE;
        }
 
-       switch (mod) {
-       case E_CAL_OBJ_MOD_ONLY_THIS:
-       case E_CAL_OBJ_MOD_THIS:
-               if (e_cal_component_is_instance (comp)) {
-                       icalcomponent *new_comp = e_cal_component_get_icalcomponent (comp);
-
-                       /* new object is only this instance */
-                       if (new_components)
-                               *new_components = g_slist_prepend (*new_components, e_cal_component_clone 
(comp));
-
-                       /* add the detached instance */
-                       if (icalcomponent_isa (cache_comp) == ICAL_VCALENDAR_COMPONENT) {
-                               /* do not modify the EXDATE, as the component will be put back */
-                               remove_instance (cbdav, cache_comp, icalcomponent_get_recurrenceid 
(new_comp), mod, FALSE);
-                       } else {
-                               /* this is only a master object, thus make is a VCALENDAR component */
-                               icalcomponent *icomp;
-
-                               icomp = e_cal_util_new_top_level ();
-                               icalcomponent_add_component (icomp, cache_comp);
-
-                               /* no need to free the cache_comp, as it is inside icomp */
-                               cache_comp = icomp;
-                       }
+       e_xml_document_add_namespaces (xml, "C", E_WEBDAV_NS_CALDAV, NULL);
 
-                       if (cache_comp && cbdav->priv->is_google) {
-                               icalcomponent_set_sequence (cache_comp, icalcomponent_get_sequence 
(cache_comp) + 1);
-                               icalcomponent_set_sequence (new_comp, icalcomponent_get_sequence (new_comp) + 
1);
-                       }
+       e_xml_document_start_element (xml, NULL, "prop");
+       e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CALDAV, "schedule-outbox-URL");
+       e_xml_document_end_element (xml); /* prop */
 
-                       /* add the detached instance finally */
-                       icalcomponent_add_component (cache_comp, icalcomponent_new_clone (new_comp));
-               } else {
-                       cache_comp = replace_master (cbdav, cache_comp, icalcomponent_new_clone 
(e_cal_component_get_icalcomponent (comp)));
-               }
-               break;
-       case E_CAL_OBJ_MOD_ALL:
-               e_cal_recur_ensure_end_dates (comp, TRUE, resolve_tzid, cbdav);
-               cache_comp = replace_master (cbdav, cache_comp, icalcomponent_new_clone 
(e_cal_component_get_icalcomponent (comp)));
-               break;
-       case E_CAL_OBJ_MOD_THIS_AND_PRIOR:
-       case E_CAL_OBJ_MOD_THIS_AND_FUTURE:
-               master_comp = get_master_comp (cbdav, cache_comp);
-               if (e_cal_component_is_instance (comp) && master_comp) {
-                       ECalComponent *mcomp;
-                       struct icaltimetype rid, master_dtstart;
-                       icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
-                       icalcomponent *split_icalcomp;
-                       icalproperty *prop;
-
-                       rid = icalcomponent_get_recurrenceid (icalcomp);
-                       mcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone 
(master_comp));
-
-                       if (mod == E_CAL_OBJ_MOD_THIS_AND_FUTURE &&
-                           e_cal_util_is_first_instance (mcomp, icalcomponent_get_recurrenceid (icalcomp), 
resolve_tzid, cbdav)) {
-                               icalproperty *prop = icalcomponent_get_first_property (icalcomp, 
ICAL_RECURRENCEID_PROPERTY);
-
-                               if (prop)
-                                       icalcomponent_remove_property (icalcomp, prop);
-
-                               e_cal_component_rescan (comp);
-                               e_cal_recur_ensure_end_dates (comp, TRUE, resolve_tzid, cbdav);
-
-                               /* Then do it like for "mod_all" */
-                               cache_comp = replace_master (cbdav, cache_comp, icalcomponent_new_clone 
(e_cal_component_get_icalcomponent (comp)));
-                               g_clear_object (&mcomp);
-
-                               if (new_components) {
-                                       /* read the comp from cache again, as some servers can modify it on 
put */
-                                       *new_components = g_slist_prepend (*new_components, 
get_ecalcomp_master_from_cache_or_fallback (cbdav, id->uid, NULL, comp));
-                               }
-                               break;
-                       }
-
-                       prop = icalcomponent_get_first_property (icalcomp, ICAL_RECURRENCEID_PROPERTY);
-                       if (prop)
-                               icalcomponent_remove_property (icalcomp, prop);
-                       e_cal_component_rescan (comp);
-
-                       master_dtstart = icalcomponent_get_dtstart (master_comp);
-                       if (master_dtstart.zone && master_dtstart.zone != rid.zone)
-                               rid = icaltime_convert_to_zone (rid, (icaltimezone *) master_dtstart.zone);
-                       split_icalcomp = e_cal_util_split_at_instance (icalcomp, rid, master_dtstart);
-                       if (split_icalcomp) {
-                               ECalComponent *prev_comp;
-
-                               prev_comp = e_cal_component_clone (mcomp);
+       success = e_webdav_session_propfind_sync (cbdav->priv->webdav, owner_href, E_WEBDAV_DEPTH_THIS, xml,
+               ecb_caldav_propfind_get_schedule_outbox_url_cb, &schedule_outbox_url, cancellable, error);
 
-                               rid = icaltime_convert_to_zone (rid, icaltimezone_get_utc_timezone ());
-                               e_cal_util_remove_instances (master_comp, rid, mod);
-                               e_cal_component_rescan (mcomp);
-                               e_cal_recur_ensure_end_dates (mcomp, TRUE, resolve_tzid, cbdav);
+       g_object_unref (xml);
+       g_free (owner_href);
 
-                               if (new_components) {
-                                       *new_components = g_slist_prepend (*new_components,
-                                               get_ecalcomp_master_from_cache_or_fallback (cbdav, id->uid, 
NULL, mcomp));
-                               }
-
-                               g_clear_object (&prev_comp);
-                       }
-
-                       cache_comp = replace_master (cbdav, cache_comp, icalcomponent_new_clone 
(master_comp));
-                       if (split_icalcomp) {
-                               gchar *new_uid;
-
-                               new_uid = e_cal_component_gen_uid ();
-                               icalcomponent_set_uid (split_icalcomp, new_uid);
-                               g_free (new_uid);
-
-                               g_warn_if_fail (e_cal_component_set_icalcomponent (comp, split_icalcomp));
-
-                               e_cal_recur_ensure_end_dates (comp, TRUE, resolve_tzid, cbdav);
-
-                               /* sanitize the component */
-                               sanitize_component (cbdav, comp);
-
-                               if (online) {
-                                       CalDAVObject object;
-
-                                       object.href = ecalcomp_gen_href (comp);
-                                       object.etag = NULL;
-                                       object.cdata = pack_cobj (cbdav, split_icalcomp);
-
-                                       success = caldav_server_put_object (cbdav, &object, split_icalcomp, 
cancellable, error);
-                                       if (success && new_components) {
-                                               ECalComponent *new_comp;
-
-                                               /* read the comp from cache again, as some servers can modify 
it on put */
-                                               new_comp = get_ecalcomp_master_from_cache_or_fallback (cbdav, 
icalcomponent_get_uid (split_icalcomp), NULL, comp);
-                                               if (new_comp)
-                                                       e_cal_backend_notify_component_created (E_CAL_BACKEND 
(cbdav), new_comp);
-
-                                               g_clear_object (&new_comp);
-                                       }
-
-                                       caldav_object_free (&object, FALSE);
-                               } else {
-                                       /* mark component as out of synch */
-                                       /*ecalcomp_set_synch_state (comp, ECALCOMP_LOCALLY_CREATED); */
-                               }
-                       }
+       if (success && schedule_outbox_url && *schedule_outbox_url) {
+               g_free (cbdav->priv->schedule_outbox_url);
+               cbdav->priv->schedule_outbox_url = schedule_outbox_url;
 
-                       g_clear_object (&mcomp);
-               } else {
-                       cache_comp = replace_master (cbdav, cache_comp, icalcomponent_new_clone 
(e_cal_component_get_icalcomponent (comp)));
-               }
-               break;
-       }
-
-       if (online) {
-               CalDAVObject object;
-
-               object.href = href;
-               object.etag = etag;
-               object.cdata = pack_cobj (cbdav, cache_comp);
-
-               did_put = success && caldav_server_put_object (cbdav, &object, cache_comp, cancellable, 
error);
-
-               caldav_object_free (&object, FALSE);
-               href = NULL;
-               etag = NULL;
+               schedule_outbox_url = NULL;
        } else {
-               /* mark component as out of synch */
-               /*ecalcomp_set_synch_state (comp, ECALCOMP_LOCALLY_MODIFIED);*/
-       }
-
-       if (did_put) {
-               if (new_components && !*new_components) {
-                       /* read the comp from cache again, as some servers can modify it on put */
-                       *new_components = g_slist_prepend (*new_components, 
get_ecalcomp_master_from_cache_or_fallback (cbdav, id->uid, id->rid, NULL));
-               }
-       }
-
-       e_cal_component_free_id (id);
-       icalcomponent_free (cache_comp);
-       g_object_unref (comp);
-       g_free (href);
-       g_free (etag);
-}
-
-/* a busy_lock is supposed to be locked already, when calling this function */
-static void
-do_remove_objects (ECalBackendCalDAV *cbdav,
-                   const GSList *ids,
-                   ECalObjModType mod,
-                   GSList **old_components,
-                   GSList **new_components,
-                   GCancellable *cancellable,
-                   GError **perror)
-{
-       icalcomponent            *cache_comp;
-       gboolean                  online;
-       gchar *href = NULL, *etag = NULL;
-       const gchar *uid = ((ECalComponentId *) ids->data)->uid;
-       const gchar *rid = ((ECalComponentId *) ids->data)->rid;
-
-       if (new_components)
-               *new_components = NULL;
-
-       if (!check_state (cbdav, &online, perror))
-               return;
-
-       /* We make the assumption that the ids list we're passed is always exactly one element long, since we 
haven't specified "bulk-removes"
-        * in our static capability list. This simplifies a lot of the logic, especially around asynchronous 
results. */
-       if (ids->next != NULL) {
-               g_propagate_error (perror, e_data_cal_create_error (UnsupportedMethod, _("CalDAV does not 
support bulk removals")));
-               return;
-       }
-
-       cache_comp = get_comp_from_cache (cbdav, uid, NULL, &href, &etag);
-
-       if (cache_comp == NULL) {
-               g_propagate_error (perror, EDC_ERROR (ObjectNotFound));
-               return;
-       }
-
-       if (old_components) {
-               ECalComponent *old = e_cal_backend_store_get_component (cbdav->priv->store, uid, rid);
-
-               if (old) {
-                       *old_components = g_slist_prepend (*old_components, e_cal_component_clone (old));
-                       g_object_unref (old);
-               } else {
-                       icalcomponent *master = get_master_comp (cbdav, cache_comp);
-                       if (master) {
-                               *old_components = g_slist_prepend (*old_components, 
e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master)));
-                       }
-               }
-       }
-
-       switch (mod) {
-       case E_CAL_OBJ_MOD_ONLY_THIS:
-       case E_CAL_OBJ_MOD_THIS:
-               if (rid && *rid) {
-                       /* remove one instance from the component */
-                       if (remove_instance (cbdav, cache_comp, icaltime_from_string (rid), mod, mod != 
E_CAL_OBJ_MOD_ONLY_THIS)) {
-                               if (new_components) {
-                                       icalcomponent *master = get_master_comp (cbdav, cache_comp);
-                                       if (master) {
-                                               *new_components = g_slist_prepend (*new_components, 
e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master)));
-                                       }
-                               }
-                       } else {
-                               /* this was the last instance, thus delete whole component */
-                               rid = NULL;
-                               remove_comp_from_cache (cbdav, uid, NULL);
-                       }
-               } else {
-                       /* remove whole object */
-                       remove_comp_from_cache (cbdav, uid, NULL);
-               }
-               break;
-       case E_CAL_OBJ_MOD_ALL:
-               remove_comp_from_cache (cbdav, uid, NULL);
-               break;
-       case E_CAL_OBJ_MOD_THIS_AND_PRIOR:
-       case E_CAL_OBJ_MOD_THIS_AND_FUTURE:
-               if (remove_instance (cbdav, cache_comp, icaltime_from_string (rid), mod, TRUE)) {
-                       if (new_components) {
-                               icalcomponent *master = get_master_comp (cbdav, cache_comp);
-                               if (master) {
-                                       *new_components = g_slist_prepend (*new_components, 
e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master)));
-                               }
-                       }
-               }
-               break;
+               success = FALSE;
        }
 
-       if (online) {
-               CalDAVObject caldav_object;
-
-               caldav_object.href = href;
-               caldav_object.etag = etag;
-               caldav_object.cdata = NULL;
-
-               if (mod == E_CAL_OBJ_MOD_THIS && rid && *rid) {
-                       caldav_object.cdata = pack_cobj (cbdav, cache_comp);
-
-                       caldav_server_put_object (cbdav, &caldav_object, cache_comp, cancellable, perror);
-               } else
-                       caldav_server_delete_object (cbdav, &caldav_object, cancellable, perror);
-
-               caldav_object_free (&caldav_object, FALSE);
-               href = NULL;
-               etag = NULL;
-       } else {
-               /* mark component as out of synch */
-               /*if (mod == E_CAL_OBJ_MOD_THIS && rid && *rid)
-                       ecalcomp_set_synch_state (cache_comp_master, ECALCOMP_LOCALLY_MODIFIED);
-               else
-                       ecalcomp_set_synch_state (cache_comp_master, ECALCOMP_LOCALLY_DELETED);*/
-       }
-       remove_cached_attachment (cbdav, uid);
+       g_free (schedule_outbox_url);
 
-       icalcomponent_free (cache_comp);
-       g_free (href);
-       g_free (etag);
+       return success;
 }
 
 static void
-extract_objects (icalcomponent *icomp,
-                 icalcomponent_kind ekind,
-                 GSList **objects,
-                 GError **error)
+ecb_caldav_extract_objects (icalcomponent *icomp,
+                           icalcomponent_kind ekind,
+                           GSList **out_objects,
+                           GError **error)
 {
-       icalcomponent         *scomp;
-       icalcomponent_kind     kind;
+       icalcomponent *scomp;
+       icalcomponent_kind kind;
        GSList *link;
 
+       g_return_if_fail (icomp != NULL);
+       g_return_if_fail (out_objects != NULL);
+
        kind = icalcomponent_isa (icomp);
 
        if (kind == ekind) {
-               *objects = g_slist_prepend (NULL, icomp);
+               *out_objects = g_slist_prepend (NULL, icalcomponent_new_clone (icomp));
                return;
        }
 
@@ -4634,648 +1252,107 @@ extract_objects (icalcomponent *icomp,
                return;
        }
 
-       *objects = NULL;
+       *out_objects = NULL;
        scomp = icalcomponent_get_first_component (icomp, ekind);
 
        while (scomp) {
-               *objects = g_slist_prepend (*objects, scomp);
+               *out_objects = g_slist_prepend (*out_objects, scomp);
 
                scomp = icalcomponent_get_next_component (icomp, ekind);
        }
 
-       for (link = *objects; link; link = g_slist_next (link)) {
+       for (link = *out_objects; link; link = g_slist_next (link)) {
                /* Remove components from toplevel here */
                icalcomponent_remove_component (icomp, link->data);
        }
-}
 
-static gboolean
-extract_timezones (ECalBackendCalDAV *cbdav,
-                   icalcomponent *icomp)
-{
-       ETimezoneCache *timezone_cache;
-       GSList *timezones = NULL, *iter;
-       icaltimezone *zone;
-       GError *err = NULL;
-
-       g_return_val_if_fail (cbdav != NULL, FALSE);
-       g_return_val_if_fail (icomp != NULL, FALSE);
-
-       timezone_cache = E_TIMEZONE_CACHE (cbdav);
-
-       extract_objects (icomp, ICAL_VTIMEZONE_COMPONENT, &timezones, &err);
-       if (err) {
-               g_error_free (err);
-               return FALSE;
-       }
-
-       zone = icaltimezone_new ();
-       for (iter = timezones; iter; iter = iter->next) {
-               if (icaltimezone_set_component (zone, iter->data)) {
-                       e_timezone_cache_add_timezone (timezone_cache, zone);
-               } else {
-                       icalcomponent_free (iter->data);
-               }
-       }
-
-       icaltimezone_free (zone, TRUE);
-       g_slist_free (timezones);
-
-       return TRUE;
+       *out_objects = g_slist_reverse (*out_objects);
 }
 
-static void
-process_object (ECalBackendCalDAV *cbdav,
-                ECalComponent *ecomp,
-                gboolean online,
-                icalproperty_method method,
-                GCancellable *cancellable,
-                GError **error)
+static gchar *
+ecb_caldav_maybe_append_email_domain (const gchar *username,
+                                     const gchar *may_append)
 {
-       ESourceRegistry *registry;
-       ECalBackend              *backend;
-       struct icaltimetype       now;
-       gchar *new_obj_str;
-       gboolean is_declined, is_in_cache;
-       ECalObjModType mod;
-       ECalComponentId *id = e_cal_component_get_id (ecomp);
-       GError *err = NULL;
-
-       backend = E_CAL_BACKEND (cbdav);
-
-       if (id == NULL) {
-               g_set_error_literal (
-                       error, E_CAL_CLIENT_ERROR,
-                       E_CAL_CLIENT_ERROR_INVALID_OBJECT,
-                       e_cal_client_error_to_string (
-                       E_CAL_CLIENT_ERROR_INVALID_OBJECT));
-               return;
-       }
-
-       registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbdav));
-
-       /* ctime, mtime */
-       now = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
-       e_cal_component_set_created (ecomp, &now);
-       e_cal_component_set_last_modified (ecomp, &now);
-
-       /* just to check whether component exists in a cache */
-       is_in_cache = cache_contains (cbdav, id->uid, NULL) || cache_contains (cbdav, id->uid, id->rid);
-
-       new_obj_str = e_cal_component_get_as_string (ecomp);
-       mod = e_cal_component_is_instance (ecomp) ? E_CAL_OBJ_MOD_THIS : E_CAL_OBJ_MOD_ALL;
-
-       switch (method) {
-       case ICAL_METHOD_PUBLISH:
-       case ICAL_METHOD_REQUEST:
-       case ICAL_METHOD_REPLY:
-               is_declined = e_cal_backend_user_declined (
-                       registry, e_cal_component_get_icalcomponent (ecomp));
-               if (is_in_cache) {
-                       if (!is_declined) {
-                               GSList *new_components = NULL, *old_components = NULL;
-                               GSList new_obj_strs = {0,};
-
-                               new_obj_strs.data = new_obj_str;
-                               do_modify_objects (cbdav, &new_obj_strs, mod,
-                                                 &old_components, &new_components, cancellable, &err);
-                               if (!err && new_components && new_components->data) {
-                                       if (!old_components || !old_components->data) {
-                                               e_cal_backend_notify_component_created (backend, 
new_components->data);
-                                       } else {
-                                               e_cal_backend_notify_component_modified (backend, 
old_components->data, new_components->data);
-                                       }
-                               }
-
-                               e_util_free_nullable_object_slist (old_components);
-                               e_util_free_nullable_object_slist (new_components);
-                       } else {
-                               GSList *new_components = NULL, *old_components = NULL;
-                               GSList ids = {0,};
-
-                               ids.data = id;
-                               do_remove_objects (cbdav, &ids, mod, &old_components, &new_components, 
cancellable, &err);
-                               if (!err && old_components && old_components->data) {
-                                       if (new_components && new_components->data) {
-                                               e_cal_backend_notify_component_modified (backend, 
old_components->data, new_components->data);
-                                       } else {
-                                               e_cal_backend_notify_component_removed (backend, id, 
old_components->data, NULL);
-                                       }
-                               }
-
-                               e_util_free_nullable_object_slist (old_components);
-                               e_util_free_nullable_object_slist (new_components);
-                       }
-               } else if (!is_declined) {
-                       GSList *new_components = NULL;
-                       GSList new_objs = {0,};
-
-                       new_objs.data = new_obj_str;
-
-                       do_create_objects (cbdav, &new_objs, NULL, &new_components, cancellable, &err);
-
-                       if (!err) {
-                               if (new_components && new_components->data)
-                                       e_cal_backend_notify_component_created (backend, 
new_components->data);
-                       }
-
-                       e_util_free_nullable_object_slist (new_components);
-               }
-               break;
-       case ICAL_METHOD_CANCEL:
-               if (is_in_cache) {
-                       GSList *new_components = NULL, *old_components = NULL;
-                       GSList ids = {0,};
-
-                       ids.data = id;
-                       do_remove_objects (cbdav, &ids, E_CAL_OBJ_MOD_THIS, &old_components, &new_components, 
cancellable, &err);
-                       if (!err && old_components && old_components->data) {
-                               if (new_components && new_components->data) {
-                                       e_cal_backend_notify_component_modified (backend, 
old_components->data, new_components->data);
-                               } else {
-                                       e_cal_backend_notify_component_removed (backend, id, 
old_components->data, NULL);
-                               }
-                       }
-
-                       e_util_free_nullable_object_slist (old_components);
-                       e_util_free_nullable_object_slist (new_components);
-               } else {
-                       err = EDC_ERROR (ObjectNotFound);
-               }
-               break;
-
-       default:
-               err = EDC_ERROR (UnsupportedMethod);
-               break;
-       }
+       if (!username || !*username)
+               return NULL;
 
-       e_cal_component_free_id (id);
-       g_free (new_obj_str);
+       if (strchr (username, '@'))
+               return g_strdup (username);
 
-       if (err)
-               g_propagate_error (error, err);
+       return g_strconcat (username, may_append, NULL);
 }
 
-static void
-do_receive_objects (ECalBackendSync *backend,
-                    EDataCal *cal,
-                    GCancellable *cancellable,
-                    const gchar *calobj,
-                    GError **perror)
+static gchar *
+ecb_caldav_get_usermail (ECalBackendCalDAV *cbdav)
 {
-       ECalBackendCalDAV        *cbdav;
-       icalcomponent            *icomp;
-       icalcomponent_kind        kind;
-       icalproperty_method       tmethod;
-       gboolean                  online;
-       GSList                   *objects, *iter;
-       GError *err = NULL;
-
-       cbdav = E_CAL_BACKEND_CALDAV (backend);
-
-       if (!check_state (cbdav, &online, perror))
-               return;
-
-       icomp = icalparser_parse_string (calobj);
-
-       /* Try to parse cal object string */
-       if (icomp == NULL) {
-               g_propagate_error (perror, EDC_ERROR (InvalidObject));
-               return;
-       }
-
-       kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend));
-       extract_objects (icomp, kind, &objects, &err);
-
-       if (err) {
-               icalcomponent_free (icomp);
-               g_propagate_error (perror, err);
-               return;
-       }
-
-       /* Extract optional timezone compnents */
-       extract_timezones (cbdav, icomp);
-
-       if (icalcomponent_get_first_property (icomp, ICAL_METHOD_PROPERTY))
-               tmethod = icalcomponent_get_method (icomp);
-       else
-               tmethod = ICAL_METHOD_PUBLISH;
-
-       for (iter = objects; iter && !err; iter = iter->next) {
-               icalcomponent       *scomp;
-               ECalComponent       *ecomp;
-               icalproperty_method  method;
-
-               scomp = (icalcomponent *) iter->data;
-               ecomp = e_cal_component_new ();
+       ESource *source;
+       ESourceAuthentication *auth_extension;
+       ESourceWebdav *webdav_extension;
+       const gchar *extension_name;
+       gchar *usermail;
+       gchar *username;
+       gchar *res = NULL;
 
-               e_cal_component_set_icalcomponent (ecomp, scomp);
+       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), NULL);
 
-               if (icalcomponent_get_first_property (scomp, ICAL_METHOD_PROPERTY)) {
-                       method = icalcomponent_get_method (scomp);
-               } else {
-                       method = tmethod;
-               }
+       source = e_backend_get_source (E_BACKEND (cbdav));
 
-               process_object (cbdav, ecomp, online, method, cancellable, &err);
-               g_object_unref (ecomp);
-       }
+       extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
+       webdav_extension = e_source_get_extension (source, extension_name);
 
-       g_slist_free (objects);
+       /* This will never return an empty string. */
+       usermail = e_source_webdav_dup_email_address (webdav_extension);
 
-       icalcomponent_free (icomp);
+       if (usermail)
+               return usermail;
 
-       if (err)
-               g_propagate_error (perror, err);
-}
+       extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
+       auth_extension = e_source_get_extension (source, extension_name);
+       username = e_source_authentication_dup_user (auth_extension);
 
-#define caldav_busy_stub(_func_name, _params, _call_func, _call_params) \
-static void \
-_func_name _params \
-{ \
-       ECalBackendCalDAV        *cbdav; \
-       SlaveCommand              old_slave_cmd; \
-       gboolean                  was_slave_busy; \
- \
-       cbdav = E_CAL_BACKEND_CALDAV (backend); \
- \
-       /* this is done before locking */ \
-       old_slave_cmd = cbdav->priv->slave_cmd; \
-       was_slave_busy = cbdav->priv->slave_busy; \
-       if (was_slave_busy) { \
-               /* let it pause its work and do our job */ \
-               update_slave_cmd (cbdav->priv, SLAVE_SHOULD_SLEEP); \
-       } \
- \
-       g_mutex_lock (&cbdav->priv->busy_lock); \
-       _call_func _call_params; \
- \
-       /* this is done before unlocking */ \
-       if (was_slave_busy) { \
-               update_slave_cmd (cbdav->priv, old_slave_cmd); \
-               g_cond_signal (&cbdav->priv->cond); \
-       } \
- \
-       g_mutex_unlock (&cbdav->priv->busy_lock); \
-}
+       if (cbdav->priv->is_google)
+               res = ecb_caldav_maybe_append_email_domain (username, "@gmail.com");
 
-caldav_busy_stub (
-        caldav_create_objects,
-                  (ECalBackendSync *backend,
-                  EDataCal *cal,
-                  GCancellable *cancellable,
-                  const GSList *in_calobjs,
-                  GSList **uids,
-                  GSList **new_components,
-                  GError **perror),
-        do_create_objects,
-                  (cbdav,
-                  in_calobjs,
-                  uids,
-                  new_components,
-                  cancellable,
-                  perror))
-
-caldav_busy_stub (
-        caldav_modify_objects,
-                  (ECalBackendSync *backend,
-                  EDataCal *cal,
-                  GCancellable *cancellable,
-                  const GSList *calobjs,
-                  ECalObjModType mod,
-                  GSList **old_components,
-                  GSList **new_components,
-                  GError **perror),
-        do_modify_objects,
-                  (cbdav,
-                  calobjs,
-                  mod,
-                  old_components,
-                  new_components,
-                  cancellable,
-                  perror))
-
-caldav_busy_stub (
-        caldav_remove_objects,
-                  (ECalBackendSync *backend,
-                  EDataCal *cal,
-                  GCancellable *cancellable,
-                  const GSList *ids,
-                  ECalObjModType mod,
-                  GSList **old_components,
-                  GSList **new_components,
-                  GError **perror),
-        do_remove_objects,
-                  (cbdav,
-                  ids,
-                  mod,
-                  old_components,
-                  new_components,
-                  cancellable,
-                  perror))
-
-caldav_busy_stub (
-        caldav_receive_objects,
-                  (ECalBackendSync *backend,
-                  EDataCal *cal,
-                  GCancellable *cancellable,
-                  const gchar *calobj,
-                  GError **perror),
-        do_receive_objects,
-                  (backend,
-                  cal,
-                  cancellable,
-                  calobj,
-                  perror))
+       g_free (username);
 
-static void
-caldav_send_objects (ECalBackendSync *backend,
-                     EDataCal *cal,
-                     GCancellable *cancellable,
-                     const gchar *calobj,
-                     GSList **users,
-                     gchar **modified_calobj,
-                     GError **perror)
-{
-       *users = NULL;
-       *modified_calobj = g_strdup (calobj);
+       return res;
 }
 
 static gboolean
-caldav_server_download_uid (ECalBackendCalDAV *cbdav,
-                           const gchar *uid,
-                           GCancellable *cancellable,
-                           GError **error)
-{
-       CalDAVObject obj;
-       GError *local_error = NULL;
-
-       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
-       g_return_val_if_fail (uid != NULL, FALSE);
-
-       obj.href = g_strdup (uid);
-       obj.etag = NULL;
-       obj.status = 0;
-       obj.cdata = NULL;
-
-       if (!caldav_server_get_object (cbdav, &obj, cancellable, &local_error)) {
-               if (g_error_matches (local_error, E_DATA_CAL_ERROR, ObjectNotFound)) {
-                       gchar *file;
-
-                       /* OK, the event was properly created, but cannot be found on the place
-                        * where it was PUT - why didn't server tell us where it saved it? */
-                       g_clear_error (&local_error);
-
-                       /* try whether it's saved as its UID.ics file */
-                       file = caldav_gen_file_from_uid (cbdav, uid);
-                       if (file) {
-                               g_free (obj.href);
-                               obj.href = file;
-
-                               if (!caldav_server_get_object (cbdav, &obj, cancellable, &local_error)) {
-                               }
-                       }
-               }
-       }
-
-       if (!local_error) {
-               icalcomponent *use_comp = NULL;
-
-               if (obj.cdata) {
-                       /* maybe server also modified component, thus rather store the server's */
-                       use_comp = icalparser_parse_string (obj.cdata);
-                       put_comp_to_cache (cbdav, use_comp, obj.href, obj.etag);
-               }
-
-               if (use_comp)
-                       icalcomponent_free (use_comp);
-               else
-                       local_error = EDC_ERROR (ObjectNotFound);
-       }
-
-       if (local_error) {
-               g_propagate_error (error, local_error);
-
-               return FALSE;
-       }
-
-       g_free (obj.href);
-       g_free (obj.etag);
-       g_free (obj.cdata);
-
-       return TRUE;
-}
-
-static void
-caldav_get_object (ECalBackendSync *backend,
-                   EDataCal *cal,
-                   GCancellable *cancellable,
-                   const gchar *uid,
-                   const gchar *rid,
-                   gchar **object,
-                   GError **perror)
-{
-       ECalBackendCalDAV        *cbdav;
-       icalcomponent            *icalcomp;
-
-       cbdav = E_CAL_BACKEND_CALDAV (backend);
-
-       *object = NULL;
-       icalcomp = get_comp_from_cache (cbdav, uid, rid, NULL, NULL);
-
-       if (!icalcomp && e_backend_get_online (E_BACKEND (backend))) {
-               /* try to fetch from the server, maybe the event was received only recently */
-               if (caldav_server_download_uid (cbdav, uid, cancellable, NULL)) {
-                       icalcomp = get_comp_from_cache (cbdav, uid, rid, NULL, NULL);
-               }
-       }
-
-       if (!icalcomp) {
-               g_propagate_error (perror, EDC_ERROR (ObjectNotFound));
-               return;
-       }
-
-       *object = icalcomponent_as_ical_string_r (icalcomp);
-       icalcomponent_free (icalcomp);
-}
-
-static void
-caldav_add_timezone (ECalBackendSync *backend,
-                     EDataCal *cal,
-                     GCancellable *cancellable,
-                     const gchar *tzobj,
-                     GError **error)
+ecb_caldav_get_free_busy_from_schedule_outbox_sync (ECalBackendCalDAV *cbdav,
+                                                   const GSList *users,
+                                                   time_t start,
+                                                   time_t end,
+                                                   GSList **out_freebusy,
+                                                   GCancellable *cancellable,
+                                                   GError **error)
 {
-       ETimezoneCache *timezone_cache;
-       icalcomponent *tz_comp;
-
-       timezone_cache = E_TIMEZONE_CACHE (backend);
-
-       tz_comp = icalparser_parse_string (tzobj);
-       if (!tz_comp) {
-               g_propagate_error (error, EDC_ERROR (InvalidObject));
-               return;
-       }
-
-       if (icalcomponent_isa (tz_comp) == ICAL_VTIMEZONE_COMPONENT) {
-               icaltimezone *zone;
-
-               zone = icaltimezone_new ();
-               icaltimezone_set_component (zone, tz_comp);
-
-               e_timezone_cache_add_timezone (timezone_cache, zone);
-
-               icaltimezone_free (zone, TRUE);
-       } else {
-               icalcomponent_free (tz_comp);
-       }
-}
-
-static void
-caldav_get_object_list (ECalBackendSync *backend,
-                        EDataCal *cal,
-                        GCancellable *cancellable,
-                        const gchar *sexp_string,
-                        GSList **objects,
-                        GError **perror)
-{
-       ECalBackendCalDAV        *cbdav;
-       ECalBackendSExp  *sexp;
-       ETimezoneCache *cache;
-       gboolean                  do_search;
-       GSList                   *list, *iter;
-       time_t occur_start = -1, occur_end = -1;
-       gboolean prunning_by_time;
-
-       cbdav = E_CAL_BACKEND_CALDAV (backend);
-
-       sexp = e_cal_backend_sexp_new (sexp_string);
-
-       if (sexp == NULL) {
-               g_propagate_error (perror, EDC_ERROR (InvalidQuery));
-               return;
-       }
-
-       if (g_str_equal (sexp_string, "#t")) {
-               do_search = FALSE;
-       } else {
-               do_search = TRUE;
-       }
-
-       *objects = NULL;
-
-       prunning_by_time = e_cal_backend_sexp_evaluate_occur_times (sexp, &occur_start, &occur_end);
-
-       cache = E_TIMEZONE_CACHE (backend);
-
-       list = prunning_by_time ?
-               e_cal_backend_store_get_components_occuring_in_range (cbdav->priv->store, occur_start, 
occur_end)
-               : e_cal_backend_store_get_components (cbdav->priv->store);
-
-       for (iter = list; iter; iter = g_slist_next (iter)) {
-               ECalComponent *comp = E_CAL_COMPONENT (iter->data);
-
-               if (!do_search ||
-                   e_cal_backend_sexp_match_comp (sexp, comp, cache)) {
-                       *objects = g_slist_prepend (*objects, e_cal_component_get_as_string (comp));
-               }
-
-               g_object_unref (comp);
-       }
-
-       g_object_unref (sexp);
-       g_slist_free (list);
-}
-
-static void
-caldav_start_view (ECalBackend *backend,
-                   EDataCalView *query)
-{
-       ECalBackendCalDAV        *cbdav;
-       ECalBackendSExp  *sexp;
-       ETimezoneCache *cache;
-       gboolean                  do_search;
-       GSList                   *list, *iter;
-       const gchar               *sexp_string;
-       time_t occur_start = -1, occur_end = -1;
-       gboolean prunning_by_time;
-       cbdav = E_CAL_BACKEND_CALDAV (backend);
-
-       sexp = e_data_cal_view_get_sexp (query);
-       sexp_string = e_cal_backend_sexp_text (sexp);
-
-       if (g_str_equal (sexp_string, "#t")) {
-               do_search = FALSE;
-       } else {
-               do_search = TRUE;
-       }
-       prunning_by_time = e_cal_backend_sexp_evaluate_occur_times (
-               sexp,
-               &occur_start,
-               &occur_end);
-
-       cache = E_TIMEZONE_CACHE (backend);
-
-       list = prunning_by_time ?
-               e_cal_backend_store_get_components_occuring_in_range (cbdav->priv->store, occur_start, 
occur_end)
-               : e_cal_backend_store_get_components (cbdav->priv->store);
-
-       for (iter = list; iter; iter = g_slist_next (iter)) {
-               ECalComponent *comp = E_CAL_COMPONENT (iter->data);
-
-               if (!do_search ||
-                   e_cal_backend_sexp_match_comp (sexp, comp, cache)) {
-                       e_data_cal_view_notify_components_added_1 (query, comp);
-               }
-
-               g_object_unref (comp);
-       }
-
-       g_slist_free (list);
-
-       e_data_cal_view_notify_complete (query, NULL /* Success */);
-}
-
-static void
-caldav_get_free_busy (ECalBackendSync *backend,
-                      EDataCal *cal,
-                      GCancellable *cancellable,
-                      const GSList *users,
-                      time_t start,
-                      time_t end,
-                      GSList **freebusy,
-                      GError **error)
-{
-       ECalBackendCalDAV *cbdav;
        icalcomponent *icalcomp;
        ECalComponent *comp;
        ECalComponentDateTime dt;
-       ECalComponentOrganizer organizer = {NULL};
+       ECalComponentOrganizer organizer = { NULL };
        ESourceAuthentication *auth_extension;
        ESource *source;
        struct icaltimetype dtvalue;
        icaltimezone *utc;
        gchar *str;
-       const GSList *u;
+       GSList *link;
        GSList *attendees = NULL, *to_free = NULL;
        const gchar *extension_name;
        gchar *usermail;
-       GError *err = NULL;
+       GByteArray *response = NULL;
+       GError *local_error = NULL;
 
-       cbdav = E_CAL_BACKEND_CALDAV (backend);
+       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
 
-       if (!cbdav->priv->calendar_schedule) {
-               g_propagate_error (error, EDC_ERROR_EX (OtherError, _("Calendar doesnā€™t support Free/Busy")));
-               return;
-       }
+       if (!cbdav->priv->calendar_schedule)
+               return FALSE;
 
        if (!cbdav->priv->schedule_outbox_url) {
-               caldav_receive_schedule_outbox_url (cbdav, cancellable, error);
-               if (!cbdav->priv->schedule_outbox_url) {
+               if (!ecb_caldav_receive_schedule_outbox_url_sync (cbdav, cancellable, error) ||
+                   !cbdav->priv->schedule_outbox_url) {
                        cbdav->priv->calendar_schedule = FALSE;
-                       if (error && !*error)
-                               g_propagate_error (error, EDC_ERROR_EX (OtherError, _("Schedule outbox url 
not found")));
-                       return;
+                       return FALSE;
                }
        }
 
@@ -5299,17 +1376,17 @@ caldav_get_free_busy (ECalBackendSync *backend,
        dtvalue = icaltime_from_timet_with_zone (end, FALSE, utc);
        e_cal_component_set_dtend (comp, &dt);
 
-       usermail = get_usermail (E_CAL_BACKEND (backend));
+       usermail = ecb_caldav_get_usermail (cbdav);
        if (usermail != NULL && *usermail == '\0') {
                g_free (usermail);
                usermail = NULL;
        }
 
-       source = e_backend_get_source (E_BACKEND (backend));
+       source = e_backend_get_source (E_BACKEND (cbdav));
        extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
        auth_extension = e_source_get_extension (source, extension_name);
 
-       if (usermail == NULL)
+       if (!usermail)
                usermail = e_source_authentication_dup_user (auth_extension);
 
        organizer.value = g_strconcat ("mailto:";, usermail, NULL);
@@ -5318,9 +1395,9 @@ caldav_get_free_busy (ECalBackendSync *backend,
 
        g_free (usermail);
 
-       for (u = users; u; u = u->next) {
+       for (link = (GSList *) users; link; link = g_slist_next (link)) {
                ECalComponentAttendee *ca;
-               gchar *temp = g_strconcat ("mailto:";, (const gchar *) u->data, NULL);
+               gchar *temp = g_strconcat ("mailto:";, (const gchar *) link->data, NULL);
 
                ca = g_new0 (ECalComponentAttendee, 1);
 
@@ -5335,11 +1412,8 @@ caldav_get_free_busy (ECalBackendSync *backend,
 
        e_cal_component_set_attendee_list (comp, attendees);
 
-       g_slist_foreach (attendees, (GFunc) g_free, NULL);
-       g_slist_free (attendees);
-
-       g_slist_foreach (to_free, (GFunc) g_free, NULL);
-       g_slist_free (to_free);
+       g_slist_free_full (attendees, g_free);
+       g_slist_free_full (to_free, g_free);
 
        e_cal_component_abort_sequence (comp);
 
@@ -5353,387 +1427,361 @@ caldav_get_free_busy (ECalBackendSync *backend,
        icalcomponent_free (icalcomp);
        g_object_unref (comp);
 
-       caldav_post_freebusy (cbdav, cbdav->priv->schedule_outbox_url, &str, cancellable, &err);
-
-       if (!err) {
+       if (e_webdav_session_post_sync (cbdav->priv->webdav, cbdav->priv->schedule_outbox_url, str, -1, NULL, 
&response, cancellable, &local_error) &&
+           response) {
                /* parse returned xml */
                xmlDocPtr doc;
+               xmlXPathContextPtr xpath_ctx = NULL;
+
+               doc = e_xml_parse_data (response->data, response->len);
+
+               if (!doc) {
+                       g_set_error_literal (&local_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+                               _("Failed to parse response data"));
+               } else {
+                       xpath_ctx = e_xml_new_xpath_context_with_namespaces (doc,
+                               "D", E_WEBDAV_NS_DAV,
+                               "C", E_WEBDAV_NS_CALDAV,
+                               NULL);
+               }
 
-               doc = xmlReadMemory (str, strlen (str), "response.xml", NULL, 0);
-               if (doc != NULL) {
-                       xmlXPathContextPtr xpctx;
-                       xmlXPathObjectPtr result;
+               if (xpath_ctx) {
+                       xmlXPathObjectPtr xpath_obj_response;
 
-                       xpctx = xmlXPathNewContext (doc);
-                       xmlXPathRegisterNs (xpctx, (xmlChar *) "D", (xmlChar *) "DAV:");
-                       xmlXPathRegisterNs (xpctx, (xmlChar *) "C", (xmlChar *) 
"urn:ietf:params:xml:ns:caldav");
+                       xpath_obj_response = e_xml_xpath_eval (xpath_ctx, "/C:schedule-response/C:response");
 
-                       result = xpath_eval (xpctx, "/C:schedule-response/C:response");
+                       if (xpath_obj_response) {
+                               gint response_index, response_length;
 
-                       if (result == NULL || result->type != XPATH_NODESET) {
-                               err = EDC_ERROR_EX (OtherError, _("Unexpected result in schedule-response"));
-                       } else {
-                               gint i, n;
+                               response_length = xmlXPathNodeSetGetLength (xpath_obj_response->nodesetval);
 
-                               n = xmlXPathNodeSetGetLength (result->nodesetval);
-                               for (i = 0; i < n; i++) {
+                               for (response_index = 0; response_index < response_length; response_index++) {
                                        gchar *tmp;
 
-                                       tmp = xp_object_get_string (xpath_eval (xpctx, 
"string(/C:schedule-response/C:response[%d]/C:calendar-data)", i + 1));
+                                       tmp = e_xml_xpath_eval_as_string 
(xpath_ctx,"/C:schedule-response/C:response[%d]/C:calendar-data", response_index + 1);
                                        if (tmp && *tmp) {
-                                               GSList *objects = NULL, *o;
+                                               GSList *objects = NULL;
 
                                                icalcomp = icalparser_parse_string (tmp);
                                                if (icalcomp)
-                                                       extract_objects (icalcomp, ICAL_VFREEBUSY_COMPONENT, 
&objects, &err);
-                                               if (icalcomp && !err) {
-                                                       for (o = objects; o; o = o->next) {
-                                                               gchar *obj_str = 
icalcomponent_as_ical_string_r (o->data);
+                                                       ecb_caldav_extract_objects (icalcomp, 
ICAL_VFREEBUSY_COMPONENT, &objects, &local_error);
+                                               if (icalcomp && !local_error) {
+                                                       for (link = objects; link; link = g_slist_next 
(link)) {
+                                                               gchar *obj_str = 
icalcomponent_as_ical_string_r (link->data);
 
                                                                if (obj_str && *obj_str)
-                                                                       *freebusy = g_slist_append 
(*freebusy, obj_str);
+                                                                       *out_freebusy = g_slist_prepend 
(*out_freebusy, obj_str);
                                                                else
                                                                        g_free (obj_str);
                                                        }
                                                }
 
-                                               g_slist_foreach (objects, (GFunc) icalcomponent_free, NULL);
-                                               g_slist_free (objects);
+                                               g_slist_free_full (objects, (GDestroyNotify) 
icalcomponent_free);
 
                                                if (icalcomp)
                                                        icalcomponent_free (icalcomp);
-                                               if (err)
-                                                       g_error_free (err);
-                                               err = NULL;
+                                               g_clear_error (&local_error);
                                        }
 
                                        g_free (tmp);
                                }
+
+                               xmlXPathFreeObject (xpath_obj_response);
                        }
 
-                       if (result != NULL)
-                               xmlXPathFreeObject (result);
-                       xmlXPathFreeContext (xpctx);
-                       xmlFreeDoc (doc);
+                       xmlXPathFreeContext (xpath_ctx);
                }
+
+               if (doc)
+                       xmlFreeDoc (doc);
        }
 
+       if (response)
+               g_byte_array_free (response, TRUE);
        g_free (str);
 
-       if (err)
-               g_propagate_error (error, err);
-}
+       if (local_error)
+               g_propagate_error (error, local_error);
 
-static void
-caldav_notify_online_cb (ECalBackend *backend,
-                         GParamSpec *pspec)
-{
-       ECalBackendCalDAV        *cbdav;
-       gboolean online;
+       return local_error != NULL;
+}
 
-       cbdav = E_CAL_BACKEND_CALDAV (backend);
+static gboolean
+ecb_caldav_get_free_busy_from_principal_sync (ECalBackendCalDAV *cbdav,
+                                             const gchar *usermail,
+                                             time_t start,
+                                             time_t end,
+                                             GSList **out_freebusy,
+                                             GCancellable *cancellable,
+                                             GError **error)
+{
+       EWebDAVResource *resource;
+       GSList *principals = NULL;
+       EXmlDocument *xml;
+       gchar *href;
+       gchar *content_type = NULL;
+       GByteArray *content = NULL;
+       gboolean success;
 
-       /*g_mutex_lock (&cbdav->priv->busy_lock);*/
+       g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
+       g_return_val_if_fail (usermail != NULL, FALSE);
+       g_return_val_if_fail (out_freebusy != NULL, FALSE);
 
-       online = e_backend_get_online (E_BACKEND (backend));
+       if (!e_webdav_session_principal_property_search_sync (cbdav->priv->webdav, NULL, TRUE,
+               E_WEBDAV_NS_CALDAV, "calendar-user-address-set", usermail, &principals, cancellable, error)) {
+               return FALSE;
+       }
 
-       if (!cbdav->priv->loaded) {
-               /*g_mutex_unlock (&cbdav->priv->busy_lock);*/
-               return;
+       if (!principals || principals->next || !principals->data) {
+               g_slist_free_full (principals, e_webdav_resource_free);
+               return FALSE;
        }
 
-       if (online) {
-               /* Wake up the slave thread */
-               update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
-               g_cond_signal (&cbdav->priv->cond);
-       } else {
-               soup_session_abort (cbdav->priv->session);
-               update_slave_cmd (cbdav->priv, SLAVE_SHOULD_SLEEP);
+       resource = principals->data;
+       href = g_strdup (resource->href);
+
+       g_slist_free_full (principals, e_webdav_resource_free);
+
+       if (!href || !*href) {
+               g_free (href);
+               return FALSE;
        }
 
-       /*g_mutex_unlock (&cbdav->priv->busy_lock);*/
-}
+       xml = e_xml_document_new (E_WEBDAV_NS_CALDAV, "free-busy-query");
 
-static gpointer
-caldav_source_changed_thread (gpointer data)
-{
-       ECalBackendCalDAV *cbdav = data;
-       SlaveCommand old_slave_cmd;
-       gboolean old_slave_busy;
+       e_xml_document_start_element (xml, NULL, "time-range");
+       e_xml_document_add_attribute_time (xml, NULL, "start", start);
+       e_xml_document_add_attribute_time (xml, NULL, "end", end);
+       e_xml_document_end_element (xml); /* time-range */
 
-       g_return_val_if_fail (cbdav != NULL, NULL);
+       success = e_webdav_session_report_sync (cbdav->priv->webdav, NULL, E_WEBDAV_DEPTH_INFINITY, xml, 
NULL, NULL, &content_type, &content, cancellable, error);
 
-       old_slave_cmd = cbdav->priv->slave_cmd;
-       old_slave_busy = cbdav->priv->slave_busy;
-       if (old_slave_busy)
-               update_slave_cmd (cbdav->priv, SLAVE_SHOULD_SLEEP);
+       g_object_unref (xml);
 
-       g_mutex_lock (&cbdav->priv->busy_lock);
+       if (success && content_type && content && content->data && content->len &&
+           g_ascii_strcasecmp (content_type, "text/calendar") == 0) {
+               icalcomponent *vcalendar;
 
-       /* guard the call with busy_lock, thus the two threads (this 'source changed'
-        * thread and the 'backend open' thread) will not clash on internal data
-        * when they are called in once */
-       initialize_backend (cbdav, NULL);
+               vcalendar = icalcomponent_new_from_string ((const gchar *) content->data);
+               if (vcalendar) {
+                       GSList *comps = NULL, *link;
 
-       /* always wakeup thread, even when it was sleeping */
-       g_cond_signal (&cbdav->priv->cond);
+                       ecb_caldav_extract_objects (vcalendar, ICAL_VFREEBUSY_COMPONENT, &comps, NULL);
 
-       if (old_slave_busy)
-               update_slave_cmd (cbdav->priv, old_slave_cmd);
+                       for (link = comps; link; link = g_slist_next (link)) {
+                               icalcomponent *subcomp = link->data;
+                               gchar *obj_str;
 
-       g_mutex_unlock (&cbdav->priv->busy_lock);
+                               if (!icalcomponent_get_first_property (subcomp, ICAL_ATTENDEE_PROPERTY)) {
+                                       icalproperty *prop;
+                                       gchar *mailto;
 
-       cbdav->priv->updating_source = FALSE;
+                                       mailto = g_strconcat ("mailto:";, usermail, NULL);
+                                       prop = icalproperty_new_attendee (mailto);
+                                       g_free (mailto);
 
-       g_object_unref (cbdav);
+                                       icalcomponent_add_property (subcomp, prop);
+                               }
 
-       return NULL;
-}
+                               obj_str = icalcomponent_as_ical_string_r (subcomp);
 
-static void
-caldav_source_changed_cb (ESource *source,
-                          ECalBackendCalDAV *cbdav)
-{
-       GThread *thread;
+                               if (obj_str && *obj_str)
+                                       *out_freebusy = g_slist_prepend (*out_freebusy, obj_str);
+                               else
+                                       g_free (obj_str);
+                       }
 
-       g_return_if_fail (source != NULL);
-       g_return_if_fail (cbdav != NULL);
+                       success = comps != NULL;
 
-       if (cbdav->priv->updating_source ||
-           !cbdav->priv->loaded ||
-           !e_cal_backend_is_opened (E_CAL_BACKEND (cbdav)))
-               return;
+                       g_slist_free_full (comps, (GDestroyNotify) icalcomponent_free);
+               } else {
+                       success = FALSE;
+               }
+       }
 
-       cbdav->priv->updating_source = TRUE;
+       if (content)
+               g_byte_array_free (content, TRUE);
+       g_free (content_type);
+       g_free (href);
 
-       thread = g_thread_new (NULL, caldav_source_changed_thread, g_object_ref (cbdav));
-       g_thread_unref (thread);
+       return success;
 }
 
-static ESourceAuthenticationResult
-caldav_authenticate_sync (EBackend *backend,
-                         const ENamedParameters *credentials,
-                         gchar **out_certificate_pem,
-                         GTlsCertificateFlags *out_certificate_errors,
-                         GCancellable *cancellable,
-                         GError **error)
+static void
+ecb_caldav_get_free_busy_sync (ECalBackendSync *sync_backend,
+                              EDataCal *cal,
+                              GCancellable *cancellable,
+                              const GSList *users,
+                              time_t start,
+                              time_t end,
+                              GSList **out_freebusy,
+                              GError **error)
 {
        ECalBackendCalDAV *cbdav;
-       ESourceAuthenticationResult result;
-       GError *local_error = NULL;
 
-       cbdav = E_CAL_BACKEND_CALDAV (backend);
+       g_return_if_fail (E_IS_CAL_BACKEND_CALDAV (sync_backend));
+       g_return_if_fail (out_freebusy != NULL);
 
-       g_mutex_lock (&cbdav->priv->busy_lock);
+       cbdav = E_CAL_BACKEND_CALDAV (sync_backend);
 
-       e_named_parameters_free (cbdav->priv->credentials);
-       cbdav->priv->credentials = e_named_parameters_new_clone (credentials);
-
-       open_calendar_wrapper (cbdav, cancellable, &local_error, FALSE, NULL, out_certificate_pem, 
out_certificate_errors);
-
-       if (local_error == NULL) {
-               result = E_SOURCE_AUTHENTICATION_ACCEPTED;
+       if (e_backend_get_online (E_BACKEND (cbdav)) &&
+           cbdav->priv->webdav) {
+               const GSList *link;
+               GError *local_error = NULL;
 
-               update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
-               g_cond_signal (&cbdav->priv->cond);
-       } else if (g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed) ||
-                  g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationRequired)) {
-               const gchar *username;
-               gchar *auth_user = NULL;
+               if (ecb_caldav_get_free_busy_from_schedule_outbox_sync (cbdav, users, start, end, 
out_freebusy, cancellable, &local_error))
+                       return;
 
-               username = e_named_parameters_get (cbdav->priv->credentials, E_SOURCE_CREDENTIAL_USERNAME);
+               g_clear_error (&local_error);
 
-               if (!username || !*username) {
-                       ESource *source;
-                       ESourceAuthentication *auth_extension;
+               if (g_cancellable_set_error_if_cancelled (cancellable, error))
+                       return;
 
-                       source = e_backend_get_source (backend);
-                       auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
-                       auth_user = e_source_authentication_dup_user (auth_extension);
+               *out_freebusy = NULL;
 
-                       username = auth_user;
+               for (link = users; link && !g_cancellable_is_cancelled (cancellable); link = g_slist_next 
(link)) {
+                       if (!ecb_caldav_get_free_busy_from_principal_sync (cbdav, link->data, start, end, 
out_freebusy, cancellable, &local_error))
+                               g_clear_error (&local_error);
                }
 
-               if (username && *username) {
-                       if (!cbdav->priv->using_bearer_auth &&
-                           !e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD))
-                               result = E_SOURCE_AUTHENTICATION_REQUIRED;
-                       else
-                               result = E_SOURCE_AUTHENTICATION_REJECTED;
-                       g_clear_error (&local_error);
-               } else {
-                       result = E_SOURCE_AUTHENTICATION_ERROR;
-                       g_propagate_error (error, local_error);
-               }
+               g_clear_error (&local_error);
 
-               g_free (auth_user);
-       } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
-               result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
-               g_propagate_error (error, local_error);
-       } else {
-               result = E_SOURCE_AUTHENTICATION_ERROR;
-               g_propagate_error (error, local_error);
+               if (*out_freebusy || g_cancellable_set_error_if_cancelled (cancellable, error))
+                       return;
        }
 
-       g_mutex_unlock (&cbdav->priv->busy_lock);
-
-       return result;
+       /* Chain up to parent's method. */
+       E_CAL_BACKEND_SYNC_CLASS (e_cal_backend_caldav_parent_class)->get_free_busy_sync (sync_backend, cal, 
cancellable,
+               users, start, end, out_freebusy, error);
 }
 
-/* ************************************************************************* */
-/* ***************************** GObject Foo ******************************* */
-
-static void
-e_cal_backend_caldav_dispose (GObject *object)
+static gchar *
+ecb_caldav_get_backend_property (ECalBackend *backend,
+                                const gchar *prop_name)
 {
-       ECalBackendCalDAVPrivate *priv;
-
-       priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (object);
+       g_return_val_if_fail (prop_name != NULL, NULL);
 
-       g_clear_object (&priv->store);
-       g_clear_object (&priv->session);
-       g_clear_object (&priv->using_bearer_auth);
+       if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
+               ESourceWebdav *extension;
+               ESource *source;
+               GString *caps;
+               gchar *usermail;
+               const gchar *extension_name;
 
-       /* Chain up to parent's dispose() method. */
-       G_OBJECT_CLASS (parent_class)->dispose (object);
-}
+               caps = g_string_new (
+                       CAL_STATIC_CAPABILITY_NO_THISANDPRIOR ","
+                       CAL_STATIC_CAPABILITY_REFRESH_SUPPORTED);
+               g_string_append (caps, ",");
+               g_string_append (caps, e_cal_meta_backend_get_capabilities (E_CAL_META_BACKEND (backend)));
 
-static void
-e_cal_backend_caldav_finalize (GObject *object)
-{
-       ECalBackendCalDAVPrivate *priv;
+               usermail = ecb_caldav_get_usermail (E_CAL_BACKEND_CALDAV (backend));
+               if (!usermail || !*usermail)
+                       g_string_append (caps, "," CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS);
+               g_free (usermail);
 
-       priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (object);
+               source = e_backend_get_source (E_BACKEND (backend));
 
-       g_mutex_clear (&priv->busy_lock);
-       g_cond_clear (&priv->cond);
-       g_cond_clear (&priv->slave_gone_cond);
+               extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
+               extension = e_source_get_extension (source, extension_name);
 
-       g_free (priv->uri);
-       e_named_parameters_free (priv->credentials);
-       priv->credentials = NULL;
-       g_free (priv->schedule_outbox_url);
+               if (e_source_webdav_get_calendar_auto_schedule (extension)) {
+                       g_string_append (
+                               caps,
+                               "," CAL_STATIC_CAPABILITY_CREATE_MESSAGES
+                               "," CAL_STATIC_CAPABILITY_SAVE_SCHEDULES);
+               }
 
-       if (priv->ctag_to_store) {
-               g_free (priv->ctag_to_store);
-               priv->ctag_to_store = NULL;
+               return g_string_free (caps, FALSE);
+       } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS) ||
+                  g_str_equal (prop_name, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) {
+               return ecb_caldav_get_usermail (E_CAL_BACKEND_CALDAV (backend));
        }
 
-       g_clear_error (&priv->bearer_auth_error);
-       g_mutex_clear (&priv->bearer_auth_error_lock);
-
-       /* Chain up to parent's finalize() method. */
-       G_OBJECT_CLASS (parent_class)->finalize (object);
+       /* Chain up to parent's method. */
+       return E_CAL_BACKEND_CLASS (e_cal_backend_caldav_parent_class)->get_backend_property (backend, 
prop_name);
 }
 
-static gboolean
-caldav_backend_initable_init (GInitable *initable,
-                              GCancellable *cancellable,
-                              GError **error)
+static gchar *
+ecb_caldav_dup_component_revision_cb (ECalCache *cal_cache,
+                                     icalcomponent *icalcomp)
 {
-       ECalBackendCalDAVPrivate *priv;
-
-       priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (initable);
-
-       g_mutex_init (&priv->bearer_auth_error_lock);
+       g_return_val_if_fail (icalcomp != NULL, NULL);
 
-       return TRUE;
+       return e_cal_util_dup_x_property (icalcomp, E_CALDAV_X_ETAG);
 }
 
 static void
-e_cal_backend_caldav_init (ECalBackendCalDAV *cbdav)
+e_cal_backend_caldav_constructed (GObject *object)
 {
-       cbdav->priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
-       cbdav->priv->session = soup_session_sync_new ();
-       g_object_set (
-               cbdav->priv->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);
+       ECalBackendCalDAV *cbdav = E_CAL_BACKEND_CALDAV (object);
+       ECalCache *cal_cache;
 
-       e_binding_bind_property (
-               cbdav, "proxy-resolver",
-               cbdav->priv->session, "proxy-resolver",
-               G_BINDING_SYNC_CREATE);
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_cal_backend_caldav_parent_class)->constructed (object);
 
-       if (G_UNLIKELY (caldav_debug_show (DEBUG_MESSAGE)))
-               caldav_debug_setup (cbdav->priv->session);
+       cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbdav));
 
-       cbdav->priv->loaded = FALSE;
-       cbdav->priv->opened = FALSE;
+       g_signal_connect (cal_cache, "dup-component-revision",
+               G_CALLBACK (ecb_caldav_dup_component_revision_cb), NULL);
 
-       /* Thinks the 'getctag' extension is available the first time, but unset it when realizes it isn't. */
-       cbdav->priv->ctag_supported = TRUE;
-       cbdav->priv->ctag_to_store = NULL;
+       g_clear_object (&cal_cache);
+}
+
+static void
+e_cal_backend_caldav_dispose (GObject *object)
+{
+       ECalBackendCalDAV *cbdav = E_CAL_BACKEND_CALDAV (object);
 
-       cbdav->priv->schedule_outbox_url = NULL;
+       g_clear_object (&cbdav->priv->webdav);
 
-       cbdav->priv->is_google = FALSE;
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_cal_backend_caldav_parent_class)->dispose (object);
+}
 
-       g_mutex_init (&cbdav->priv->busy_lock);
-       g_cond_init (&cbdav->priv->cond);
-       g_cond_init (&cbdav->priv->slave_gone_cond);
+static void
+e_cal_backend_caldav_finalize (GObject *object)
+{
+       ECalBackendCalDAV *cbdav = E_CAL_BACKEND_CALDAV (object);
 
-       /* Slave control ... */
-       cbdav->priv->slave_cmd = SLAVE_SHOULD_SLEEP;
-       cbdav->priv->slave_busy = FALSE;
+       g_clear_pointer (&cbdav->priv->schedule_outbox_url, g_free);
 
-       g_signal_connect (
-               cbdav->priv->session, "authenticate",
-               G_CALLBACK (soup_authenticate), cbdav);
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_cal_backend_caldav_parent_class)->finalize (object);
+}
 
-       g_signal_connect (
-               cbdav, "notify::online",
-               G_CALLBACK (caldav_notify_online_cb), NULL);
+static void
+e_cal_backend_caldav_init (ECalBackendCalDAV *cbdav)
+{
+       cbdav->priv = G_TYPE_INSTANCE_GET_PRIVATE (cbdav, E_TYPE_CAL_BACKEND_CALDAV, 
ECalBackendCalDAVPrivate);
 }
 
 static void
-e_cal_backend_caldav_class_init (ECalBackendCalDAVClass *class)
+e_cal_backend_caldav_class_init (ECalBackendCalDAVClass *klass)
 {
        GObjectClass *object_class;
-       EBackendClass *backend_class;
        ECalBackendClass *cal_backend_class;
-       ECalBackendSyncClass *sync_class;
+       ECalBackendSyncClass *cal_backend_sync_class;
+       ECalMetaBackendClass *cal_meta_backend_class;
+
+       g_type_class_add_private (klass, sizeof (ECalBackendCalDAVPrivate));
 
-       object_class = G_OBJECT_CLASS (class);
-       backend_class = E_BACKEND_CLASS (class);
-       cal_backend_class = E_CAL_BACKEND_CLASS (class);
-       sync_class = E_CAL_BACKEND_SYNC_CLASS (class);
+       cal_meta_backend_class = E_CAL_META_BACKEND_CLASS (klass);
+       cal_meta_backend_class->connect_sync = ecb_caldav_connect_sync;
+       cal_meta_backend_class->disconnect_sync = ecb_caldav_disconnect_sync;
+       cal_meta_backend_class->get_changes_sync = ecb_caldav_get_changes_sync;
+       cal_meta_backend_class->list_existing_sync = ecb_caldav_list_existing_sync;
+       cal_meta_backend_class->load_component_sync = ecb_caldav_load_component_sync;
+       cal_meta_backend_class->save_component_sync = ecb_caldav_save_component_sync;
+       cal_meta_backend_class->remove_component_sync = ecb_caldav_remove_component_sync;
 
-       caldav_debug_init ();
+       cal_backend_sync_class = E_CAL_BACKEND_SYNC_CLASS (klass);
+       cal_backend_sync_class->get_free_busy_sync = ecb_caldav_get_free_busy_sync;
 
-       parent_class = (ECalBackendSyncClass *) g_type_class_peek_parent (class);
-       g_type_class_add_private (class, sizeof (ECalBackendCalDAVPrivate));
+       cal_backend_class = E_CAL_BACKEND_CLASS (klass);
+       cal_backend_class->get_backend_property = ecb_caldav_get_backend_property;
 
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->constructed = e_cal_backend_caldav_constructed;
        object_class->dispose = e_cal_backend_caldav_dispose;
        object_class->finalize = e_cal_backend_caldav_finalize;
-
-       backend_class->authenticate_sync = caldav_authenticate_sync;
-
-       cal_backend_class->get_backend_property = caldav_get_backend_property;
-       cal_backend_class->shutdown = caldav_shutdown;
-       cal_backend_class->start_view = caldav_start_view;
-
-       sync_class->open_sync = caldav_do_open;
-       sync_class->refresh_sync = caldav_refresh;
-
-       sync_class->create_objects_sync = caldav_create_objects;
-       sync_class->modify_objects_sync = caldav_modify_objects;
-       sync_class->remove_objects_sync = caldav_remove_objects;
-
-       sync_class->receive_objects_sync = caldav_receive_objects;
-       sync_class->send_objects_sync = caldav_send_objects;
-       sync_class->get_object_sync = caldav_get_object;
-       sync_class->get_object_list_sync = caldav_get_object_list;
-       sync_class->add_timezone_sync = caldav_add_timezone;
-       sync_class->get_free_busy_sync = caldav_get_free_busy;
 }
-
-static void
-e_caldav_backend_initable_init (GInitableIface *interface)
-{
-       interface->init = caldav_backend_initable_init;
-}
-
diff --git a/src/calendar/backends/caldav/e-cal-backend-caldav.h 
b/src/calendar/backends/caldav/e-cal-backend-caldav.h
index 3ea3273..3a8b639 100644
--- a/src/calendar/backends/caldav/e-cal-backend-caldav.h
+++ b/src/calendar/backends/caldav/e-cal-backend-caldav.h
@@ -47,12 +47,12 @@ typedef struct _ECalBackendCalDAVClass ECalBackendCalDAVClass;
 typedef struct _ECalBackendCalDAVPrivate ECalBackendCalDAVPrivate;
 
 struct _ECalBackendCalDAV {
-       ECalBackendSync parent;
+       ECalMetaBackend parent;
        ECalBackendCalDAVPrivate *priv;
 };
 
 struct _ECalBackendCalDAVClass {
-       ECalBackendSyncClass parent_class;
+       ECalMetaBackendClass parent_class;
 };
 
 GType          e_cal_backend_caldav_get_type   (void);
diff --git a/src/calendar/backends/gtasks/e-cal-backend-gtasks.c 
b/src/calendar/backends/gtasks/e-cal-backend-gtasks.c
index e74f3ba..ddbc6cf 100644
--- a/src/calendar/backends/gtasks/e-cal-backend-gtasks.c
+++ b/src/calendar/backends/gtasks/e-cal-backend-gtasks.c
@@ -537,6 +537,7 @@ ecb_gtasks_check_tasklist_changed_sync (ECalBackendGTasks *cbgtasks,
 static gboolean
 ecb_gtasks_get_changes_sync (ECalMetaBackend *meta_backend,
                             const gchar *last_sync_tag,
+                            gboolean is_repeat,
                             gchar **out_new_sync_tag,
                             gboolean *out_repeat,
                             GSList **out_created_objects, /* ECalMetaBackendInfo * */
@@ -644,7 +645,7 @@ ecb_gtasks_get_changes_sync (ECalMetaBackend *meta_backend,
                                                                e_cal_component_set_created (new_comp, 
cached_tt);
 
                                                        *out_modified_objects = g_slist_prepend 
(*out_modified_objects,
-                                                               e_cal_meta_backend_info_new (uid, NULL, 
revision, object));
+                                                               e_cal_meta_backend_info_new (uid, revision, 
object, NULL));
                                                }
 
                                                if (cached_tt)
@@ -653,7 +654,7 @@ ecb_gtasks_get_changes_sync (ECalMetaBackend *meta_backend,
                                                        e_cal_component_free_icaltimetype (new_tt);
                                        } else {
                                                *out_created_objects = g_slist_prepend (*out_created_objects,
-                                                       e_cal_meta_backend_info_new (uid, NULL, revision, 
object));
+                                                       e_cal_meta_backend_info_new (uid, revision, object, 
NULL));
                                        }
 
                                        g_free (revision);
@@ -705,6 +706,7 @@ ecb_gtasks_get_changes_sync (ECalMetaBackend *meta_backend,
 static gboolean
 ecb_gtasks_load_component_sync (ECalMetaBackend *meta_backend,
                                const gchar *uid,
+                               const gchar *extra,
                                icalcomponent **out_instances,
                                gchar **out_extra,
                                GCancellable *cancellable,
@@ -751,6 +753,7 @@ ecb_gtasks_save_component_sync (ECalMetaBackend *meta_backend,
                                const GSList *instances, /* ECalComponent * */
                                const gchar *extra,
                                gchar **out_new_uid,
+                               gchar **out_new_extra,
                                GCancellable *cancellable,
                                GError **error)
 {
@@ -846,39 +849,20 @@ ecb_gtasks_remove_component_sync (ECalMetaBackend *meta_backend,
                                  GError **error)
 {
        ECalBackendGTasks *cbgtasks;
-       ECalCache *cal_cache;
        GDataTasksTask *task;
        ECalComponent *cached_comp = NULL;
        GError *local_error = NULL;
 
        g_return_val_if_fail (E_IS_CAL_BACKEND_GTASKS (meta_backend), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+       g_return_val_if_fail (object != NULL, FALSE);
 
-       cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
-       g_return_val_if_fail (cal_cache != NULL, FALSE);
-
-       if (object) {
-               cached_comp = e_cal_component_new_from_string (object);
-               if (!cached_comp) {
-                       g_propagate_error (error, EDC_ERROR (InvalidObject));
-                       g_object_unref (cal_cache);
-
-                       return FALSE;
-               }
-       } else if (!e_cal_cache_get_component (cal_cache, uid, NULL, &cached_comp, cancellable, 
&local_error)) {
-               if (g_error_matches (local_error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND)) {
-                       g_clear_error (&local_error);
-                       g_propagate_error (error, EDC_ERROR (ObjectNotFound));
-               } else if (local_error) {
-                       g_propagate_error (error, local_error);
-               }
-
-               g_object_unref (cal_cache);
-
+       cached_comp = e_cal_component_new_from_string (object);
+       if (!cached_comp) {
+               g_propagate_error (error, EDC_ERROR (InvalidObject));
                return FALSE;
        }
 
-       g_object_unref (cal_cache);
-
        cbgtasks = E_CAL_BACKEND_GTASKS (meta_backend);
 
        if (!cached_comp) {
diff --git a/src/calendar/backends/http/e-cal-backend-http.c b/src/calendar/backends/http/e-cal-backend-http.c
index 137edfe..51a4246 100644
--- a/src/calendar/backends/http/e-cal-backend-http.c
+++ b/src/calendar/backends/http/e-cal-backend-http.c
@@ -26,6 +26,7 @@
 
 #include <libsoup/soup.h>
 #include <libedata-cal/libedata-cal.h>
+
 #include "e-cal-backend-http.h"
 
 #define EDC_ERROR(_code) e_data_cal_create_error (_code, NULL)
@@ -167,15 +168,17 @@ ecb_http_connect_sync (ECalMetaBackend *meta_backend,
                                        "%s", message->reason_phrase);
                        } else if (status_code == SOUP_STATUS_FORBIDDEN && credentials_empty) {
                                *out_auth_result = E_SOURCE_AUTHENTICATION_REQUIRED;
-                       } else if (status_code == SOUP_STATUS_UNAUTHORIZED ||
-                                  status_code == SOUP_STATUS_FORBIDDEN) {
-                               *out_auth_result = E_SOURCE_AUTHENTICATION_REJECTED;
+                       } else if (status_code == 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 (error, SOUP_HTTP_ERROR, status_code,
-                                       "%s", message ? message->reason_phrase : soup_status_get_phrase 
(status_code));
+                               g_set_error_literal (error, SOUP_HTTP_ERROR, status_code,
+                                       message ? message->reason_phrase : soup_status_get_phrase 
(status_code));
                        }
 
                        if (status_code == SOUP_STATUS_SSL_FAILED) {
@@ -268,34 +271,10 @@ ecb_http_read_stream_sync (GInputStream *input_stream,
        return g_string_free (icalstr, !success);
 }
 
-static void
-ecb_http_gather_uids_cb (gpointer uid,
-                        gpointer icalcomp,
-                        gpointer user_data)
-{
-       GHashTable *hash_table = user_data;
-
-       g_return_if_fail (hash_table != NULL);
-
-       if (uid)
-               g_hash_table_insert (hash_table, g_strdup (uid), NULL);
-}
-
-static gboolean
-ecb_http_remove_unchanged_cb (gpointer uid,
-                             gpointer icalcomp,
-                             gpointer user_data)
-{
-       GHashTable *unchanged = user_data;
-
-       g_return_val_if_fail (unchanged != NULL, FALSE);
-
-       return !uid || g_hash_table_contains (unchanged, uid);
-}
-
 static gboolean
 ecb_http_get_changes_sync (ECalMetaBackend *meta_backend,
                           const gchar *last_sync_tag,
+                          gboolean is_repeat,
                           gchar **out_new_sync_tag,
                           gboolean *out_repeat,
                           GSList **out_created_objects,
@@ -424,40 +403,14 @@ ecb_http_get_changes_sync (ECalMetaBackend *meta_backend,
                        }
                }
 
+               g_warn_if_fail (cbhttp->priv->components == NULL);
                cbhttp->priv->components = components;
 
                icalcomponent_free (vcalendar);
 
                success = E_CAL_META_BACKEND_CLASS (e_cal_backend_http_parent_class)->get_changes_sync 
(meta_backend,
-                       last_sync_tag, out_new_sync_tag, out_repeat, out_created_objects,
+                       last_sync_tag, is_repeat, out_new_sync_tag, out_repeat, out_created_objects,
                        out_modified_objects, out_removed_objects, cancellable, error);
-
-               if (success) {
-                       GHashTable *unchanged;
-                       GSList *link;
-
-                       unchanged = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-                       g_hash_table_foreach (cbhttp->priv->components, ecb_http_gather_uids_cb, unchanged);
-
-                       for (link = *out_created_objects; link; link = g_slist_next (link)) {
-                               ECalMetaBackendInfo *nfo = link->data;
-
-                               if (nfo && nfo->uid)
-                                       g_hash_table_remove (unchanged, nfo->uid);
-                       }
-
-                       for (link = *out_modified_objects; link; link = g_slist_next (link)) {
-                               ECalMetaBackendInfo *nfo = link->data;
-
-                               if (nfo && nfo->uid)
-                                       g_hash_table_remove (unchanged, nfo->uid);
-                       }
-
-                       if (g_hash_table_size (unchanged))
-                               g_hash_table_foreach_remove (cbhttp->priv->components, 
ecb_http_remove_unchanged_cb, unchanged);
-
-                       g_hash_table_destroy (unchanged);
-               }
        } else {
                icalcomponent_free (vcalendar);
        }
@@ -512,7 +465,7 @@ ecb_http_list_existing_sync (ECalMetaBackend *meta_backend,
                revision = e_cal_cache_dup_component_revision (cal_cache, icalcomp);
                object = icalcomponent_as_ical_string_r (value);
 
-               nfo = e_cal_meta_backend_info_new (uid, NULL, revision, object);
+               nfo = e_cal_meta_backend_info_new (uid, revision, object, NULL);
 
                *out_existing_objects = g_slist_prepend (*out_existing_objects, nfo);
 
@@ -522,9 +475,6 @@ ecb_http_list_existing_sync (ECalMetaBackend *meta_backend,
 
        g_object_unref (cal_cache);
 
-       g_hash_table_destroy (cbhttp->priv->components);
-       cbhttp->priv->components = NULL;
-
        ecb_http_disconnect_sync (meta_backend, cancellable, NULL);
 
        return TRUE;
@@ -533,6 +483,7 @@ ecb_http_list_existing_sync (ECalMetaBackend *meta_backend,
 static gboolean
 ecb_http_load_component_sync (ECalMetaBackend *meta_backend,
                              const gchar *uid,
+                             const gchar *extra,
                              icalcomponent **out_component,
                              gchar **out_extra,
                              GCancellable *cancellable,
diff --git a/src/calendar/libedata-cal/e-cal-meta-backend.c b/src/calendar/libedata-cal/e-cal-meta-backend.c
index a0af4f9..aab860c 100644
--- a/src/calendar/libedata-cal/e-cal-meta-backend.c
+++ b/src/calendar/libedata-cal/e-cal-meta-backend.c
@@ -43,8 +43,9 @@
 #include "e-cal-backend-util.h"
 #include "e-cal-meta-backend.h"
 
-#define ECMB_KEY_SYNC_TAG "sync-tag"
-#define ECMB_KEY_DID_CONNECT "did-connect"
+#define ECMB_KEY_SYNC_TAG              "ecmb::sync-tag"
+#define ECMB_KEY_EVER_CONNECTED                "ecmb::ever-connected"
+#define ECMB_KEY_CONNECTED_WRITABLE    "ecmb::connected-writable"
 
 #define LOCAL_PREFIX "file://"
 
@@ -64,6 +65,8 @@ struct _ECalMetaBackendPrivate {
        guint refresh_timeout_id;
 
        gboolean refresh_after_authenticate;
+       gint ever_connected;
+       gint connected_writable;
 
        /* Last successful connect data, for some extensions */
        guint16 authentication_port;
@@ -99,6 +102,7 @@ static gboolean ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
                                                  ECalCache *cal_cache,
                                                  const gchar *uid,
                                                  const gchar *preloaded_object,
+                                                 const gchar *preloaded_extra,
                                                  GCancellable *cancellable,
                                                  GError **error);
 static gboolean ecmb_save_component_wrapper_sync (ECalMetaBackend *meta_backend,
@@ -110,28 +114,29 @@ static gboolean ecmb_save_component_wrapper_sync (ECalMetaBackend *meta_backend,
                                                  const gchar *orig_uid,
                                                  gboolean *out_requires_put,
                                                  gchar **out_new_uid,
+                                                 gchar **out_new_extra,
                                                  GCancellable *cancellable,
                                                  GError **error);
 
 /**
  * e_cal_cache_search_data_new:
  * @uid: a component UID; cannot be %NULL
- * @rid: (nullable): a component Recurrence-ID; can be %NULL
  * @revision: (nullable): the component revision; can be %NULL
  * @object: (nullable): the component object as an iCalendar string; can be %NULL
+ * @extra: (nullable): extra backend-specific data; can be %NULL
  *
  * Creates a new #ECalMetaBackendInfo prefilled with the given values.
  *
  * Returns: (transfer full): A new #ECalMetaBackendInfo. Free it with
- *    e_cal_meta_backend_info_free() when no longer needed.
+ *    e_cal_meta_backend_info_free(), when no longer needed.
  *
  * Since: 3.26
  **/
 ECalMetaBackendInfo *
 e_cal_meta_backend_info_new (const gchar *uid,
-                            const gchar *rid,
                             const gchar *revision,
-                            const gchar *object)
+                            const gchar *object,
+                            const gchar *extra)
 {
        ECalMetaBackendInfo *info;
 
@@ -139,9 +144,9 @@ e_cal_meta_backend_info_new (const gchar *uid,
 
        info = g_new0 (ECalMetaBackendInfo, 1);
        info->uid = g_strdup (uid);
-       info->rid = g_strdup (rid && *rid ? rid : NULL);
        info->revision = g_strdup (revision);
        info->object = g_strdup (object);
+       info->extra = g_strdup (extra);
 
        return info;
 }
@@ -159,10 +164,10 @@ e_cal_meta_backend_info_new (const gchar *uid,
 ECalMetaBackendInfo *
 e_cal_meta_backend_info_copy (const ECalMetaBackendInfo *src)
 {
-       if (src)
+       if (!src)
                return NULL;
 
-       return e_cal_meta_backend_info_new (src->uid, src->rid, src->revision, src->object);
+       return e_cal_meta_backend_info_new (src->uid, src->revision, src->object, src->extra);
 }
 
 /**
@@ -181,9 +186,9 @@ e_cal_meta_backend_info_free (gpointer ptr)
 
        if (info) {
                g_free (info->uid);
-               g_free (info->rid);
                g_free (info->revision);
                g_free (info->object);
+               g_free (info->extra);
                g_free (info);
        }
 }
@@ -271,6 +276,9 @@ ecmb_update_connection_values (ECalMetaBackend *meta_backend)
        }
 
        g_mutex_unlock (&meta_backend->priv->property_lock);
+
+       e_cal_meta_backend_set_ever_connected (meta_backend, TRUE);
+       e_cal_meta_backend_set_connected_writable (meta_backend, e_cal_backend_get_writable (E_CAL_BACKEND 
(meta_backend)));
 }
 
 static gboolean
@@ -369,6 +377,7 @@ ecmb_gather_locally_cached_objects_cb (ECalCache *cal_cache,
 static gboolean
 ecmb_get_changes_sync (ECalMetaBackend *meta_backend,
                       const gchar *last_sync_tag,
+                      gboolean is_repeat,
                       gchar **out_new_sync_tag,
                       gboolean *out_repeat,
                       GSList **out_created_objects,
@@ -421,7 +430,7 @@ ecmb_get_changes_sync (ECalMetaBackend *meta_backend,
                        continue;
 
                id.uid = nfo->uid;
-               id.rid = nfo->rid;
+               id.rid = NULL;
 
                if (!g_hash_table_contains (locally_cached, &id)) {
                        link->data = NULL;
@@ -463,7 +472,7 @@ ecmb_get_changes_sync (ECalMetaBackend *meta_backend,
                                continue;
                }
 
-               nfo = e_cal_meta_backend_info_new (id->uid, id->rid, revision, NULL);
+               nfo = e_cal_meta_backend_info_new (id->uid, revision, NULL, NULL);
                *out_removed_objects = g_slist_prepend (*out_removed_objects, nfo);
        }
 
@@ -611,7 +620,7 @@ ecmb_upload_local_changes_sync (ECalMetaBackend *meta_backend,
                        if (success) {
                                success = ecmb_save_component_wrapper_sync (meta_backend, cal_cache,
                                        change->state == E_OFFLINE_STATE_LOCALLY_MODIFIED,
-                                       conflict_resolution, instances, extra, change->uid, NULL, NULL, 
cancellable, error);
+                                       conflict_resolution, instances, extra, change->uid, NULL, NULL, NULL, 
cancellable, error);
                        }
 
                        g_slist_free_full (instances, g_object_unref);
@@ -645,6 +654,60 @@ ecmb_upload_local_changes_sync (ECalMetaBackend *meta_backend,
        return success;
 }
 
+static gboolean
+ecmb_maybe_remove_from_cache (ECalMetaBackend *meta_backend,
+                             ECalCache *cal_cache,
+                             ECacheOfflineFlag offline_flag,
+                             const gchar *uid,
+                             GCancellable *cancellable,
+                             GError **error)
+{
+       ECalBackend *cal_backend;
+       GSList *comps = NULL, *link;
+       GError *local_error = NULL;
+
+       g_return_val_if_fail (uid != NULL, FALSE);
+
+       if (!e_cal_cache_get_components_by_uid (cal_cache, uid, &comps, cancellable, &local_error)) {
+               if (g_error_matches (local_error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND)) {
+                       g_clear_error (&local_error);
+                       return TRUE;
+               }
+
+               g_propagate_error (error, local_error);
+               return FALSE;
+       }
+
+       cal_backend = E_CAL_BACKEND (meta_backend);
+
+       for (link = comps; link; link = g_slist_next (link)) {
+               ECalComponent *comp = link->data;
+               ECalComponentId *id;
+
+               g_warn_if_fail (E_IS_CAL_COMPONENT (comp));
+
+               if (!E_IS_CAL_COMPONENT (comp))
+                       continue;
+
+               id = e_cal_component_get_id (comp);
+               if (id) {
+                       if (!e_cal_cache_remove_component (cal_cache, id->uid, id->rid, offline_flag, 
cancellable, error)) {
+                               e_cal_component_free_id (id);
+                               g_slist_free_full (comps, g_object_unref);
+
+                               return FALSE;
+                       }
+
+                       e_cal_backend_notify_component_removed (cal_backend, id, comp, NULL);
+                       e_cal_component_free_id (id);
+               }
+       }
+
+       g_slist_free_full (comps, g_object_unref);
+
+       return TRUE;
+}
+
 static void
 ecmb_refresh_thread_func (ECalBackend *cal_backend,
                          gpointer user_data,
@@ -653,8 +716,8 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
 {
        ECalMetaBackend *meta_backend;
        ECalCache *cal_cache;
-       ECacheOfflineFlag offline_flag = E_CACHE_IS_ONLINE;
-       gboolean success, repeat = TRUE;
+       gboolean success, repeat = TRUE, is_repeat = FALSE;
+       GString *invalid_objects = NULL;
 
        g_return_if_fail (E_IS_CAL_META_BACKEND (cal_backend));
 
@@ -697,7 +760,7 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
                        last_sync_tag = NULL;
                }
 
-               success = e_cal_meta_backend_get_changes_sync (meta_backend, last_sync_tag, &new_sync_tag, 
&repeat,
+               success = e_cal_meta_backend_get_changes_sync (meta_backend, last_sync_tag, is_repeat, 
&new_sync_tag, &repeat,
                        &created_objects, &modified_objects, &removed_objects, cancellable, error);
 
                if (success) {
@@ -708,24 +771,19 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
                        /* Removed objects first */
                        for (link = removed_objects; link && success; link = g_slist_next (link)) {
                                ECalMetaBackendInfo *nfo = link->data;
-                               ECalComponentId id;
 
                                if (!nfo) {
                                        g_warn_if_reached ();
                                        continue;
                                }
 
-                               id.uid = nfo->uid;
-                               id.rid = nfo->rid;
-
-                               success = e_cal_cache_remove_component (cal_cache, id.uid, id.rid, 
offline_flag, cancellable, error);
-                               if (success)
-                                       e_cal_backend_notify_component_removed (cal_backend, &id, NULL, NULL);
+                               success = ecmb_maybe_remove_from_cache (meta_backend, cal_cache, 
E_CACHE_IS_ONLINE, nfo->uid, cancellable, error);
                        }
 
                        /* Then modified objects */
                        for (link = modified_objects; link && success; link = g_slist_next (link)) {
                                ECalMetaBackendInfo *nfo = link->data;
+                               GError *local_error = NULL;
 
                                if (!nfo || !nfo->uid) {
                                        g_warn_if_reached ();
@@ -737,7 +795,21 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
 
                                g_hash_table_insert (covered_uids, nfo->uid, NULL);
 
-                               success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, 
nfo->uid, nfo->object, cancellable, error);
+                               success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, 
nfo->uid, nfo->object, nfo->extra, 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)) {
+                                       if (!invalid_objects) {
+                                               invalid_objects = g_string_new (local_error->message);
+                                       } else {
+                                               g_string_append_c (invalid_objects, '\n');
+                                               g_string_append (invalid_objects, local_error->message);
+                                       }
+                                       g_clear_error (&local_error);
+                                       success = TRUE;
+                               } else if (local_error) {
+                                       g_propagate_error (error, local_error);
+                               }
                        }
 
                        g_hash_table_remove_all (covered_uids);
@@ -745,13 +817,28 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
                        /* Finally created objects */
                        for (link = created_objects; link && success; link = g_slist_next (link)) {
                                ECalMetaBackendInfo *nfo = link->data;
+                               GError *local_error = NULL;
 
                                if (!nfo || !nfo->uid) {
                                        g_warn_if_reached ();
                                        continue;
                                }
 
-                               success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, 
nfo->uid, nfo->object, cancellable, error);
+                               success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, 
nfo->uid, nfo->object, nfo->extra, 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)) {
+                                       if (!invalid_objects) {
+                                               invalid_objects = g_string_new (local_error->message);
+                                       } else {
+                                               g_string_append_c (invalid_objects, '\n');
+                                               g_string_append (invalid_objects, local_error->message);
+                                       }
+                                       g_clear_error (&local_error);
+                                       success = TRUE;
+                               } else if (local_error) {
+                                       g_propagate_error (error, local_error);
+                               }
                        }
 
                        g_hash_table_destroy (covered_uids);
@@ -765,6 +852,8 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
                g_slist_free_full (removed_objects, e_cal_meta_backend_info_free);
                g_free (last_sync_tag);
                g_free (new_sync_tag);
+
+               is_repeat = TRUE;
        }
 
        g_object_unref (cal_cache);
@@ -777,6 +866,12 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
 
        g_mutex_unlock (&meta_backend->priv->property_lock);
 
+       if (invalid_objects) {
+               e_cal_backend_notify_error (E_CAL_BACKEND (meta_backend), invalid_objects->str);
+
+               g_string_free (invalid_objects, TRUE);
+       }
+
        g_signal_emit (meta_backend, signals[REFRESH_COMPLETED], 0, NULL);
 }
 
@@ -894,60 +989,6 @@ ecmb_find_in_instances (const GSList *instances, /* ECalComponent * */
 }
 
 static gboolean
-ecmb_maybe_remove_from_cache (ECalMetaBackend *meta_backend,
-                             ECalCache *cal_cache,
-                             ECacheOfflineFlag offline_flag,
-                             const gchar *uid,
-                             GCancellable *cancellable,
-                             GError **error)
-{
-       ECalBackend *cal_backend;
-       GSList *comps = NULL, *link;
-       GError *local_error = NULL;
-
-       g_return_val_if_fail (uid != NULL, FALSE);
-
-       if (!e_cal_cache_get_components_by_uid (cal_cache, uid, &comps, cancellable, &local_error)) {
-               if (g_error_matches (local_error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND)) {
-                       g_clear_error (&local_error);
-                       return TRUE;
-               }
-
-               g_propagate_error (error, local_error);
-               return FALSE;
-       }
-
-       cal_backend = E_CAL_BACKEND (meta_backend);
-
-       for (link = comps; link; link = g_slist_next (link)) {
-               ECalComponent *comp = link->data;
-               ECalComponentId *id;
-
-               g_warn_if_fail (E_IS_CAL_COMPONENT (comp));
-
-               if (!E_IS_CAL_COMPONENT (comp))
-                       continue;
-
-               id = e_cal_component_get_id (comp);
-               if (id) {
-                       if (!e_cal_cache_remove_component (cal_cache, id->uid, id->rid, offline_flag, 
cancellable, error)) {
-                               e_cal_component_free_id (id);
-                               g_slist_free_full (comps, g_object_unref);
-
-                               return FALSE;
-                       }
-
-                       e_cal_backend_notify_component_removed (cal_backend, id, comp, NULL);
-                       e_cal_component_free_id (id);
-               }
-       }
-
-       g_slist_free_full (comps, g_object_unref);
-
-       return TRUE;
-}
-
-static gboolean
 ecmb_put_one_component (ECalMetaBackend *meta_backend,
                        ECalCache *cal_cache,
                        ECacheOfflineFlag offline_flag,
@@ -1086,6 +1127,7 @@ ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
                                  ECalCache *cal_cache,
                                  const gchar *uid,
                                  const gchar *preloaded_object,
+                                 const gchar *preloaded_extra,
                                  GCancellable *cancellable,
                                  GError **error)
 {
@@ -1098,11 +1140,14 @@ ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
        if (preloaded_object && *preloaded_object) {
                icalcomp = icalcomponent_new_from_string (preloaded_object);
                if (!icalcomp) {
-                       g_propagate_error (error, e_data_cal_create_error (InvalidObject, _("Preloaded object 
is invalid")));
+                       g_propagate_error (error, e_data_cal_create_error_fmt (InvalidObject, _("Preloaded 
object for UID ā€œ%sā€ is invalid"), uid));
                        return FALSE;
                }
-       } else if (!e_cal_meta_backend_load_component_sync (meta_backend, uid, &icalcomp, &extra, 
cancellable, error) ||
-                  !icalcomp) {
+       } else if (!e_cal_meta_backend_load_component_sync (meta_backend, uid, preloaded_extra, &icalcomp, 
&extra, cancellable, error)) {
+               g_free (extra);
+               return FALSE;
+       } else if (!icalcomp) {
+               g_propagate_error (error, e_data_cal_create_error_fmt (InvalidObject, _("Received object for 
UID ā€œ%sā€ is invalid"), uid));
                g_free (extra);
                return FALSE;
        }
@@ -1136,9 +1181,9 @@ ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
                new_instances = g_slist_reverse (new_instances);
 
                success = ecmb_put_instances (meta_backend, cal_cache, uid, offline_flag,
-                       new_instances, extra, cancellable, error);
+                       new_instances, extra ? extra : preloaded_extra, cancellable, error);
        } else {
-               g_propagate_error (error, e_data_cal_create_error (InvalidObject, _("Received object is 
invalid")));
+               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;
        }
 
@@ -1160,11 +1205,12 @@ ecmb_save_component_wrapper_sync (ECalMetaBackend *meta_backend,
                                  const gchar *orig_uid,
                                  gboolean *out_requires_put,
                                  gchar **out_new_uid,
+                                 gchar **out_new_extra,
                                  GCancellable *cancellable,
                                  GError **error)
 {
        GSList *link, *instances = NULL;
-       gchar *new_uid = NULL;
+       gchar *new_uid = NULL, *new_extra = NULL;
        gboolean has_attachments = FALSE, success = TRUE;
 
        if (out_requires_put)
@@ -1197,10 +1243,11 @@ 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, cancellable, error);
+               instances ? instances : in_instances, extra, &new_uid, &new_extra, cancellable, error);
 
        if (success && new_uid) {
-               success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, new_uid, NULL, 
cancellable, error);
+               success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, new_uid, NULL,
+                       new_extra ? new_extra : extra, cancellable, error);
 
                if (success && g_strcmp0 (new_uid, orig_uid) != 0)
                        success = ecmb_maybe_remove_from_cache (meta_backend, cal_cache, E_CACHE_IS_ONLINE, 
orig_uid, cancellable, error);
@@ -1214,6 +1261,11 @@ ecmb_save_component_wrapper_sync (ECalMetaBackend *meta_backend,
                        *out_requires_put = FALSE;
        }
 
+       if (success && out_new_extra)
+               *out_new_extra = new_extra;
+       else
+               g_free (new_extra);
+
        g_slist_free_full (instances, g_object_unref);
 
        return success;
@@ -1255,6 +1307,14 @@ ecmb_open_sync (ECalBackendSync *sync_backend,
                e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
        }
 
+       if (e_cal_meta_backend_get_ever_connected (meta_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))
+                       return;
+       }
+
        ecmb_schedule_refresh (E_CAL_META_BACKEND (sync_backend));
 }
 
@@ -1309,7 +1369,7 @@ 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, 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);
                }
 
@@ -1531,7 +1591,7 @@ ecmb_create_object_sync (ECalMetaBackend *meta_backend,
        icalcomponent *icalcomp;
        struct icaltimetype itt;
        const gchar *uid;
-       gchar *new_uid = NULL;
+       gchar *new_uid = NULL, *new_extra = NULL;
        gboolean success, requires_put = TRUE;
 
        g_return_val_if_fail (comp != NULL, FALSE);
@@ -1582,7 +1642,8 @@ ecmb_create_object_sync (ECalMetaBackend *meta_backend,
 
                instances = g_slist_prepend (NULL, comp);
 
-               if (!ecmb_save_component_wrapper_sync (meta_backend, cal_cache, FALSE, conflict_resolution, 
instances, NULL, uid, &requires_put, &new_uid, cancellable, error)) {
+               if (!ecmb_save_component_wrapper_sync (meta_backend, cal_cache, FALSE, conflict_resolution, 
instances, NULL, uid,
+                       &requires_put, &new_uid, &new_extra, cancellable, error)) {
                        g_slist_free (instances);
                        return FALSE;
                }
@@ -1591,7 +1652,7 @@ ecmb_create_object_sync (ECalMetaBackend *meta_backend,
        }
 
        if (requires_put) {
-               success = e_cal_cache_put_component (cal_cache, comp, NULL, *offline_flag, cancellable, 
error);
+               success = e_cal_cache_put_component (cal_cache, comp, new_extra, *offline_flag, cancellable, 
error);
                if (success && !out_new_comp) {
                        e_cal_backend_notify_component_created (E_CAL_BACKEND (meta_backend), comp);
                }
@@ -1613,6 +1674,7 @@ ecmb_create_object_sync (ECalMetaBackend *meta_backend,
        }
 
        g_free (new_uid);
+       g_free (new_extra);
 
        return success;
 }
@@ -1700,7 +1762,7 @@ ecmb_modify_object_sync (ECalMetaBackend *meta_backend,
        ECalComponentId *id;
        ECalComponent *old_comp = NULL, *new_comp = NULL, *master_comp, *existing_comp = NULL;
        GSList *instances = NULL;
-       gchar *extra = NULL, *new_uid = NULL;
+       gchar *extra = NULL, *new_uid = NULL, *new_extra = NULL;
        gboolean success = TRUE, requires_put = TRUE;
        GError *local_error = NULL;
 
@@ -1876,11 +1938,11 @@ ecmb_modify_object_sync (ECalMetaBackend *meta_backend,
 
        if (success && *offline_flag == E_CACHE_IS_ONLINE) {
                success = ecmb_save_component_wrapper_sync (meta_backend, cal_cache, TRUE, 
conflict_resolution,
-                       instances, extra, id->uid, &requires_put, &new_uid, cancellable, error);
+                       instances, extra, id->uid, &requires_put, &new_uid, &new_extra, cancellable, error);
        }
 
        if (success && requires_put)
-               success = ecmb_put_instances (meta_backend, cal_cache, id->uid, *offline_flag, instances, 
extra, cancellable, error);
+               success = ecmb_put_instances (meta_backend, cal_cache, id->uid, *offline_flag, instances, 
new_extra ? new_extra : extra, cancellable, error);
 
        if (!success) {
                g_clear_object (&old_comp);
@@ -1901,6 +1963,7 @@ ecmb_modify_object_sync (ECalMetaBackend *meta_backend,
        g_slist_free_full (instances, g_object_unref);
        e_cal_component_free_id (id);
        g_clear_object (&new_comp);
+       g_free (new_extra);
        g_free (new_uid);
        g_free (extra);
 
@@ -2151,13 +2214,19 @@ ecmb_remove_object_sync (ECalMetaBackend *meta_backend,
 
                if (mod == E_CAL_OBJ_MOD_ALL) {
                        if (*offline_flag == E_CACHE_IS_ONLINE) {
-                               success = e_cal_meta_backend_remove_component_sync (meta_backend, 
conflict_resolution, uid, extra, NULL, cancellable, error);
+                               gchar *ical_string = NULL;
+
+                               g_warn_if_fail (e_cal_cache_get_component_as_string (cal_cache, uid, NULL, 
&ical_string, cancellable, NULL));
+
+                               success = e_cal_meta_backend_remove_component_sync (meta_backend, 
conflict_resolution, uid, extra, ical_string, cancellable, error);
+
+                               g_free (ical_string);
                        }
 
                        success = success && ecmb_maybe_remove_from_cache (meta_backend, cal_cache, 
*offline_flag, uid, cancellable, error);
                } else {
                        gboolean requires_put = TRUE;
-                       gchar *new_uid = NULL;
+                       gchar *new_uid = NULL, *new_extra = NULL;
 
                        if (master_comp) {
                                icalcomponent *icalcomp = e_cal_component_get_icalcomponent (master_comp);
@@ -2173,11 +2242,11 @@ ecmb_remove_object_sync (ECalMetaBackend *meta_backend,
 
                        if (*offline_flag == E_CACHE_IS_ONLINE) {
                                success = ecmb_save_component_wrapper_sync (meta_backend, cal_cache, TRUE, 
conflict_resolution,
-                                       instances, extra, uid, &requires_put, &new_uid, cancellable, error);
+                                       instances, extra, uid, &requires_put, &new_uid, &new_extra, 
cancellable, error);
                        }
 
                        if (success && requires_put)
-                               success = ecmb_put_instances (meta_backend, cal_cache, uid, *offline_flag, 
instances, extra, cancellable, error);
+                               success = ecmb_put_instances (meta_backend, cal_cache, uid, *offline_flag, 
instances, new_extra ? new_extra : extra, cancellable, error);
 
                        if (success && new_uid && !requires_put) {
                                g_clear_object (&new_comp);
@@ -2187,6 +2256,7 @@ ecmb_remove_object_sync (ECalMetaBackend *meta_backend,
                        }
 
                        g_free (new_uid);
+                       g_free (new_extra);
                }
 
                g_free (extra);
@@ -3166,6 +3236,8 @@ e_cal_meta_backend_init (ECalMetaBackend *meta_backend)
        meta_backend->priv->view_cancellables = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, 
g_object_unref);
        meta_backend->priv->current_online_state = FALSE;
        meta_backend->priv->refresh_after_authenticate = FALSE;
+       meta_backend->priv->ever_connected = -1;
+       meta_backend->priv->connected_writable = -1;
 }
 
 /**
@@ -3191,6 +3263,132 @@ e_cal_meta_backend_get_capabilities (ECalMetaBackend *meta_backend)
 }
 
 /**
+ * e_cal_meta_backend_set_ever_connected:
+ * @meta_backend: an #ECalMetaBackend
+ * @value: value to set
+ *
+ * Sets whether the @meta_backend ever made a successful connection
+ * to its destination.
+ *
+ * This is used by the @meta_backend itself, during the opening phase,
+ * when it had not been connected yet, then it does so immediately, to
+ * eventually report settings error easily.
+ *
+ * Since: 3.26
+ **/
+void
+e_cal_meta_backend_set_ever_connected (ECalMetaBackend *meta_backend,
+                                      gboolean value)
+{
+       ECalCache *cal_cache;
+
+       g_return_if_fail (E_IS_CAL_META_BACKEND (meta_backend));
+
+       if ((value ? 1 : 0) == meta_backend->priv->ever_connected)
+               return;
+
+       cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
+       meta_backend->priv->ever_connected = value ? 1 : 0;
+       e_cache_set_key_int (E_CACHE (cal_cache), ECMB_KEY_EVER_CONNECTED, 
meta_backend->priv->ever_connected, NULL);
+       g_clear_object (&cal_cache);
+}
+
+/**
+ * e_cal_meta_backend_get_ever_connected:
+ * @meta_backend: an #ECalMetaBackend
+ *
+ * Returns: Whether the @meta_backend ever made a successful connection
+ *    to its destination.
+ *
+ * Since: 3.26
+ **/
+gboolean
+e_cal_meta_backend_get_ever_connected (ECalMetaBackend *meta_backend)
+{
+       gboolean result;
+
+       g_return_val_if_fail (E_IS_CAL_META_BACKEND (meta_backend), FALSE);
+
+       if (meta_backend->priv->ever_connected == -1) {
+               ECalCache *cal_cache;
+
+               cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
+               result = e_cache_get_key_int (E_CACHE (cal_cache), ECMB_KEY_EVER_CONNECTED, NULL) == 1;
+               g_clear_object (&cal_cache);
+
+               meta_backend->priv->ever_connected = result ? 1 : 0;
+       } else {
+               result = meta_backend->priv->ever_connected == 1;
+       }
+
+       return result;
+}
+
+/**
+ * e_cal_meta_backend_set_connected_writable:
+ * @meta_backend: an #ECalMetaBackend
+ * @value: value to set
+ *
+ * Sets whether the @meta_backend connected to a writable destination.
+ * This value has meaning only if e_cal_meta_backend_get_ever_connected()
+ * is %TRUE.
+ *
+ * This is used by the @meta_backend itself, during the opening phase,
+ * to set the backend writable or not also in the offline mode.
+ *
+ * Since: 3.26
+ **/
+void
+e_cal_meta_backend_set_connected_writable (ECalMetaBackend *meta_backend,
+                                          gboolean value)
+{
+       ECalCache *cal_cache;
+
+       g_return_if_fail (E_IS_CAL_META_BACKEND (meta_backend));
+
+       if ((value ? 1 : 0) == meta_backend->priv->connected_writable)
+               return;
+
+       cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
+       meta_backend->priv->connected_writable = value ? 1 : 0;
+       e_cache_set_key_int (E_CACHE (cal_cache), ECMB_KEY_CONNECTED_WRITABLE, 
meta_backend->priv->connected_writable, NULL);
+       g_clear_object (&cal_cache);
+}
+
+/**
+ * e_cal_meta_backend_get_connected_writable:
+ * @meta_backend: an #ECalMetaBackend
+ *
+ * This value has meaning only if e_cal_meta_backend_get_ever_connected()
+ * is %TRUE.
+ *
+ * Returns: Whether the @meta_backend connected to a writable destination.
+ *
+ * Since: 3.26
+ **/
+gboolean
+e_cal_meta_backend_get_connected_writable (ECalMetaBackend *meta_backend)
+{
+       gboolean result;
+
+       g_return_val_if_fail (E_IS_CAL_META_BACKEND (meta_backend), FALSE);
+
+       if (meta_backend->priv->connected_writable == -1) {
+               ECalCache *cal_cache;
+
+               cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
+               result = e_cache_get_key_int (E_CACHE (cal_cache), ECMB_KEY_CONNECTED_WRITABLE, NULL) == 1;
+               g_clear_object (&cal_cache);
+
+               meta_backend->priv->connected_writable = result ? 1 : 0;
+       } else {
+               result = meta_backend->priv->connected_writable == 1;
+       }
+
+       return result;
+}
+
+/**
  * e_cal_meta_backend_set_cache:
  * @meta_backend: an #ECalMetaBackend
  * @cache: an #ECalCache to use
@@ -3719,12 +3917,15 @@ e_cal_meta_backend_empty_cache_sync (ECalMetaBackend *meta_backend,
  * @cancellable: optional #GCancellable object, or %NULL
  * @error: return location for a #GError, or %NULL
  *
- * This is called always before any operations which requires
- * a connection to the remote side. It can fail with
- * an #E_CLIENT_ERROR_REPOSITORY_OFFLINE error to indicate
- * that the remote side cannot be currently reached. Other
- * errors are propagated to the caller/client side.
- * This method is not called when the backend is not online.
+ * This is called always before any operations which requires a connection
+ * to the remote side. It can fail with an #E_CLIENT_ERROR_REPOSITORY_OFFLINE
+ * error to indicate that the remote side cannot be currently reached. Other
+ * errors are propagated to the caller/client side. This method is not called
+ * when the backend is not online.
+ *
+ * The descendant should also call e_cal_backend_set_writable() after successful
+ * connect to the remote side. This value is stored for later use, when being
+ * opened offline.
  *
  * The @credentials parameter consists of the previously used credentials.
  * It's always %NULL with the first connection attempt. To get the credentials,
@@ -3804,6 +4005,7 @@ e_cal_meta_backend_disconnect_sync (ECalMetaBackend *meta_backend,
  * e_cal_meta_backend_get_changes_sync:
  * @meta_backend: an #ECalMetaBackend
  * @last_sync_tag: (nullable): optional sync tag from the last check
+ * @is_repeat: set to %TRUE when this is the repeated call
  * @out_new_sync_tag: (out) (transfer full): new sync tag to store on success
  * @out_repeat: (out): whether to repeat this call again; default is %FALSE
  * @out_created_objects: (out) (element-type ECalMetaBackendInfo) (transfer full):
@@ -3829,7 +4031,8 @@ e_cal_meta_backend_disconnect_sync (ECalMetaBackend *meta_backend,
  * The @out_repeat can be set to %TRUE when the descendant didn't finish
  * read of all the changes. In that case the @meta_backend calls this
  * function again with the @out_new_sync_tag as the @last_sync_tag, but also
- * notifies about the found changes immediately.
+ * notifies about the found changes immediately. The @is_repeat is set
+ * to %TRUE as well in this case, otherwise it's %FALSE.
  *
  * The descendant can populate also ECalMetaBackendInfo::object of
  * the @out_created_objects and @out_modified_objects, if known, in which
@@ -3851,6 +4054,7 @@ e_cal_meta_backend_disconnect_sync (ECalMetaBackend *meta_backend,
 gboolean
 e_cal_meta_backend_get_changes_sync (ECalMetaBackend *meta_backend,
                                     const gchar *last_sync_tag,
+                                    gboolean is_repeat,
                                     gchar **out_new_sync_tag,
                                     gboolean *out_repeat,
                                     GSList **out_created_objects,
@@ -3875,6 +4079,7 @@ e_cal_meta_backend_get_changes_sync (ECalMetaBackend *meta_backend,
 
        return klass->get_changes_sync (meta_backend,
                last_sync_tag,
+               is_repeat,
                out_new_sync_tag,
                out_repeat,
                out_created_objects,
@@ -3933,6 +4138,7 @@ e_cal_meta_backend_list_existing_sync (ECalMetaBackend *meta_backend,
  * e_cal_meta_backend_load_component_sync:
  * @meta_backend: an #ECalMetaBackend
  * @uid: a component UID
+ * @extra: (nullable): optional extra data stored with the component, or %NULL
  * @out_component: (out) (transfer full): a loaded component, as icalcomponent
  * @out_extra: (out) (transfer full): an extra data to store to #ECalCache with this component
  * @cancellable: optional #GCancellable object, or %NULL
@@ -3959,6 +4165,7 @@ e_cal_meta_backend_list_existing_sync (ECalMetaBackend *meta_backend,
 gboolean
 e_cal_meta_backend_load_component_sync (ECalMetaBackend *meta_backend,
                                        const gchar *uid,
+                                       const gchar *extra,
                                        icalcomponent **out_component,
                                        gchar **out_extra,
                                        GCancellable *cancellable,
@@ -3975,7 +4182,7 @@ e_cal_meta_backend_load_component_sync (ECalMetaBackend *meta_backend,
        g_return_val_if_fail (klass != NULL, FALSE);
        g_return_val_if_fail (klass->load_component_sync != NULL, FALSE);
 
-       return klass->load_component_sync (meta_backend, uid, out_component, out_extra, cancellable, error);
+       return klass->load_component_sync (meta_backend, uid, extra, out_component, out_extra, cancellable, 
error);
 }
 
 /**
@@ -3986,6 +4193,7 @@ e_cal_meta_backend_load_component_sync (ECalMetaBackend *meta_backend,
  * @instances: (element-type ECalComponent): instances of the component to save
  * @extra: (nullable): extra data saved with the components in an #ECalCache
  * @out_new_uid: (out) (transfer full): return location for the UID of the saved component
+ * @out_new_extra: (out) (transfer full): return location for the extra data to store with the component
  * @cancellable: optional #GCancellable object, or %NULL
  * @error: return location for a #GError, or %NULL
  *
@@ -4008,6 +4216,9 @@ e_cal_meta_backend_load_component_sync (ECalMetaBackend *meta_backend,
  * assigned it to it. This UID, if set, is loaded from the remote side afterwards,
  * also to see whether any changes had been made to the component by the remote side.
  *
+ * The @out_new_extra can be populated with a new extra data to save with the component.
+ * Left it %NULL, to keep the same value as the @extra.
+ *
  * The descendant can use an #E_CLIENT_ERROR_OUT_OF_SYNC error to indicate that
  * the save failed due to made changes on the remote side, and let the @meta_backend
  * to resolve this conflict based on the @conflict_resolution on its own.
@@ -4027,6 +4238,7 @@ e_cal_meta_backend_save_component_sync (ECalMetaBackend *meta_backend,
                                        const GSList *instances,
                                        const gchar *extra,
                                        gchar **out_new_uid,
+                                       gchar **out_new_extra,
                                        GCancellable *cancellable,
                                        GError **error)
 {
@@ -4035,6 +4247,7 @@ e_cal_meta_backend_save_component_sync (ECalMetaBackend *meta_backend,
        g_return_val_if_fail (E_IS_CAL_META_BACKEND (meta_backend), FALSE);
        g_return_val_if_fail (instances != NULL, FALSE);
        g_return_val_if_fail (out_new_uid != NULL, FALSE);
+       g_return_val_if_fail (out_new_extra != NULL, FALSE);
 
        klass = E_CAL_META_BACKEND_GET_CLASS (meta_backend);
        g_return_val_if_fail (klass != NULL, FALSE);
@@ -4044,7 +4257,15 @@ e_cal_meta_backend_save_component_sync (ECalMetaBackend *meta_backend,
                return FALSE;
        }
 
-       return klass->save_component_sync (meta_backend, overwrite_existing, conflict_resolution, instances, 
extra, out_new_uid, cancellable, error);
+       return klass->save_component_sync (meta_backend,
+               overwrite_existing,
+               conflict_resolution,
+               instances,
+               extra,
+               out_new_uid,
+               out_new_extra,
+               cancellable,
+               error);
 }
 
 /**
diff --git a/src/calendar/libedata-cal/e-cal-meta-backend.h b/src/calendar/libedata-cal/e-cal-meta-backend.h
index d4a7bf5..ba1a4e1 100644
--- a/src/calendar/libedata-cal/e-cal-meta-backend.h
+++ b/src/calendar/libedata-cal/e-cal-meta-backend.h
@@ -50,9 +50,9 @@ G_BEGIN_DECLS
 
 typedef struct _ECalMetaBackendInfo {
        gchar *uid;
-       gchar *rid;
        gchar *revision;
        gchar *object;
+       gchar *extra;
 } ECalMetaBackendInfo;
 
 #define E_TYPE_CAL_META_BACKEND_INFO (e_cal_meta_backend_info_get_type ())
@@ -61,9 +61,9 @@ GType         e_cal_meta_backend_info_get_type
                                                (void) G_GNUC_CONST;
 ECalMetaBackendInfo *
                e_cal_meta_backend_info_new     (const gchar *uid,
-                                                const gchar *rid,
                                                 const gchar *revision,
-                                                const gchar *object);
+                                                const gchar *object,
+                                                const gchar *extra);
 ECalMetaBackendInfo *
                e_cal_meta_backend_info_copy    (const ECalMetaBackendInfo *src);
 void           e_cal_meta_backend_info_free    (gpointer ptr /* ECalMetaBackendInfo * */);
@@ -111,6 +111,7 @@ struct _ECalMetaBackendClass {
 
        gboolean        (* get_changes_sync)    (ECalMetaBackend *meta_backend,
                                                 const gchar *last_sync_tag,
+                                                gboolean is_repeat,
                                                 gchar **out_new_sync_tag,
                                                 gboolean *out_repeat,
                                                 GSList **out_created_objects, /* ECalMetaBackendInfo * */
@@ -125,6 +126,7 @@ struct _ECalMetaBackendClass {
                                                 GError **error);
        gboolean        (* load_component_sync) (ECalMetaBackend *meta_backend,
                                                 const gchar *uid,
+                                                const gchar *extra,
                                                 icalcomponent **out_component,
                                                 gchar **out_extra,
                                                 GCancellable *cancellable,
@@ -135,6 +137,7 @@ struct _ECalMetaBackendClass {
                                                 const GSList *instances, /* ECalComponent * */
                                                 const gchar *extra,
                                                 gchar **out_new_uid,
+                                                gchar **out_new_extra,
                                                 GCancellable *cancellable,
                                                 GError **error);
        gboolean        (* remove_component_sync)
@@ -158,6 +161,16 @@ GType              e_cal_meta_backend_get_type     (void) G_GNUC_CONST;
 
 const gchar *  e_cal_meta_backend_get_capabilities
                                                (ECalMetaBackend *meta_backend);
+void           e_cal_meta_backend_set_ever_connected
+                                               (ECalMetaBackend *meta_backend,
+                                                gboolean value);
+gboolean       e_cal_meta_backend_get_ever_connected
+                                               (ECalMetaBackend *meta_backend);
+void           e_cal_meta_backend_set_connected_writable
+                                               (ECalMetaBackend *meta_backend,
+                                                gboolean value);
+gboolean       e_cal_meta_backend_get_connected_writable
+                                               (ECalMetaBackend *meta_backend);
 void           e_cal_meta_backend_set_cache    (ECalMetaBackend *meta_backend,
                                                 ECalCache *cache);
 ECalCache *    e_cal_meta_backend_ref_cache    (ECalMetaBackend *meta_backend);
@@ -199,6 +212,7 @@ gboolean    e_cal_meta_backend_disconnect_sync
 gboolean       e_cal_meta_backend_get_changes_sync
                                                (ECalMetaBackend *meta_backend,
                                                 const gchar *last_sync_tag,
+                                                gboolean is_repeat,
                                                 gchar **out_new_sync_tag,
                                                 gboolean *out_repeat,
                                                 GSList **out_created_objects, /* ECalMetaBackendInfo * */
@@ -215,6 +229,7 @@ gboolean    e_cal_meta_backend_list_existing_sync
 gboolean       e_cal_meta_backend_load_component_sync
                                                (ECalMetaBackend *meta_backend,
                                                 const gchar *uid,
+                                                const gchar *extra,
                                                 icalcomponent **out_component,
                                                 gchar **out_extra,
                                                 GCancellable *cancellable,
@@ -226,6 +241,7 @@ gboolean    e_cal_meta_backend_save_component_sync
                                                 const GSList *instances, /* ECalComponent * */
                                                 const gchar *extra,
                                                 gchar **out_new_uid,
+                                                gchar **out_new_extra,
                                                 GCancellable *cancellable,
                                                 GError **error);
 gboolean       e_cal_meta_backend_remove_component_sync
diff --git a/src/libedataserver/e-soup-session.c b/src/libedataserver/e-soup-session.c
index bf8bb36..4a175d1 100644
--- a/src/libedataserver/e-soup-session.c
+++ b/src/libedataserver/e-soup-session.c
@@ -114,6 +114,12 @@ e_soup_session_setup_bearer_auth (ESoupSession *session,
        source = e_soup_session_get_source (session);
        credentials = e_soup_session_dup_credentials (session);
 
+       if (!credentials || !e_named_parameters_count (credentials)) {
+               e_named_parameters_free (credentials);
+               g_set_error_literal (error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED, _("Credentials 
required"));
+               return FALSE;
+       }
+
        success = e_util_get_source_oauth2_access_token_sync (source, credentials,
                &access_token, &expires_in_seconds, cancellable, error);
 
diff --git a/src/libedataserver/e-source-credentials-provider-impl-google.c 
b/src/libedataserver/e-source-credentials-provider-impl-google.c
index 80146f2..9867df4 100644
--- a/src/libedataserver/e-source-credentials-provider-impl-google.c
+++ b/src/libedataserver/e-source-credentials-provider-impl-google.c
@@ -522,6 +522,7 @@ e_source_credentials_google_get_access_token_sync (ESource *source,
                                                   GError **error)
 {
        ENamedParameters *tmp_credentials = NULL;
+       gboolean success;
 
        g_return_val_if_fail (credentials != NULL, FALSE);
 
@@ -546,10 +547,13 @@ e_source_credentials_google_get_access_token_sync (ESource *source,
                return TRUE;
        }
 
+       /* Try to refresh the token */
+       success = e_source_credentials_google_refresh_token_sync (source, tmp_credentials ? tmp_credentials : 
credentials,
+               out_access_token, out_expires_in_seconds, cancellable, error);
+
        e_named_parameters_free (tmp_credentials);
 
-       /* Try to refresh the token */
-       return e_source_credentials_google_refresh_token_sync (source, credentials, out_access_token, 
out_expires_in_seconds, cancellable, error);
+       return success;
 }
 
 gboolean
diff --git a/src/libedataserver/e-webdav-session.c b/src/libedataserver/e-webdav-session.c
index 6202396..5752d8c 100644
--- a/src/libedataserver/e-webdav-session.c
+++ b/src/libedataserver/e-webdav-session.c
@@ -628,7 +628,7 @@ e_webdav_session_new (ESource *source)
 }
 
 /**
- * e_webdav_session_options_sync:
+ * e_webdav_session_new_request:
  * @webdav: an #EWebDAVSession
  * @method: an HTTP method
  * @uri: (nullable): URI to create the request for, or %NULL to read from #ESource
@@ -998,12 +998,14 @@ e_webdav_session_replace_with_detailed_error (EWebDAVSession *webdav,
 /**
  * e_webdav_session_ensure_full_uri:
  * @webdav: an #EWebDAVSession
- * @request_uri: a #SoupURI to which the @href belongs
+ * @request_uri: (nullable): a #SoupURI to which the @href belongs, or %NULL
  * @href: a possibly path-only href
  *
- * Converts possbily path-only @href into a full URI under
- * the @request_uri. Free the returned pointer with g_free(),
- * when no longer needed.
+ * Converts possibly path-only @href into a full URI under the @request_uri.
+ * When the @request_uri is %NULL, the URI defined in associated #ESource is
+ * used instead.
+ *
+ * Free the returned pointer with g_free(), when no longer needed.
  *
  * Returns: (transfer full): The @href as a full URI
  *
@@ -1015,14 +1017,27 @@ e_webdav_session_ensure_full_uri (EWebDAVSession *webdav,
                                  const gchar *href)
 {
        g_return_val_if_fail (E_IS_WEBDAV_SESSION (webdav), NULL);
-       g_return_val_if_fail (request_uri != NULL, NULL);
        g_return_val_if_fail (href != NULL, NULL);
 
        if (*href == '/' || !strstr (href, "://")) {
                SoupURI *soup_uri;
                gchar *full_uri;
 
-               soup_uri = soup_uri_copy ((SoupURI *) request_uri);
+               if (request_uri) {
+                       soup_uri = soup_uri_copy ((SoupURI *) request_uri);
+               } else {
+                       ESource *source;
+                       ESourceWebdav *webdav_extension;
+
+                       source = e_soup_session_get_source (E_SOUP_SESSION (webdav));
+                       g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+                       webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+                       soup_uri = e_source_webdav_dup_soup_uri (webdav_extension);
+               }
+
+               g_return_val_if_fail (soup_uri != NULL, NULL);
+
                soup_uri_set_path (soup_uri, href);
                soup_uri_set_user (soup_uri, NULL);
                soup_uri_set_password (soup_uri, NULL);
@@ -1093,7 +1108,7 @@ e_webdav_session_comma_header_to_hashtable (SoupMessageHeaders *headers,
  * value can be %NULL on success, it's when the server doesn't provide the information.
  *
  * The @out_allows contains a set of allowed methods returned by the server. Some known
- * are defined as E_WEBDAV_ALLOW_OPTIONS, and so on. The 'value' of the #GHashTable
+ * are defined as %SOUP_METHOD_OPTIONS, and so on. The 'value' of the #GHashTable
  * doesn't have any particular meaning and the strings are compared case insensitively.
  * Free the hash table with g_hash_table_destroy(), when no longer needed. The returned
  * value can be %NULL on success, it's when the server doesn't provide the information.
@@ -1148,6 +1163,96 @@ e_webdav_session_options_sync (EWebDAVSession *webdav,
 }
 
 /**
+ * e_webdav_session_post_sync:
+ * @webdav: an #EWebDAVSession
+ * @uri: (nullable): URI to issue the request for, or %NULL to read from #ESource
+ * @data: data to post to the server
+ * @data_length: length of @data, or -1, when @data is NUL-terminated
+ * @out_content_type: (nullable) (transfer full): return location for response Content-Type, or %NULL
+ * @out_content: (nullable) (transfer full): return location for response content, or %NULL
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Issues POST request on the provided @uri, or, in case it's %NULL, on the URI
+ * defined in associated #ESource.
+ *
+ * The optional @out_content_type can be used to get content type of the response.
+ * Free it with g_free(), when no longer needed.
+ *
+ * The optional @out_content can be used to get actual result content. Free it
+ * with g_byte_array_free(), when no longer needed.
+ *
+ * Returns: Whether succeeded.
+ *
+ * Since: 3.26
+ **/
+gboolean
+e_webdav_session_post_sync (EWebDAVSession *webdav,
+                           const gchar *uri,
+                           const gchar *data,
+                           gsize data_length,
+                           gchar **out_content_type,
+                           GByteArray **out_content,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+       SoupRequestHTTP *request;
+       SoupMessage *message;
+       GByteArray *bytes;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_WEBDAV_SESSION (webdav), FALSE);
+       g_return_val_if_fail (data != NULL, FALSE);
+
+       if (out_content_type)
+               *out_content_type = NULL;
+
+       if (out_content)
+               *out_content = NULL;
+
+       if (data_length == (gsize) -1)
+               data_length = strlen (data);
+
+       request = e_webdav_session_new_request (webdav, SOUP_METHOD_POST, uri, error);
+       if (!request)
+               return FALSE;
+
+       message = soup_request_http_get_message (request);
+       if (!message) {
+               g_warn_if_fail (message != NULL);
+               g_object_unref (request);
+
+               return FALSE;
+       }
+
+       soup_message_set_request (message, E_WEBDAV_CONTENT_TYPE_XML,
+               SOUP_MEMORY_COPY, data, data_length);
+
+       bytes = e_soup_session_send_request_simple_sync (E_SOUP_SESSION (webdav), request, cancellable, 
error);
+
+       success = !e_webdav_session_replace_with_detailed_error (webdav, request, bytes, TRUE, _("Failed to 
post data"), error) &&
+               bytes != NULL;
+
+       if (success) {
+               if (out_content_type) {
+                       *out_content_type = g_strdup (soup_message_headers_get_content_type 
(message->response_headers, NULL));
+               }
+
+               if (out_content) {
+                       *out_content = bytes;
+                       bytes = NULL;
+               }
+       }
+
+       if (bytes)
+               g_byte_array_free (bytes, TRUE);
+       g_object_unref (message);
+       g_object_unref (request);
+
+       return success;
+}
+
+/**
  * e_webdav_session_propfind_sync:
  * @webdav: an #EWebDAVSession
  * @uri: (nullable): URI to issue the request for, or %NULL to read from #ESource
@@ -1318,7 +1423,7 @@ e_webdav_session_proppatch_sync (EWebDAVSession *webdav,
  * e_webdav_session_report_sync:
  * @webdav: an #EWebDAVSession
  * @uri: (nullable): URI to issue the request for, or %NULL to read from #ESource
- * @depth: (nullable): requested depth, can be %NULL, then %E_WEBDAV_DEPTH_THIS is used
+ * @depth: (nullable): requested depth, can be %NULL, then no Depth header is sent
  * @xml: the request itself, as an #EXmlDocument
  * @func: (nullable): an #EWebDAVPropstatTraverseFunc function to call for each DAV:propstat in the 
multistatus response, or %NULL
  * @func_user_data: user data passed to @func
@@ -1389,10 +1494,8 @@ e_webdav_session_report_sync (EWebDAVSession *webdav,
                return FALSE;
        }
 
-       if (!depth)
-               depth = E_WEBDAV_DEPTH_THIS;
-
-       soup_message_headers_replace (message->request_headers, "Depth", depth);
+       if (depth)
+               soup_message_headers_replace (message->request_headers, "Depth", depth);
 
        content = e_xml_document_get_content (xml, &content_length);
        if (!content) {
@@ -1534,10 +1637,8 @@ e_webdav_session_mkcol_addressbook_sync (EWebDAVSession *webdav,
        e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "set");
        e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "prop");
        e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "resourcetype");
-       e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "collection");
-       e_xml_document_end_element (xml);
-       e_xml_document_start_element (xml, E_WEBDAV_NS_CARDDAV, "addressbook");
-       e_xml_document_end_element (xml);
+       e_xml_document_add_empty_element (xml, E_WEBDAV_NS_DAV, "collection");
+       e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CARDDAV, "addressbook");
        e_xml_document_end_element (xml); /* resourcetype */
 
        if (display_name && *display_name) {
@@ -1841,6 +1942,7 @@ e_webdav_session_get_sync (EWebDAVSession *webdav,
                SoupLoggerLogLevel log_level = e_soup_session_get_log_level (E_SOUP_SESSION (webdav));
                gpointer buffer;
                gsize nread = 0, nwritten;
+               gboolean first_chunk = TRUE;
 
                buffer = g_malloc (BUFFER_SIZE);
 
@@ -1851,11 +1953,27 @@ e_webdav_session_get_sync (EWebDAVSession *webdav,
                                fflush (stdout);
                        }
 
+                       if (first_chunk) {
+                               GByteArray tmp_bytes;
+
+                               first_chunk = FALSE;
+
+                               tmp_bytes.data = buffer;
+                               tmp_bytes.len = nread;
+
+                               success = !e_webdav_session_replace_with_detailed_error (webdav, request, 
&tmp_bytes, FALSE, _("Failed to read resource"), error);
+                               if (!success)
+                                       break;
+                       }
+
                        success = g_output_stream_write_all (out_stream, buffer, nread, &nwritten, 
cancellable, error);
                        if (!success)
                                break;
                }
 
+               if (success && first_chunk)
+                       success = !e_webdav_session_replace_with_detailed_error (webdav, request, NULL, 
FALSE, _("Failed to read resource"), error);
+
                g_free (buffer);
        }
 
@@ -1884,8 +2002,9 @@ e_webdav_session_get_sync (EWebDAVSession *webdav,
  * reference a collection.
  *
  * The @out_bytes is filled by actual data being read. If not %NULL, @out_length
- * is populated with how many bytes had been read. Free the @out_bytes with g_free(),
- * when no longer needed.
+ * is populated with how many bytes had been read. The @out_bytes is always
+ * NUL-terminated, while this termination byte is not part of @out_length.
+ * Free the @out_bytes with g_free(), when no longer needed.
  *
  * Free returned pointer of @out_href and @out_etag, if not %NULL, with g_free(),
  * when no longer needed.
@@ -1907,6 +2026,7 @@ e_webdav_session_get_data_sync (EWebDAVSession *webdav,
                                GError **error)
 {
        GOutputStream *output_stream;
+       gsize bytes_written = 0;
        gboolean success;
 
        g_return_val_if_fail (E_IS_WEBDAV_SESSION (webdav), FALSE);
@@ -1920,11 +2040,13 @@ e_webdav_session_get_data_sync (EWebDAVSession *webdav,
        output_stream = g_memory_output_stream_new_resizable ();
 
        success = e_webdav_session_get_sync (webdav, uri, out_href, out_etag, output_stream, cancellable, 
error) &&
+               g_output_stream_write_all (output_stream, "", 1, &bytes_written, cancellable, error) &&
                g_output_stream_close (output_stream, cancellable, error);
 
        if (success) {
                if (out_length)
-                       *out_length = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM 
(output_stream));
+                       *out_length = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM 
(output_stream)) - 1;
+
                *out_bytes = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (output_stream));
        }
 
@@ -3075,8 +3197,7 @@ e_webdav_session_getctag_sync (EWebDAVSession *webdav,
        e_xml_document_add_namespaces (xml, "CS", E_WEBDAV_NS_CALENDARSERVER, NULL);
 
        e_xml_document_start_element (xml, NULL, "prop");
-       e_xml_document_start_element (xml, E_WEBDAV_NS_CALENDARSERVER, "getctag");
-       e_xml_document_end_element (xml); /* getctag */
+       e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CALENDARSERVER, "getctag");
        e_xml_document_end_element (xml); /* prop */
 
        success = e_webdav_session_propfind_sync (webdav, uri, E_WEBDAV_DEPTH_THIS, xml,
@@ -3363,8 +3484,7 @@ e_webdav_session_list_sync (EWebDAVSession *webdav,
 
        e_xml_document_start_element (xml, NULL, "prop");
 
-       e_xml_document_start_element (xml, NULL, "resourcetype");
-       e_xml_document_end_element (xml);
+       e_xml_document_add_empty_element (xml, NULL, "resourcetype");
 
        if ((flags & E_WEBDAV_LIST_SUPPORTS) != 0 ||
            (flags & E_WEBDAV_LIST_DESCRIPTION) != 0 ||
@@ -3373,60 +3493,49 @@ e_webdav_session_list_sync (EWebDAVSession *webdav,
        }
 
        if ((flags & E_WEBDAV_LIST_SUPPORTS) != 0) {
-               e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "supported-calendar-component-set");
-               e_xml_document_end_element (xml);
+               e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CALDAV, 
"supported-calendar-component-set");
        }
 
        if ((flags & E_WEBDAV_LIST_DISPLAY_NAME) != 0) {
-               e_xml_document_start_element (xml, NULL, "displayname");
-               e_xml_document_end_element (xml);
+               e_xml_document_add_empty_element (xml, NULL, "displayname");
        }
 
        if ((flags & E_WEBDAV_LIST_ETAG) != 0) {
-               e_xml_document_start_element (xml, NULL, "getetag");
-               e_xml_document_end_element (xml);
+               e_xml_document_add_empty_element (xml, NULL, "getetag");
 
                e_xml_document_add_namespaces (xml, "CS", E_WEBDAV_NS_CALENDARSERVER, NULL);
 
-               e_xml_document_start_element (xml, E_WEBDAV_NS_CALENDARSERVER, "getctag");
-               e_xml_document_end_element (xml);
+               e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CALENDARSERVER, "getctag");
        }
 
        if ((flags & E_WEBDAV_LIST_CONTENT_TYPE) != 0) {
-               e_xml_document_start_element (xml, NULL, "getcontenttype");
-               e_xml_document_end_element (xml);
+               e_xml_document_add_empty_element (xml, NULL, "getcontenttype");
        }
 
        if ((flags & E_WEBDAV_LIST_CONTENT_LENGTH) != 0) {
-               e_xml_document_start_element (xml, NULL, "getcontentlength");
-               e_xml_document_end_element (xml);
+               e_xml_document_add_empty_element (xml, NULL, "getcontentlength");
        }
 
        if ((flags & E_WEBDAV_LIST_CREATION_DATE) != 0) {
-               e_xml_document_start_element (xml, NULL, "creationdate");
-               e_xml_document_end_element (xml);
+               e_xml_document_add_empty_element (xml, NULL, "creationdate");
        }
 
        if ((flags & E_WEBDAV_LIST_LAST_MODIFIED) != 0) {
-               e_xml_document_start_element (xml, NULL, "getlastmodified");
-               e_xml_document_end_element (xml);
+               e_xml_document_add_empty_element (xml, NULL, "getlastmodified");
        }
 
        if ((flags & E_WEBDAV_LIST_DESCRIPTION) != 0) {
-               e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "calendar-description");
-               e_xml_document_end_element (xml);
+               e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CALDAV, "calendar-description");
 
                e_xml_document_add_namespaces (xml, "A", E_WEBDAV_NS_CARDDAV, NULL);
 
-               e_xml_document_start_element (xml, E_WEBDAV_NS_CARDDAV, "addressbook-description");
-               e_xml_document_end_element (xml);
+               e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CARDDAV, "addressbook-description");
        }
 
        if ((flags & E_WEBDAV_LIST_COLOR) != 0) {
                e_xml_document_add_namespaces (xml, "IC", E_WEBDAV_NS_ICAL, NULL);
 
-               e_xml_document_start_element (xml, E_WEBDAV_NS_ICAL, "calendar-color");
-               e_xml_document_end_element (xml);
+               e_xml_document_add_empty_element (xml, E_WEBDAV_NS_ICAL, "calendar-color");
        }
 
        e_xml_document_end_element (xml); /* prop */
@@ -3531,10 +3640,9 @@ e_webdav_session_update_properties_sync (EWebDAVSession *webdav,
                case E_WEBDAV_PROPERTY_REMOVE:
                        e_xml_document_start_element (xml, NULL, "remove");
                        e_xml_document_start_element (xml, NULL, "prop");
-                       e_xml_document_start_element (xml, change->ns_uri, change->name);
-                       e_xml_document_end_element (xml); /* change->name */
+                       e_xml_document_add_empty_element (xml, change->ns_uri, change->name);
                        e_xml_document_end_element (xml); /* prop */
-                       e_xml_document_end_element (xml); /* set */
+                       e_xml_document_end_element (xml); /* remove */
                        break;
                }
        }
@@ -3595,19 +3703,16 @@ e_webdav_session_lock_resource_sync (EWebDAVSession *webdav,
        e_xml_document_start_element (xml, NULL, "lockscope");
        switch (lock_scope) {
        case E_WEBDAV_LOCK_EXCLUSIVE:
-               e_xml_document_start_element (xml, NULL, "exclusive");
-               e_xml_document_end_element (xml);
+               e_xml_document_add_empty_element (xml, NULL, "exclusive");
                break;
        case E_WEBDAV_LOCK_SHARED:
-               e_xml_document_start_element (xml, NULL, "shared");
-               e_xml_document_end_element (xml);
+               e_xml_document_add_empty_element (xml, NULL, "shared");
                break;
        }
        e_xml_document_end_element (xml); /* lockscope */
 
        e_xml_document_start_element (xml, NULL, "locktype");
-       e_xml_document_start_element (xml, NULL, "write");
-       e_xml_document_end_element (xml); /* write */
+       e_xml_document_add_empty_element (xml, NULL, "write");
        e_xml_document_end_element (xml); /* locktype */
 
        e_xml_document_start_text_element (xml, NULL, "owner");
@@ -3830,7 +3935,7 @@ e_webdav_session_acl_sync (EWebDAVSession *webdav,
 
        bytes = e_soup_session_send_request_simple_sync (E_SOUP_SESSION (webdav), request, cancellable, 
error);
 
-       success = !e_webdav_session_replace_with_detailed_error (webdav, request, bytes, TRUE, _("Failed to 
get properties"), error) &&
+       success = !e_webdav_session_replace_with_detailed_error (webdav, request, bytes, TRUE, _("Failed to 
get access control list"), error) &&
                bytes != NULL;
 
        if (bytes)
@@ -3880,8 +3985,7 @@ e_webdav_session_get_supported_privilege_set_sync (EWebDAVSession *webdav,
        g_return_val_if_fail (xml != NULL, FALSE);
 
        e_xml_document_start_element (xml, NULL, "prop");
-       e_xml_document_start_element (xml, NULL, "supported-privilege-set");
-       e_xml_document_end_element (xml);
+       e_xml_document_add_empty_element (xml, NULL, "supported-privilege-set");
        e_xml_document_end_element (xml); /* prop */
 
        success = e_webdav_session_propfind_sync (webdav, uri, E_WEBDAV_DEPTH_THIS, xml,
@@ -4013,8 +4117,7 @@ e_webdav_session_get_current_user_privilege_set_sync (EWebDAVSession *webdav,
        g_return_val_if_fail (xml != NULL, FALSE);
 
        e_xml_document_start_element (xml, NULL, "prop");
-       e_xml_document_start_element (xml, NULL, "current-user-privilege-set");
-       e_xml_document_end_element (xml);
+       e_xml_document_add_empty_element (xml, NULL, "current-user-privilege-set");
        e_xml_document_end_element (xml); /* prop */
 
        success = e_webdav_session_propfind_sync (webdav, uri, E_WEBDAV_DEPTH_THIS, xml,
@@ -4288,8 +4391,7 @@ e_webdav_session_get_acl_sync (EWebDAVSession *webdav,
        g_return_val_if_fail (xml != NULL, FALSE);
 
        e_xml_document_start_element (xml, NULL, "prop");
-       e_xml_document_start_element (xml, NULL, "acl");
-       e_xml_document_end_element (xml);
+       e_xml_document_add_empty_element (xml, NULL, "acl");
        e_xml_document_end_element (xml); /* prop */
 
        success = e_webdav_session_propfind_sync (webdav, uri, E_WEBDAV_DEPTH_THIS, xml,
@@ -4399,8 +4501,7 @@ e_webdav_session_get_acl_restrictions_sync (EWebDAVSession *webdav,
        g_return_val_if_fail (xml != NULL, FALSE);
 
        e_xml_document_start_element (xml, NULL, "prop");
-       e_xml_document_start_element (xml, NULL, "acl-restrictions");
-       e_xml_document_end_element (xml);
+       e_xml_document_add_empty_element (xml, NULL, "acl-restrictions");
        e_xml_document_end_element (xml); /* prop */
 
        ard.out_restrictions = out_restrictions;
@@ -4496,8 +4597,7 @@ e_webdav_session_get_principal_collection_set_sync (EWebDAVSession *webdav,
        g_return_val_if_fail (xml != NULL, FALSE);
 
        e_xml_document_start_element (xml, NULL, "prop");
-       e_xml_document_start_element (xml, NULL, "principal-collection-set");
-       e_xml_document_end_element (xml);
+       e_xml_document_add_empty_element (xml, NULL, "principal-collection-set");
        e_xml_document_end_element (xml); /* prop */
 
        success = e_webdav_session_propfind_sync (webdav, uri, E_WEBDAV_DEPTH_THIS, xml,
@@ -4622,28 +4722,23 @@ e_webdav_session_set_acl_sync (EWebDAVSession *webdav,
                        e_xml_document_end_element (xml);
                        break;
                case E_WEBDAV_ACE_PRINCIPAL_ALL:
-                       e_xml_document_start_element (xml, NULL, "all");
-                       e_xml_document_end_element (xml);
+                       e_xml_document_add_empty_element (xml, NULL, "all");
                        break;
                case E_WEBDAV_ACE_PRINCIPAL_AUTHENTICATED:
-                       e_xml_document_start_element (xml, NULL, "authenticated");
-                       e_xml_document_end_element (xml);
+                       e_xml_document_add_empty_element (xml, NULL, "authenticated");
                        break;
                case E_WEBDAV_ACE_PRINCIPAL_UNAUTHENTICATED:
-                       e_xml_document_start_element (xml, NULL, "unauthenticated");
-                       e_xml_document_end_element (xml);
+                       e_xml_document_add_empty_element (xml, NULL, "unauthenticated");
                        break;
                case E_WEBDAV_ACE_PRINCIPAL_PROPERTY:
                        g_warn_if_reached ();
                        break;
                case E_WEBDAV_ACE_PRINCIPAL_SELF:
-                       e_xml_document_start_element (xml, NULL, "self");
-                       e_xml_document_end_element (xml);
+                       e_xml_document_add_empty_element (xml, NULL, "self");
                        break;
                case E_WEBDAV_ACE_PRINCIPAL_OWNER:
                        e_xml_document_start_element (xml, NULL, "property");
-                       e_xml_document_start_element (xml, NULL, "owner");
-                       e_xml_document_end_element (xml);
+                       e_xml_document_add_empty_element (xml, NULL, "owner");
                        e_xml_document_end_element (xml);
                        break;
 
@@ -4671,8 +4766,7 @@ e_webdav_session_set_acl_sync (EWebDAVSession *webdav,
                        }
 
                        e_xml_document_start_element (xml, NULL, "privilege");
-                       e_xml_document_start_element (xml, privilege->ns_uri, privilege->name);
-                       e_xml_document_end_element (xml);
+                       e_xml_document_add_empty_element (xml, privilege->ns_uri, privilege->name);
                        e_xml_document_end_element (xml); /* privilege */
                }
 
@@ -4733,7 +4827,9 @@ e_webdav_session_principal_property_search_cb (EWebDAVSession *webdav,
  * @webdav: an #EWebDAVSession
  * @uri: (nullable): URI to issue the request for, or %NULL to read from #ESource
  * @apply_to_principal_collection_set: whether to apply to principal-collection-set
- * @match_displayname: a string to match DAV:displayname with
+ * @match_ns_uri: (nullable): namespace URI of the property to search in, or %NULL for %E_WEBDAV_NS_DAV
+ * @match_property: name of the property to search in
+ * @match_value: a string value to search for
  * @out_principals: (out) (transfer full) (element-type EWebDAVResource): return location for matching 
principals
  * @cancellable: optional #GCancellable object, or %NULL
  * @error: return location for a #GError, or %NULL
@@ -4741,7 +4837,8 @@ e_webdav_session_principal_property_search_cb (EWebDAVSession *webdav,
  * Issues a DAV:principal-property-search for the @uri, or, in case it's %NULL,
  * for the URI defined in associated #ESource. The DAV:principal-property-search
  * performs a search for all principals whose properties contain character data
- * that matches the search criteria @match_displayname.
+ * that matches the search criteria @match_value in @match_property property
+ * of namespace @match_ns_uri.
  *
  * By default, the function searches all members (at any depth) of the collection
  * identified by the @uri. If @apply_to_principal_collection_set is set to %TRUE,
@@ -4765,7 +4862,9 @@ gboolean
 e_webdav_session_principal_property_search_sync (EWebDAVSession *webdav,
                                                 const gchar *uri,
                                                 gboolean apply_to_principal_collection_set,
-                                                const gchar *match_displayname,
+                                                const gchar *match_ns_uri,
+                                                const gchar *match_property,
+                                                const gchar *match_value,
                                                 GSList **out_principals,
                                                 GCancellable *cancellable,
                                                 GError **error)
@@ -4774,7 +4873,8 @@ e_webdav_session_principal_property_search_sync (EWebDAVSession *webdav,
        gboolean success;
 
        g_return_val_if_fail (E_IS_WEBDAV_SESSION (webdav), FALSE);
-       g_return_val_if_fail (match_displayname != NULL, FALSE);
+       g_return_val_if_fail (match_property != NULL, FALSE);
+       g_return_val_if_fail (match_value != NULL, FALSE);
        g_return_val_if_fail (out_principals != NULL, FALSE);
 
        *out_principals = NULL;
@@ -4783,23 +4883,20 @@ e_webdav_session_principal_property_search_sync (EWebDAVSession *webdav,
        g_return_val_if_fail (xml != NULL, FALSE);
 
        if (apply_to_principal_collection_set) {
-               e_xml_document_start_element (xml, NULL, "apply-to-principal-collection-set");
-               e_xml_document_end_element (xml);
+               e_xml_document_add_empty_element (xml, NULL, "apply-to-principal-collection-set");
        }
 
        e_xml_document_start_element (xml, NULL, "property-search");
        e_xml_document_start_element (xml, NULL, "prop");
-       e_xml_document_start_element (xml, NULL, "displayname");
-       e_xml_document_end_element (xml);
+       e_xml_document_add_empty_element (xml, match_ns_uri, match_property);
        e_xml_document_end_element (xml); /* prop */
        e_xml_document_start_text_element (xml, NULL, "match");
-       e_xml_document_write_string (xml, match_displayname);
+       e_xml_document_write_string (xml, match_value);
        e_xml_document_end_element (xml); /* match */
        e_xml_document_end_element (xml); /* property-search */
 
        e_xml_document_start_element (xml, NULL, "prop");
-       e_xml_document_start_element (xml, NULL, "displayname");
-       e_xml_document_end_element (xml);
+       e_xml_document_add_empty_element (xml, NULL, "displayname");
        e_xml_document_end_element (xml); /* prop */
 
        success = e_webdav_session_report_sync (webdav, uri, E_WEBDAV_DEPTH_THIS, xml,
@@ -4815,7 +4912,7 @@ e_webdav_session_principal_property_search_sync (EWebDAVSession *webdav,
 
 /**
  * e_webdav_session_util_maybe_dequote:
- * @text: (inout): text to dequote
+ * @text: (inout) (nullable): text to dequote
  *
  * Dequotes @text, if it's enclosed in double-quotes. The function
  * changes @text, it doesn't allocate new string. The function does
diff --git a/src/libedataserver/e-webdav-session.h b/src/libedataserver/e-webdav-session.h
index abbec06..8d15080 100644
--- a/src/libedataserver/e-webdav-session.h
+++ b/src/libedataserver/e-webdav-session.h
@@ -63,20 +63,6 @@ G_BEGIN_DECLS
 #define E_WEBDAV_CAPABILITY_CALENDAR_AUTO_SCHEDULE     "calendar-auto-schedule"
 #define E_WEBDAV_CAPABILITY_CALENDAR_PROXY             "calendar-proxy"
 
-#define E_WEBDAV_ALLOW_OPTIONS                 "OPTIONS"
-#define E_WEBDAV_ALLOW_PROPFIND                        "PROPFIND"
-#define E_WEBDAV_ALLOW_REPORT                  "REPORT"
-#define E_WEBDAV_ALLOW_DELETE                  "DELETE"
-#define E_WEBDAV_ALLOW_GET                     "GET"
-#define E_WEBDAV_ALLOW_PUT                     "PUT"
-#define E_WEBDAV_ALLOW_HEAD                    "HEAD"
-#define E_WEBDAV_ALLOW_ACL                     "ACL"
-#define E_WEBDAV_ALLOW_LOCK                    "LOCK"
-#define E_WEBDAV_ALLOW_UNLOCK                  "UNLOCK"
-#define E_WEBDAV_ALLOW_MOVE                    "MOVE"
-#define E_WEBDAV_ALLOW_MKTICKET                        "MKTICKET"
-#define E_WEBDAV_ALLOW_DELTICKET               "DELTICKET"
-
 #define E_WEBDAV_DEPTH_THIS                    "0"
 #define E_WEBDAV_DEPTH_THIS_AND_CHILDREN       "1"
 #define E_WEBDAV_DEPTH_INFINITY                        "infinity"
@@ -369,6 +355,14 @@ gboolean   e_webdav_session_options_sync           (EWebDAVSession *webdav,
                                                         GHashTable **out_allows,
                                                         GCancellable *cancellable,
                                                         GError **error);
+gboolean       e_webdav_session_post_sync              (EWebDAVSession *webdav,
+                                                        const gchar *uri,
+                                                        const gchar *data,
+                                                        gsize data_length,
+                                                        gchar **out_content_type,
+                                                        GByteArray **out_content,
+                                                        GCancellable *cancellable,
+                                                        GError **error);
 gboolean       e_webdav_session_propfind_sync          (EWebDAVSession *webdav,
                                                         const gchar *uri,
                                                         const gchar *depth,
@@ -574,7 +568,9 @@ gboolean    e_webdav_session_principal_property_search_sync
                                                        (EWebDAVSession *webdav,
                                                         const gchar *uri,
                                                         gboolean apply_to_principal_collection_set,
-                                                        const gchar *match_displayname,
+                                                        const gchar *match_ns_uri,
+                                                        const gchar *match_property,
+                                                        const gchar *match_value,
                                                         GSList **out_principals, /* EWebDAVResource * */
                                                         GCancellable *cancellable,
                                                         GError **error);
diff --git a/src/libedataserver/e-xml-document.c b/src/libedataserver/e-xml-document.c
index 9212df4..c195699 100644
--- a/src/libedataserver/e-xml-document.c
+++ b/src/libedataserver/e-xml-document.c
@@ -424,6 +424,33 @@ e_xml_document_end_element (EXmlDocument *xml)
 }
 
 /**
+ * e_xml_document_add_empty_element:
+ * @xml: an #EXmlDocument
+ * @ns_href: (nullable): optional namespace href for the new element, or %NULL
+ * @name: name of the new element
+ *
+ * Adds an empty element, which is an element with no attribute and no value.
+ *
+ * It's the same as calling e_xml_document_start_element() immediately
+ * followed by e_xml_document_end_element().
+ *
+ * Since: 3.26
+ **/
+void
+e_xml_document_add_empty_element (EXmlDocument *xml,
+                                 const gchar *ns_href,
+                                 const gchar *name)
+{
+       g_return_if_fail (E_IS_XML_DOCUMENT (xml));
+       g_return_if_fail (name != NULL);
+       g_return_if_fail (*name);
+       g_return_if_fail (xml->priv->current_element != NULL);
+
+       e_xml_document_start_element (xml, ns_href, name);
+       e_xml_document_end_element (xml);
+}
+
+/**
  * e_xml_document_add_attribute:
  * @xml: an #EXmlDocument
  * @ns_href: (nullable): optional namespace href for the new attribute, or %NULL
@@ -456,6 +483,104 @@ e_xml_document_add_attribute (EXmlDocument *xml,
 }
 
 /**
+ * e_xml_document_add_attribute_int:
+ * @xml: an #EXmlDocument
+ * @ns_href: (nullable): optional namespace href for the new attribute, or %NULL
+ * @name: name of the attribute
+ * @value: integer value of the attribute
+ *
+ * Adds a new attribute with an integer value to the current element.
+ * Use %NULL @ns_href, to use the default namespace, otherwise either previously
+ * added namespace with the same href from e_xml_document_add_namespaces() is picked,
+ * or a new namespace with generated prefix is added.
+ *
+ * Since: 3.26
+ **/
+void
+e_xml_document_add_attribute_int (EXmlDocument *xml,
+                                 const gchar *ns_href,
+                                 const gchar *name,
+                                 gint64 value)
+{
+       gchar *strvalue;
+
+       g_return_if_fail (E_IS_XML_DOCUMENT (xml));
+       g_return_if_fail (xml->priv->current_element != NULL);
+       g_return_if_fail (name != NULL);
+
+       strvalue = g_strdup_printf ("%" G_GINT64_FORMAT, value);
+       e_xml_document_add_attribute (xml, ns_href, name, strvalue);
+       g_free (strvalue);
+}
+
+/**
+ * e_xml_document_add_attribute_double:
+ * @xml: an #EXmlDocument
+ * @ns_href: (nullable): optional namespace href for the new attribute, or %NULL
+ * @name: name of the attribute
+ * @value: double value of the attribute
+ *
+ * Adds a new attribute with a double value to the current element.
+ * Use %NULL @ns_href, to use the default namespace, otherwise either previously
+ * added namespace with the same href from e_xml_document_add_namespaces() is picked,
+ * or a new namespace with generated prefix is added.
+ *
+ * Since: 3.26
+ **/
+void
+e_xml_document_add_attribute_double (EXmlDocument *xml,
+                                    const gchar *ns_href,
+                                    const gchar *name,
+                                    gdouble value)
+{
+       gchar *strvalue;
+
+       g_return_if_fail (E_IS_XML_DOCUMENT (xml));
+       g_return_if_fail (xml->priv->current_element != NULL);
+       g_return_if_fail (name != NULL);
+
+       strvalue = g_strdup_printf ("%f", value);
+       e_xml_document_add_attribute (xml, ns_href, name, strvalue);
+       g_free (strvalue);
+}
+
+/**
+ * e_xml_document_add_attribute_time:
+ * @xml: an #EXmlDocument
+ * @ns_href: (nullable): optional namespace href for the new attribute, or %NULL
+ * @name: name of the attribute
+ * @value: time_t value of the attribute
+ *
+ * Adds a new attribute with a time_t value in ISO 8601 format to the current element.
+ * The format is "YYYY-MM-DDTHH:MM:SSZ".
+ * Use %NULL @ns_href, to use the default namespace, otherwise either previously
+ * added namespace with the same href from e_xml_document_add_namespaces() is picked,
+ * or a new namespace with generated prefix is added.
+ *
+ * Since: 3.26
+ **/
+void
+e_xml_document_add_attribute_time (EXmlDocument *xml,
+                                  const gchar *ns_href,
+                                  const gchar *name,
+                                  time_t value)
+{
+       GTimeVal tv;
+       gchar *strvalue;
+
+       g_return_if_fail (E_IS_XML_DOCUMENT (xml));
+       g_return_if_fail (xml->priv->current_element != NULL);
+       g_return_if_fail (name != NULL);
+
+       tv.tv_usec = 0;
+       tv.tv_sec = value;
+
+       strvalue = g_time_val_to_iso8601 (&tv);
+       e_xml_document_add_attribute (xml, ns_href, name, strvalue);
+       g_free (strvalue);
+}
+
+/**
  * e_xml_document_write_int:
  * @xml: an #EXmlDocument
  * @value: value to write as the content
@@ -533,6 +658,7 @@ e_xml_document_write_base64 (EXmlDocument *xml,
  * @value: value to write as the content
  *
  * Writes @value in ISO 8601 format as content of the current element.
+ * The format is "YYYY-MM-DDTHH:MM:SSZ".
  *
  * Since: 3.26
  **/
diff --git a/src/libedataserver/e-xml-document.h b/src/libedataserver/e-xml-document.h
index afadf2e..e1fcf94 100644
--- a/src/libedataserver/e-xml-document.h
+++ b/src/libedataserver/e-xml-document.h
@@ -91,10 +91,29 @@ void                e_xml_document_start_text_element
                                                 const gchar *ns_href,
                                                 const gchar *name);
 void           e_xml_document_end_element      (EXmlDocument *xml);
+void           e_xml_document_add_empty_element
+                                               (EXmlDocument *xml,
+                                                const gchar *ns_href,
+                                                const gchar *name);
 void           e_xml_document_add_attribute    (EXmlDocument *xml,
                                                 const gchar *ns_href,
                                                 const gchar *name,
                                                 const gchar *value);
+void           e_xml_document_add_attribute_int
+                                               (EXmlDocument *xml,
+                                                const gchar *ns_href,
+                                                const gchar *name,
+                                                gint64 value);
+void           e_xml_document_add_attribute_double
+                                               (EXmlDocument *xml,
+                                                const gchar *ns_href,
+                                                const gchar *name,
+                                                gdouble value);
+void           e_xml_document_add_attribute_time
+                                               (EXmlDocument *xml,
+                                                const gchar *ns_href,
+                                                const gchar *name,
+                                                time_t value);
 void           e_xml_document_write_int        (EXmlDocument *xml,
                                                 gint64 value);
 void           e_xml_document_write_double     (EXmlDocument *xml,
diff --git a/tests/libedata-cal/test-cal-meta-backend.c b/tests/libedata-cal/test-cal-meta-backend.c
index 8524c60..63e481d 100644
--- a/tests/libedata-cal/test-cal-meta-backend.c
+++ b/tests/libedata-cal/test-cal-meta-backend.c
@@ -318,6 +318,7 @@ e_cal_meta_backend_test_disconnect_sync (ECalMetaBackend *meta_backend,
 static gboolean
 e_cal_meta_backend_test_get_changes_sync (ECalMetaBackend *meta_backend,
                                          const gchar *last_sync_tag,
+                                         gboolean is_repeat,
                                          gchar **out_new_sync_tag,
                                          gboolean *out_repeat,
                                          GSList **out_created_objects,
@@ -352,7 +353,7 @@ e_cal_meta_backend_test_get_changes_sync (ECalMetaBackend *meta_backend,
        /* Nothing to do here at the moment, left the work to the parent class,
           which calls list_existing_sync() internally. */
        return E_CAL_META_BACKEND_CLASS (e_cal_meta_backend_test_parent_class)->get_changes_sync 
(meta_backend,
-               last_sync_tag, out_new_sync_tag, out_repeat, out_created_objects,
+               last_sync_tag, is_repeat, out_new_sync_tag, out_repeat, out_created_objects,
                out_modified_objects, out_removed_objects, cancellable, error);
 }
 
@@ -395,7 +396,7 @@ e_cal_meta_backend_test_list_existing_sync (ECalMetaBackend *meta_backend,
                uid = icalcomponent_get_uid (icalcomp);
                revision = e_cal_cache_dup_component_revision (cal_cache, icalcomp);
 
-               nfo = e_cal_meta_backend_info_new (uid, NULL, revision, NULL);
+               nfo = e_cal_meta_backend_info_new (uid, revision, NULL, NULL);
                *out_existing_objects = g_slist_prepend (*out_existing_objects, nfo);
 
                g_free (revision);
@@ -413,6 +414,7 @@ e_cal_meta_backend_test_save_component_sync (ECalMetaBackend *meta_backend,
                                             const GSList *instances,
                                             const gchar *extra,
                                             gchar **out_new_uid,
+                                            gchar **out_new_extra,
                                             GCancellable *cancellable,
                                             GError **error)
 {
@@ -476,6 +478,7 @@ e_cal_meta_backend_test_save_component_sync (ECalMetaBackend *meta_backend,
 static gboolean
 e_cal_meta_backend_test_load_component_sync (ECalMetaBackend *meta_backend,
                                             const gchar *uid,
+                                            const gchar *extra,
                                             icalcomponent **out_instances,
                                             gchar **out_extra,
                                             GCancellable *cancellable,


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