[evolution-data-server] EBookBackendWebdav cleanups.



commit d6f4e33c7983601bd283defc4901f4b8273f0a19
Author: Matthew Barnes <mbarnes redhat com>
Date:   Fri Apr 5 09:27:36 2013 -0400

    EBookBackendWebdav cleanups.

 .../backends/webdav/e-book-backend-webdav.c        | 1851 ++++++++++----------
 1 files changed, 924 insertions(+), 927 deletions(-)
---
diff --git a/addressbook/backends/webdav/e-book-backend-webdav.c 
b/addressbook/backends/webdav/e-book-backend-webdav.c
index 1eb71a1..04b7cab 100644
--- a/addressbook/backends/webdav/e-book-backend-webdav.c
+++ b/addressbook/backends/webdav/e-book-backend-webdav.c
@@ -364,101 +364,6 @@ webdav_handle_auth_request (EBookBackendWebdav *webdav)
        }
 }
 
-static void
-e_book_backend_webdav_create_contacts (EBookBackend *backend,
-                                       EDataBook *book,
-                                       guint32 opid,
-                                       GCancellable *cancellable,
-                                       const GSList *vcards)
-{
-       EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
-       EBookBackendWebdavPrivate *priv   = webdav->priv;
-       EContact                  *contact;
-       gchar                     *uid;
-       guint                      status;
-       gchar                     *status_reason = NULL;
-       const gchar               *vcard = (const gchar *) vcards->data;
-       GSList                     added_contacts = {NULL,};
-
-       /* We make the assumption that the vCard list we're passed is always exactly one element long, since 
we haven't specified "bulk-adds"
-        * in our static capability list. This is because there is no clean way to roll back changes in case 
of an error. */
-       if (vcards->next != NULL) {
-               e_data_book_respond_create_contacts (
-                       book, opid,
-                       EDB_ERROR_EX (NOT_SUPPORTED,
-                       _("The backend does not support bulk additions")),
-                       NULL);
-               return;
-       }
-
-       if (!e_backend_get_online (E_BACKEND (backend))) {
-               e_data_book_respond_create_contacts (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
-               return;
-       }
-
-       /* do 3 rand() calls to construct a unique ID... poor way but should be
-        * good enough for us */
-       uid = g_strdup_printf (
-               "%s%08X-%08X-%08X.vcf",
-               priv->uri, rand (), rand (), rand ());
-
-       contact = e_contact_new_from_vcard_with_uid (vcard, uid);
-
-       /* kill revision field (might have been set by some other backend) */
-       e_contact_set (contact, E_CONTACT_REV, NULL);
-
-       status = upload_contact (webdav, contact, &status_reason, cancellable);
-       if (status != 201 && status != 204) {
-               g_object_unref (contact);
-               if (status == 401 || status == 407) {
-                       e_data_book_respond_create_contacts (book, opid, webdav_handle_auth_request (webdav), 
NULL);
-               } else {
-                       e_data_book_respond_create_contacts (
-                               book, opid,
-                               e_data_book_create_error_fmt (
-                               E_DATA_BOOK_STATUS_OTHER_ERROR,
-                               _("Create resource '%s' failed with HTTP status %d (%s)"),
-                               uid, status, status_reason),
-                                       NULL);
-               }
-               g_free (uid);
-               g_free (status_reason);
-               return;
-       }
-
-       g_free (status_reason);
-
-       /* PUT request didn't return an etag? try downloading to get one */
-       if (e_contact_get_const (contact, E_CONTACT_REV) == NULL) {
-               const gchar *new_uid;
-               EContact *new_contact;
-
-               g_warning ("Server didn't return etag for new address resource");
-               new_uid = e_contact_get_const (contact, E_CONTACT_UID);
-               new_contact = download_contact (webdav, new_uid, cancellable);
-               g_object_unref (contact);
-
-               if (new_contact == NULL) {
-                       e_data_book_respond_create_contacts (
-                               book, opid,
-                               EDB_ERROR (OTHER_ERROR), NULL);
-                       g_free (uid);
-                       return;
-               }
-               contact = new_contact;
-       }
-
-       g_mutex_lock (&priv->cache_lock);
-       e_book_backend_cache_add_contact (priv->cache, contact);
-       g_mutex_unlock (&priv->cache_lock);
-
-       added_contacts.data = contact;
-       e_data_book_respond_create_contacts (book, opid, EDB_ERROR (SUCCESS), &added_contacts);
-
-       g_object_unref (contact);
-       g_free (uid);
-}
-
 static guint
 delete_contact (EBookBackendWebdav *webdav,
                 const gchar *uri,
@@ -477,773 +382,1094 @@ delete_contact (EBookBackendWebdav *webdav,
        return status;
 }
 
-static void
-e_book_backend_webdav_remove_contacts (EBookBackend *backend,
-                                       EDataBook *book,
-                                       guint32 opid,
-                                       GCancellable *cancellable,
-                                       const GSList *id_list)
+typedef struct parser_strings_t {
+       const xmlChar *multistatus;
+       const xmlChar *dav;
+       const xmlChar *href;
+       const xmlChar *response;
+       const xmlChar *propstat;
+       const xmlChar *prop;
+       const xmlChar *getetag;
+} parser_strings_t;
+
+typedef struct response_element_t response_element_t;
+struct response_element_t {
+       xmlChar            *href;
+       xmlChar            *etag;
+       response_element_t *next;
+};
+
+static response_element_t *
+parse_response_tag (const parser_strings_t *strings,
+                    xmlTextReaderPtr reader)
 {
-       EBookBackendWebdav        *webdav      = E_BOOK_BACKEND_WEBDAV (backend);
-       EBookBackendWebdavPrivate *priv        = webdav->priv;
-       gchar                     *uid         = id_list->data;
-       GSList                     deleted_ids = {NULL,};
-       guint                      status;
+       xmlChar            *href  = NULL;
+       xmlChar            *etag  = NULL;
+       gint                 depth = xmlTextReaderDepth (reader);
+       response_element_t *element;
 
-       if (!e_backend_get_online (E_BACKEND (backend))) {
-               e_data_book_respond_remove_contacts (
-                       book, opid,
-                       EDB_ERROR (REPOSITORY_OFFLINE), NULL);
-               return;
-       }
+       while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > depth) {
+               const xmlChar *tag_name;
+               if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
+                       continue;
 
-       /* We make the assumption that the ID list we're passed is always exactly one element long, since we 
haven't specified "bulk-removes"
-        * in our static capability list. */
-       if (id_list->next != NULL) {
-               e_data_book_respond_remove_contacts (
-                       book, opid,
-                       EDB_ERROR_EX (NOT_SUPPORTED,
-                       _("The backend does not support bulk removals")),
-                       NULL);
-               return;
-       }
+               if (xmlTextReaderConstNamespaceUri (reader) != strings->dav)
+                       continue;
 
-       status = delete_contact (webdav, uid, cancellable);
-       if (status != 204) {
-               if (status == 401 || status == 407) {
-                       e_data_book_respond_remove_contacts (
-                               book, opid,
-                               webdav_handle_auth_request (webdav), NULL);
-               } else {
-                       g_warning ("DELETE failed with HTTP status %d", status);
-                       e_data_book_respond_remove_contacts (
-                               book, opid,
-                               e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
-                               _("DELETE failed with HTTP status %d"), status),
-                               NULL);
+               tag_name = xmlTextReaderConstLocalName (reader);
+               if (tag_name == strings->href) {
+                       if (href != NULL) {
+                               /* multiple href elements?!? */
+                               xmlFree (href);
+                       }
+                       href = xmlTextReaderReadString (reader);
+               } else if (tag_name == strings->propstat) {
+                       /* find <propstat><prop><getetag> hierarchy */
+                       gint depth2 = xmlTextReaderDepth (reader);
+                       while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > depth2) {
+                               gint depth3;
+                               if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
+                                       continue;
+
+                               if (xmlTextReaderConstNamespaceUri (reader) != strings->dav
+                                               || xmlTextReaderConstLocalName (reader) != strings->prop)
+                                       continue;
+
+                               depth3 = xmlTextReaderDepth (reader);
+                               while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > 
depth3) {
+                                       if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
+                                               continue;
+
+                                       if (xmlTextReaderConstNamespaceUri (reader) != strings->dav
+                                                       || xmlTextReaderConstLocalName (reader)
+                                                       != strings->getetag)
+                                               continue;
+
+                                       if (etag != NULL) {
+                                               /* multiple etag elements?!? */
+                                               xmlFree (etag);
+                                       }
+                                       etag = xmlTextReaderReadString (reader);
+                               }
+                       }
                }
-               return;
        }
 
-       g_mutex_lock (&priv->cache_lock);
-       e_book_backend_cache_remove_contact (priv->cache, uid);
-       g_mutex_unlock (&priv->cache_lock);
+       if (href == NULL) {
+               g_warning ("webdav returned response element without href");
+               return NULL;
+       }
 
-       deleted_ids.data = uid;
-       e_data_book_respond_remove_contacts (book, opid, EDB_ERROR (SUCCESS), &deleted_ids);
+       /* append element to list */
+       element = g_malloc (sizeof (element[0]));
+       element->href = href;
+       element->etag = etag;
+       return element;
 }
 
-static void
-e_book_backend_webdav_modify_contacts (EBookBackend *backend,
-                                       EDataBook *book,
-                                       guint32 opid,
-                                       GCancellable *cancellable,
-                                       const GSList *vcards)
+static response_element_t *
+parse_propfind_response (xmlTextReaderPtr reader)
 {
-       EBookBackendWebdav        *webdav  = E_BOOK_BACKEND_WEBDAV (backend);
-       EBookBackendWebdavPrivate *priv    = webdav->priv;
-       EContact                  *contact;
-       GSList                     modified_contacts = {NULL,};
-       const gchar                *uid;
-       const gchar                *etag;
-       guint status;
-       gchar *status_reason = NULL;
-       const gchar *vcard = vcards->data;
+       parser_strings_t    strings;
+       response_element_t *elements;
 
-       if (!e_backend_get_online (E_BACKEND (backend))) {
-               e_data_book_respond_create_contacts (
-                       book, opid,
-                       EDB_ERROR (REPOSITORY_OFFLINE), NULL);
-               return;
+       /* get internalized versions of some strings to avoid strcmp while
+        * parsing */
+       strings.multistatus
+               = xmlTextReaderConstString (reader, BAD_CAST "multistatus");
+       strings.dav         = xmlTextReaderConstString (reader, BAD_CAST "DAV:");
+       strings.href        = xmlTextReaderConstString (reader, BAD_CAST "href");
+       strings.response    = xmlTextReaderConstString (reader, BAD_CAST "response");
+       strings.propstat    = xmlTextReaderConstString (reader, BAD_CAST "propstat");
+       strings.prop        = xmlTextReaderConstString (reader, BAD_CAST "prop");
+       strings.getetag     = xmlTextReaderConstString (reader, BAD_CAST "getetag");
+
+       while (xmlTextReaderRead (reader) == 1 && xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT) {
        }
 
-       /* We make the assumption that the vCard list we're passed is always exactly one element long, since 
we haven't specified "bulk-modifies"
-        * in our static capability list. This is because there is no clean way to roll back changes in case 
of an error. */
-       if (vcards->next != NULL) {
-               e_data_book_respond_modify_contacts (
-                       book, opid,
-                       EDB_ERROR_EX (
-                               NOT_SUPPORTED,
-                               _("The backend does not support bulk modifications")),
-                       NULL);
-               return;
+       if (xmlTextReaderConstLocalName (reader) != strings.multistatus
+                       || xmlTextReaderConstNamespaceUri (reader) != strings.dav) {
+               g_warning ("webdav PROPFIND result is not <DAV:multistatus>");
+               return NULL;
        }
 
-       /* modify contact */
-       contact = e_contact_new_from_vcard (vcard);
-       status = upload_contact (webdav, contact, &status_reason, cancellable);
-       if (status != 201 && status != 204) {
-               g_object_unref (contact);
-               if (status == 401 || status == 407) {
-                       e_data_book_respond_modify_contacts (book, opid, webdav_handle_auth_request (webdav), 
NULL);
-                       g_free (status_reason);
-                       return;
-               }
-               /* data changed on server while we were editing */
-               if (status == 412) {
-                       /* too bad no special error code in evolution for this... */
-                       e_data_book_respond_modify_contacts (book, opid,
-                               e_data_book_create_error_fmt (
-                               E_DATA_BOOK_STATUS_OTHER_ERROR,
-                               _("Contact on server changed -> not modifying")),
-                               NULL);
-                       g_free (status_reason);
-                       return;
-               }
+       elements = NULL;
 
-               e_data_book_respond_modify_contacts (book, opid,
-                       e_data_book_create_error_fmt (
-                       E_DATA_BOOK_STATUS_OTHER_ERROR,
-                       _("Modify contact failed with HTTP status %d (%s)"),
-                       status, status_reason),
-                       NULL);
-               g_free (status_reason);
-               return;
+       /* parse all DAV:response tags */
+       while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > 0) {
+               response_element_t *element;
+
+               if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
+                       continue;
+
+               if (xmlTextReaderConstLocalName (reader) != strings.response
+                               || xmlTextReaderConstNamespaceUri (reader) != strings.dav)
+                       continue;
+
+               element = parse_response_tag (&strings, reader);
+               if (element == NULL)
+                       continue;
+
+               element->next = elements;
+               elements      = element;
        }
 
-       g_free (status_reason);
+       return elements;
+}
 
-       uid = e_contact_get_const (contact, E_CONTACT_UID);
-       g_mutex_lock (&priv->cache_lock);
-       e_book_backend_cache_remove_contact (priv->cache, uid);
+static SoupMessage *
+send_propfind (EBookBackendWebdav *webdav,
+               GCancellable *cancellable)
+{
+       SoupMessage               *message;
+       EBookBackendWebdavPrivate *priv    = webdav->priv;
+       const gchar               *request =
+               "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+               "<propfind xmlns=\"DAV:\"><prop><getetag/></prop></propfind>";
 
-       etag = e_contact_get_const (contact, E_CONTACT_REV);
+       message = soup_message_new (SOUP_METHOD_PROPFIND, priv->uri);
+       soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
+       soup_message_headers_append (message->request_headers, "Connection", "close");
+       soup_message_headers_append (message->request_headers, "Depth", "1");
+       soup_message_set_request (
+               message, "text/xml", SOUP_MEMORY_TEMPORARY,
+               (gchar *) request, strlen (request));
 
-       /* PUT request didn't return an etag? try downloading to get one */
-       if (etag == NULL || (etag[0] == 'W' && etag[1] == '/')) {
-               EContact *new_contact;
+       send_and_handle_ssl (webdav, message, cancellable);
 
-               g_warning ("Server didn't return etag for modified address resource");
-               new_contact = download_contact (webdav, uid, cancellable);
-               if (new_contact != NULL) {
-                       contact = new_contact;
-               }
+       return message;
+}
+
+static xmlXPathObjectPtr
+xpath_eval (xmlXPathContextPtr ctx,
+            const gchar *format,
+            ...)
+{
+       xmlXPathObjectPtr  result;
+       va_list            args;
+       gchar              *expr;
+
+       if (ctx == NULL) {
+               return NULL;
        }
-       e_book_backend_cache_add_contact (priv->cache, contact);
-       g_mutex_unlock (&priv->cache_lock);
 
-       modified_contacts.data = contact;
-       e_data_book_respond_modify_contacts (book, opid, EDB_ERROR (SUCCESS), &modified_contacts);
+       va_start (args, format);
+       expr = g_strdup_vprintf (format, args);
+       va_end (args);
 
-       g_object_unref (contact);
+       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;
 }
 
-static void
-e_book_backend_webdav_get_contact (EBookBackend *backend,
-                                   EDataBook *book,
-                                   guint32 opid,
-                                   GCancellable *cancellable,
-                                   const gchar *uid)
+static gchar *
+xp_object_get_string (xmlXPathObjectPtr result)
 {
-       EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
-       EBookBackendWebdavPrivate *priv   = webdav->priv;
-       EContact                  *contact;
-       gchar                      *vcard;
+       gchar *ret = NULL;
 
-       if (!e_backend_get_online (E_BACKEND (backend))) {
-               g_mutex_lock (&priv->cache_lock);
-               contact = e_book_backend_cache_get_contact (priv->cache, uid);
-               g_mutex_unlock (&priv->cache_lock);
-       } else {
-               contact = download_contact (webdav, uid, cancellable);
-               /* update cache as we possibly have changes */
-               if (contact != NULL) {
-                       g_mutex_lock (&priv->cache_lock);
-                       e_book_backend_cache_remove_contact (priv->cache, uid);
-                       e_book_backend_cache_add_contact (priv->cache, contact);
-                       g_mutex_unlock (&priv->cache_lock);
-               }
-       }
+       if (result == NULL)
+               return ret;
 
-       if (contact == NULL) {
-               e_data_book_respond_get_contact (book, opid, EDB_ERROR (CONTACT_NOT_FOUND), NULL);
-               return;
+       if (result->type == XPATH_STRING) {
+               ret = g_strdup ((gchar *) result->stringval);
        }
 
-       vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
-       e_data_book_respond_get_contact (book, opid, EDB_ERROR (SUCCESS), vcard);
-       g_free (vcard);
-       g_object_unref (contact);
+       xmlXPathFreeObject (result);
+       return ret;
 }
 
-typedef struct parser_strings_t {
-       const xmlChar *multistatus;
-       const xmlChar *dav;
-       const xmlChar *href;
-       const xmlChar *response;
-       const xmlChar *propstat;
-       const xmlChar *prop;
-       const xmlChar *getetag;
-} parser_strings_t;
+static guint
+xp_object_get_status (xmlXPathObjectPtr result)
+{
+       gboolean res;
+       guint    ret = 0;
 
-typedef struct response_element_t response_element_t;
-struct response_element_t {
-       xmlChar            *href;
-       xmlChar            *etag;
-       response_element_t *next;
-};
+       if (result == NULL)
+               return ret;
 
-static response_element_t *
-parse_response_tag (const parser_strings_t *strings,
-                    xmlTextReaderPtr reader)
+       if (result->type == XPATH_STRING) {
+               res = soup_headers_parse_status_line ((gchar *) result->stringval, NULL, &ret, NULL);
+               if (!res) {
+                       ret = 0;
+               }
+       }
+
+       xmlXPathFreeObject (result);
+       return ret;
+}
+
+static gboolean
+check_addressbook_changed (EBookBackendWebdav *webdav,
+                           gchar **new_ctag,
+                           GCancellable *cancellable)
 {
-       xmlChar            *href  = NULL;
-       xmlChar            *etag  = NULL;
-       gint                 depth = xmlTextReaderDepth (reader);
-       response_element_t *element;
+       gboolean res = TRUE;
+       const gchar *request = "<?xml version=\"1.0\" encoding=\"utf-8\"?><propfind 
xmlns=\"DAV:\"><prop><getctag/></prop></propfind>";
+       EBookBackendWebdavPrivate *priv;
+       SoupMessage *message;
 
-       while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > depth) {
-               const xmlChar *tag_name;
-               if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
-                       continue;
+       g_return_val_if_fail (webdav != NULL, TRUE);
+       g_return_val_if_fail (new_ctag != NULL, TRUE);
 
-               if (xmlTextReaderConstNamespaceUri (reader) != strings->dav)
-                       continue;
+       *new_ctag = NULL;
+       priv = webdav->priv;
 
-               tag_name = xmlTextReaderConstLocalName (reader);
-               if (tag_name == strings->href) {
-                       if (href != NULL) {
-                               /* multiple href elements?!? */
-                               xmlFree (href);
-                       }
-                       href = xmlTextReaderReadString (reader);
-               } else if (tag_name == strings->propstat) {
-                       /* find <propstat><prop><getetag> hierarchy */
-                       gint depth2 = xmlTextReaderDepth (reader);
-                       while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > depth2) {
-                               gint depth3;
-                               if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
-                                       continue;
+       if (!priv->supports_getctag)
+               return TRUE;
 
-                               if (xmlTextReaderConstNamespaceUri (reader) != strings->dav
-                                               || xmlTextReaderConstLocalName (reader) != strings->prop)
-                                       continue;
+       priv->supports_getctag = FALSE;
 
-                               depth3 = xmlTextReaderDepth (reader);
-                               while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > 
depth3) {
-                                       if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
-                                               continue;
+       message = soup_message_new (SOUP_METHOD_PROPFIND, priv->uri);
+       if (!message)
+               return TRUE;
 
-                                       if (xmlTextReaderConstNamespaceUri (reader) != strings->dav
-                                                       || xmlTextReaderConstLocalName (reader)
-                                                       != strings->getetag)
-                                               continue;
+       soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
+       soup_message_headers_append (message->request_headers, "Connection", "close");
+       soup_message_headers_append (message->request_headers, "Depth", "0");
+       soup_message_set_request (message, "text/xml", SOUP_MEMORY_TEMPORARY, (gchar *) request, strlen 
(request));
+       send_and_handle_ssl (webdav, message, cancellable);
 
-                                       if (etag != NULL) {
-                                               /* multiple etag elements?!? */
-                                               xmlFree (etag);
+       if (message->status_code == 207 && message->response_body) {
+               xmlDocPtr xml;
+
+               xml = xmlReadMemory (message->response_body->data, message->response_body->length, NULL, 
NULL, XML_PARSE_NOWARNING);
+               if (xml) {
+                       const gchar *GETCTAG_XPATH_STATUS = 
"string(/D:multistatus/D:response/D:propstat/D:prop/D:getctag/../../D:status)";
+                       const gchar *GETCTAG_XPATH_VALUE = 
"string(/D:multistatus/D:response/D:propstat/D:prop/D:getctag)";
+                       xmlXPathContextPtr xpctx;
+
+                       xpctx = xmlXPathNewContext (xml);
+                       xmlXPathRegisterNs (xpctx, (xmlChar *) "D", (xmlChar *) "DAV:");
+
+                       if (xp_object_get_status (xpath_eval (xpctx, GETCTAG_XPATH_STATUS)) == 200) {
+                               gchar *txt = xp_object_get_string (xpath_eval (xpctx, GETCTAG_XPATH_VALUE));
+
+                               if (txt && *txt) {
+                                       gint len = strlen (txt);
+
+                                       if (*txt == '\"' && len > 2 && txt[len - 1] == '\"') {
+                                               /* dequote */
+                                               *new_ctag = g_strndup (txt + 1, len - 2);
+                                       } else {
+                                               *new_ctag = txt;
+                                               txt = NULL;
+                                       }
+
+                                       if (*new_ctag) {
+                                               const gchar *my_ctag;
+
+                                               g_mutex_lock (&priv->cache_lock);
+                                               my_ctag = e_file_cache_get_object (E_FILE_CACHE 
(priv->cache), WEBDAV_CTAG_KEY);
+                                               res = !my_ctag || !g_str_equal (my_ctag, *new_ctag);
+                                               priv->supports_getctag = TRUE;
+                                               g_mutex_unlock (&priv->cache_lock);
                                        }
-                                       etag = xmlTextReaderReadString (reader);
                                }
+
+                               g_free (txt);
+                       }
+
+                       xmlXPathFreeContext (xpctx);
+                       xmlFreeDoc (xml);
+               }
+       }
+
+       g_object_unref (message);
+
+       return res;
+}
+
+static GError *
+download_contacts (EBookBackendWebdav *webdav,
+                   EFlag *running,
+                   EDataBookView *book_view,
+                   GCancellable *cancellable)
+{
+       EBookBackendWebdavPrivate *priv = webdav->priv;
+       EBookBackend              *book_backend;
+       SoupMessage               *message;
+       guint                      status;
+       xmlTextReaderPtr           reader;
+       response_element_t        *elements;
+       response_element_t        *element;
+       response_element_t        *next;
+       gint                        count;
+       gint                        i;
+       gchar                     *new_ctag = NULL;
+
+       g_mutex_lock (&priv->update_lock);
+
+       if (!check_addressbook_changed (webdav, &new_ctag, cancellable)) {
+               g_free (new_ctag);
+               g_mutex_unlock (&priv->update_lock);
+               return EDB_ERROR (SUCCESS);
+       }
+
+       book_backend = E_BOOK_BACKEND (webdav);
+
+       if (book_view != NULL) {
+               e_data_book_view_notify_progress (book_view, -1,
+                               _("Loading Addressbook summary..."));
+       }
+
+       message = send_propfind (webdav, cancellable);
+       status  = message->status_code;
+
+       if (status == 401 || status == 407) {
+               g_object_unref (message);
+               g_free (new_ctag);
+               if (book_view)
+                       e_data_book_view_notify_progress (book_view, -1, NULL);
+               g_mutex_unlock (&priv->update_lock);
+               return webdav_handle_auth_request (webdav);
+       }
+       if (status != 207) {
+               GError *error;
+
+               error = e_data_book_create_error_fmt (
+                       E_DATA_BOOK_STATUS_OTHER_ERROR,
+                       _("PROPFIND on webdav failed with HTTP status %d (%s)"),
+                       status,
+                       message->reason_phrase && *message->reason_phrase ? message->reason_phrase :
+                       (soup_status_get_phrase (message->status_code) ? soup_status_get_phrase 
(message->status_code) : _("Unknown error")));
+
+               g_object_unref (message);
+               g_free (new_ctag);
+
+               if (book_view)
+                       e_data_book_view_notify_progress (book_view, -1, NULL);
+
+               g_mutex_unlock (&priv->update_lock);
+
+               return error;
+       }
+       if (message->response_body == NULL) {
+               g_warning ("No response body in webdav PROPFIND result");
+               g_object_unref (message);
+               g_free (new_ctag);
+
+               if (book_view)
+                       e_data_book_view_notify_progress (book_view, -1, NULL);
+
+               g_mutex_unlock (&priv->update_lock);
+
+               return e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR, _("No response body in 
webdav PROPFIND result"));
+       }
+
+       /* parse response */
+       reader = xmlReaderForMemory (
+               message->response_body->data,
+               message->response_body->length, NULL, NULL,
+               XML_PARSE_NOWARNING);
+
+       elements = parse_propfind_response (reader);
+
+       /* count contacts */
+       count = 0;
+       for (element = elements; element != NULL; element = element->next) {
+               ++count;
+       }
+
+       /* download contacts */
+       i = 0;
+       for (element = elements; element != NULL; element = element->next, ++i) {
+               const gchar  *uri;
+               const gchar *etag;
+               EContact    *contact;
+               gchar *complete_uri;
+
+               /* stop downloading if search was aborted */
+               if (running != NULL && !e_flag_is_set (running))
+                       break;
+
+               if (book_view != NULL) {
+                       gfloat percent = 100.0 / count * i;
+                       gchar buf[100];
+                       snprintf (buf, sizeof (buf), _("Loading Contacts (%d%%)"), (gint) percent);
+                       e_data_book_view_notify_progress (book_view, -1, buf);
+               }
+
+               /* skip collections */
+               uri = (const gchar *) element->href;
+               if (uri[strlen (uri) - 1] == '/')
+                       continue;
+
+               /* uri might be relative, construct complete one */
+               if (uri[0] == '/') {
+                       SoupURI *soup_uri = soup_uri_new (priv->uri);
+                       soup_uri->path    = g_strdup (uri);
+
+                       complete_uri = soup_uri_to_string (soup_uri, FALSE);
+                       soup_uri_free (soup_uri);
+               } else {
+                       complete_uri = g_strdup (uri);
+               }
+
+               etag = (const gchar *) element->etag;
+
+               g_mutex_lock (&priv->cache_lock);
+               contact = e_book_backend_cache_get_contact (priv->cache, complete_uri);
+               g_mutex_unlock (&priv->cache_lock);
+
+               /* download contact if it is not cached or its ETag changed */
+               if (contact == NULL || etag == NULL ||
+                   strcmp (e_contact_get_const (contact, E_CONTACT_REV), etag) != 0) {
+                       contact = download_contact (webdav, complete_uri, cancellable);
+                       if (contact != NULL) {
+                               g_mutex_lock (&priv->cache_lock);
+                               if (e_book_backend_cache_remove_contact (priv->cache, complete_uri))
+                                       e_book_backend_notify_remove (book_backend, complete_uri);
+                               e_book_backend_cache_add_contact (priv->cache, contact);
+                               g_mutex_unlock (&priv->cache_lock);
+                               e_book_backend_notify_update (book_backend, contact);
                        }
                }
+
+               g_free (complete_uri);
+       }
+
+       /* free element list */
+       for (element = elements; element != NULL; element = next) {
+               next = element->next;
+
+               xmlFree (element->href);
+               xmlFree (element->etag);
+               g_free (element);
+       }
+
+       xmlFreeTextReader (reader);
+       g_object_unref (message);
+
+       if (new_ctag) {
+               g_mutex_lock (&priv->cache_lock);
+               if (!e_file_cache_replace_object (E_FILE_CACHE (priv->cache), WEBDAV_CTAG_KEY, new_ctag))
+                       e_file_cache_add_object (E_FILE_CACHE (priv->cache), WEBDAV_CTAG_KEY, new_ctag);
+               g_mutex_unlock (&priv->cache_lock);
        }
+       g_free (new_ctag);
 
-       if (href == NULL) {
-               g_warning ("webdav returned response element without href");
-               return NULL;
-       }
+       if (book_view)
+               e_data_book_view_notify_progress (book_view, -1, NULL);
 
-       /* append element to list */
-       element = g_malloc (sizeof (element[0]));
-       element->href = href;
-       element->etag = etag;
-       return element;
+       g_mutex_unlock (&priv->update_lock);
+
+       return EDB_ERROR (SUCCESS);
 }
 
-static response_element_t *
-parse_propfind_response (xmlTextReaderPtr reader)
+static gpointer
+book_view_thread (gpointer data)
 {
-       parser_strings_t    strings;
-       response_element_t *elements;
+       EDataBookView                          *book_view = data;
+       WebdavBackendSearchClosure             *closure   = get_closure (book_view);
+       EBookBackendWebdav                     *webdav    = closure->webdav;
+       GError *error;
 
-       /* get internalized versions of some strings to avoid strcmp while
-        * parsing */
-       strings.multistatus
-               = xmlTextReaderConstString (reader, BAD_CAST "multistatus");
-       strings.dav         = xmlTextReaderConstString (reader, BAD_CAST "DAV:");
-       strings.href        = xmlTextReaderConstString (reader, BAD_CAST "href");
-       strings.response    = xmlTextReaderConstString (reader, BAD_CAST "response");
-       strings.propstat    = xmlTextReaderConstString (reader, BAD_CAST "propstat");
-       strings.prop        = xmlTextReaderConstString (reader, BAD_CAST "prop");
-       strings.getetag     = xmlTextReaderConstString (reader, BAD_CAST "getetag");
+       e_flag_set (closure->running);
 
-       while (xmlTextReaderRead (reader) == 1 && xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT) {
-       }
+       /* ref the book view because it'll be removed and unrefed when/if
+        * it's stopped */
+       g_object_ref (book_view);
 
-       if (xmlTextReaderConstLocalName (reader) != strings.multistatus
-                       || xmlTextReaderConstNamespaceUri (reader) != strings.dav) {
-               g_warning ("webdav PROPFIND result is not <DAV:multistatus>");
-               return NULL;
-       }
+       error = download_contacts (webdav, closure->running, book_view, NULL);
 
-       elements = NULL;
+       g_object_unref (book_view);
 
-       /* parse all DAV:response tags */
-       while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > 0) {
-               response_element_t *element;
+       if (error)
+               g_error_free (error);
 
-               if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
-                       continue;
+       return NULL;
+}
 
-               if (xmlTextReaderConstLocalName (reader) != strings.response
-                               || xmlTextReaderConstNamespaceUri (reader) != strings.dav)
-                       continue;
+static void
+e_book_backend_webdav_start_view (EBookBackend *backend,
+                                  EDataBookView *book_view)
+{
+       EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
+       EBookBackendWebdavPrivate *priv   = webdav->priv;
+       EBookBackendSExp *sexp;
+       const gchar *query;
+       GList *contacts;
+       GList *l;
 
-               element = parse_response_tag (&strings, reader);
-               if (element == NULL)
-                       continue;
+       sexp = e_data_book_view_get_sexp (book_view);
+       query = e_book_backend_sexp_text (sexp);
 
-               element->next = elements;
-               elements      = element;
-       }
+       g_mutex_lock (&priv->cache_lock);
+       contacts = e_book_backend_cache_get_contacts (priv->cache, query);
+       g_mutex_unlock (&priv->cache_lock);
 
-       return elements;
-}
+       for (l = contacts; l != NULL; l = g_list_next (l)) {
+               EContact *contact = l->data;
+               e_data_book_view_notify_update (book_view, contact);
+               g_object_unref (contact);
+       }
+       g_list_free (contacts);
 
-static SoupMessage *
-send_propfind (EBookBackendWebdav *webdav,
-               GCancellable *cancellable)
-{
-       SoupMessage               *message;
-       EBookBackendWebdavPrivate *priv    = webdav->priv;
-       const gchar               *request =
-               "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
-               "<propfind xmlns=\"DAV:\"><prop><getetag/></prop></propfind>";
+       /* this way the UI is notified about cached contacts immediately,
+        * and the update thread notifies about possible changes only */
+       e_data_book_view_notify_complete (book_view, NULL /* Success */);
 
-       message = soup_message_new (SOUP_METHOD_PROPFIND, priv->uri);
-       soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
-       soup_message_headers_append (message->request_headers, "Connection", "close");
-       soup_message_headers_append (message->request_headers, "Depth", "1");
-       soup_message_set_request (
-               message, "text/xml", SOUP_MEMORY_TEMPORARY,
-               (gchar *) request, strlen (request));
+       if (e_backend_get_online (E_BACKEND (backend))) {
+               WebdavBackendSearchClosure *closure
+                       = init_closure (book_view, E_BOOK_BACKEND_WEBDAV (backend));
 
-       send_and_handle_ssl (webdav, message, cancellable);
+               closure->thread
+                       = g_thread_new (NULL, book_view_thread, book_view);
 
-       return message;
+               e_flag_wait (closure->running);
+       }
 }
 
-static xmlXPathObjectPtr
-xpath_eval (xmlXPathContextPtr ctx,
-            const gchar *format,
-            ...)
+static void
+e_book_backend_webdav_stop_view (EBookBackend *backend,
+                                 EDataBookView *book_view)
 {
-       xmlXPathObjectPtr  result;
-       va_list            args;
-       gchar              *expr;
-
-       if (ctx == NULL) {
-               return NULL;
-       }
+       WebdavBackendSearchClosure *closure;
+       gboolean                    need_join;
 
-       va_start (args, format);
-       expr = g_strdup_vprintf (format, args);
-       va_end (args);
+       if (!e_backend_get_online (E_BACKEND (backend)))
+               return;
 
-       result = xmlXPathEvalExpression ((xmlChar *) expr, ctx);
-       g_free (expr);
+       closure = get_closure (book_view);
+       if (closure == NULL)
+               return;
 
-       if (result == NULL) {
-               return NULL;
-       }
+       need_join = e_flag_is_set (closure->running);
+       e_flag_clear (closure->running);
 
-       if (result->type == XPATH_NODESET &&
-           xmlXPathNodeSetIsEmpty (result->nodesetval)) {
-               xmlXPathFreeObject (result);
-               return NULL;
+       if (need_join) {
+               g_thread_join (closure->thread);
+               closure->thread = NULL;
        }
-
-       return result;
 }
 
-static gchar *
-xp_object_get_string (xmlXPathObjectPtr result)
+/** authentication callback for libsoup */
+static void
+soup_authenticate (SoupSession *session,
+                   SoupMessage *message,
+                   SoupAuth *auth,
+                   gboolean retrying,
+                   gpointer data)
 {
-       gchar *ret = NULL;
+       EBookBackendWebdav        *webdav = data;
+       EBookBackendWebdavPrivate *priv   = webdav->priv;
 
-       if (result == NULL)
-               return ret;
+       if (retrying)
+               return;
 
-       if (result->type == XPATH_STRING) {
-               ret = g_strdup ((gchar *) result->stringval);
+       if (priv->username != NULL) {
+               soup_auth_authenticate (auth, priv->username, priv->password);
        }
-
-       xmlXPathFreeObject (result);
-       return ret;
 }
 
-static guint
-xp_object_get_status (xmlXPathObjectPtr result)
+static void
+proxy_settings_changed (EProxy *proxy,
+                        gpointer user_data)
 {
-       gboolean res;
-       guint    ret = 0;
+       SoupURI *proxy_uri = NULL;
+       EBookBackendWebdavPrivate *priv = (EBookBackendWebdavPrivate *) user_data;
 
-       if (result == NULL)
-               return ret;
+       if (!priv || !priv->uri || !priv->session)
+               return;
 
-       if (result->type == XPATH_STRING) {
-               res = soup_headers_parse_status_line ((gchar *) result->stringval, NULL, &ret, NULL);
-               if (!res) {
-                       ret = 0;
-               }
+       /* use proxy if necessary */
+       if (e_proxy_require_proxy_for_uri (proxy, priv->uri)) {
+               proxy_uri = e_proxy_peek_uri_for (proxy, priv->uri);
        }
 
-       xmlXPathFreeObject (result);
-       return ret;
+       g_object_set (priv->session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
 }
 
-static gboolean
-check_addressbook_changed (EBookBackendWebdav *webdav,
-                           gchar **new_ctag,
-                           GCancellable *cancellable)
+static void
+e_book_backend_webdav_notify_online_cb (EBookBackend *backend,
+                                        GParamSpec *pspec)
 {
-       gboolean res = TRUE;
-       const gchar *request = "<?xml version=\"1.0\" encoding=\"utf-8\"?><propfind 
xmlns=\"DAV:\"><prop><getctag/></prop></propfind>";
-       EBookBackendWebdavPrivate *priv;
-       SoupMessage *message;
-
-       g_return_val_if_fail (webdav != NULL, TRUE);
-       g_return_val_if_fail (new_ctag != NULL, TRUE);
+       gboolean online;
 
-       *new_ctag = NULL;
-       priv = webdav->priv;
+       /* set_mode is called before the backend is loaded */
+       if (!e_book_backend_is_opened (backend))
+               return;
 
-       if (!priv->supports_getctag)
-               return TRUE;
+       /* XXX Could just use a property binding for this.
+        *     EBackend:online --> EBookBackend:writable */
+       online = e_backend_get_online (E_BACKEND (backend));
+       e_book_backend_set_writable (backend, online);
+}
 
-       priv->supports_getctag = FALSE;
+static void
+book_backend_webdav_dispose (GObject *object)
+{
+       EBookBackendWebdavPrivate *priv;
 
-       message = soup_message_new (SOUP_METHOD_PROPFIND, priv->uri);
-       if (!message)
-               return TRUE;
+       priv = E_BOOK_BACKEND_WEBDAV_GET_PRIVATE (object);
 
-       soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
-       soup_message_headers_append (message->request_headers, "Connection", "close");
-       soup_message_headers_append (message->request_headers, "Depth", "0");
-       soup_message_set_request (message, "text/xml", SOUP_MEMORY_TEMPORARY, (gchar *) request, strlen 
(request));
-       send_and_handle_ssl (webdav, message, cancellable);
+       g_clear_object (&priv->session);
+       g_clear_object (&priv->proxy);
+       g_clear_object (&priv->cache);
 
-       if (message->status_code == 207 && message->response_body) {
-               xmlDocPtr xml;
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_book_backend_webdav_parent_class)->dispose (object);
+}
 
-               xml = xmlReadMemory (message->response_body->data, message->response_body->length, NULL, 
NULL, XML_PARSE_NOWARNING);
-               if (xml) {
-                       const gchar *GETCTAG_XPATH_STATUS = 
"string(/D:multistatus/D:response/D:propstat/D:prop/D:getctag/../../D:status)";
-                       const gchar *GETCTAG_XPATH_VALUE = 
"string(/D:multistatus/D:response/D:propstat/D:prop/D:getctag)";
-                       xmlXPathContextPtr xpctx;
+static void
+book_backend_webdav_finalize (GObject *object)
+{
+       EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (object);
+       EBookBackendWebdavPrivate *priv = webdav->priv;
 
-                       xpctx = xmlXPathNewContext (xml);
-                       xmlXPathRegisterNs (xpctx, (xmlChar *) "D", (xmlChar *) "DAV:");
+       g_free (priv->uri);
+       g_free (priv->username);
+       g_free (priv->password);
 
-                       if (xp_object_get_status (xpath_eval (xpctx, GETCTAG_XPATH_STATUS)) == 200) {
-                               gchar *txt = xp_object_get_string (xpath_eval (xpctx, GETCTAG_XPATH_VALUE));
+       g_mutex_clear (&priv->cache_lock);
+       g_mutex_clear (&priv->update_lock);
 
-                               if (txt && *txt) {
-                                       gint len = strlen (txt);
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_book_backend_webdav_parent_class)->finalize (object);
+}
 
-                                       if (*txt == '\"' && len > 2 && txt[len - 1] == '\"') {
-                                               /* dequote */
-                                               *new_ctag = g_strndup (txt + 1, len - 2);
-                                       } else {
-                                               *new_ctag = txt;
-                                               txt = NULL;
-                                       }
+static gchar *
+book_backend_webdav_get_backend_property (EBookBackend *backend,
+                                          const gchar *prop_name)
+{
+       g_return_val_if_fail (prop_name != NULL, NULL);
 
-                                       if (*new_ctag) {
-                                               const gchar *my_ctag;
+       if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
+               return g_strdup ("net,do-initial-query,contact-lists");
 
-                                               g_mutex_lock (&priv->cache_lock);
-                                               my_ctag = e_file_cache_get_object (E_FILE_CACHE 
(priv->cache), WEBDAV_CTAG_KEY);
-                                               res = !my_ctag || !g_str_equal (my_ctag, *new_ctag);
-                                               priv->supports_getctag = TRUE;
-                                               g_mutex_unlock (&priv->cache_lock);
-                                       }
-                               }
+       } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
+               return g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
 
-                               g_free (txt);
-                       }
+       } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
+               GString *fields;
+               gint ii;
 
-                       xmlXPathFreeContext (xpctx);
-                       xmlFreeDoc (xml);
+               fields = g_string_sized_new (1024);
+
+               /* we support everything */
+               for (ii = 1; ii < E_CONTACT_FIELD_LAST; ii++) {
+                       if (fields->len > 0)
+                               g_string_append_c (fields, ',');
+                       g_string_append (fields, e_contact_field_name (ii));
                }
-       }
 
-       g_object_unref (message);
+               return g_string_free (fields, FALSE);
+       }
 
-       return res;
+       /* Chain up to parent's get_backend_property() method. */
+       return E_BOOK_BACKEND_CLASS (e_book_backend_webdav_parent_class)->
+               get_backend_property (backend, prop_name);
 }
 
-static GError *
-download_contacts (EBookBackendWebdav *webdav,
-                   EFlag *running,
-                   EDataBookView *book_view,
-                   GCancellable *cancellable)
+static void
+book_backend_webdav_open (EBookBackend *backend,
+                          EDataBook *book,
+                          guint opid,
+                          GCancellable *cancellable,
+                          gboolean only_if_exists)
 {
-       EBookBackendWebdavPrivate *priv = webdav->priv;
-       EBookBackend              *book_backend;
-       SoupMessage               *message;
-       guint                      status;
-       xmlTextReaderPtr           reader;
-       response_element_t        *elements;
-       response_element_t        *element;
-       response_element_t        *next;
-       gint                        count;
-       gint                        i;
-       gchar                     *new_ctag = NULL;
+       EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
+       EBookBackendWebdavPrivate *priv   = webdav->priv;
+       ESourceAuthentication     *auth_extension;
+       ESourceOffline            *offline_extension;
+       ESourceWebdav             *webdav_extension;
+       ESource                   *source;
+       const gchar               *extension_name;
+       const gchar               *cache_dir;
+       gchar                     *filename;
+       SoupSession               *session;
+       SoupURI                   *suri;
+       GError                    *error = NULL;
 
-       g_mutex_lock (&priv->update_lock);
+       /* will try fetch ctag for the first time, if it fails then sets this to FALSE */
+       priv->supports_getctag = TRUE;
 
-       if (!check_addressbook_changed (webdav, &new_ctag, cancellable)) {
-               g_free (new_ctag);
-               g_mutex_unlock (&priv->update_lock);
-               return EDB_ERROR (SUCCESS);
+       source = e_backend_get_source (E_BACKEND (backend));
+       cache_dir = e_book_backend_get_cache_dir (backend);
+
+       extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
+       auth_extension = e_source_get_extension (source, extension_name);
+
+       extension_name = E_SOURCE_EXTENSION_OFFLINE;
+       offline_extension = e_source_get_extension (source, extension_name);
+
+       extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
+       webdav_extension = e_source_get_extension (source, extension_name);
+
+       priv->marked_for_offline =
+               e_source_offline_get_stay_synchronized (offline_extension);
+
+       if (!e_backend_get_online (E_BACKEND (backend)) && !priv->marked_for_offline ) {
+               e_data_book_respond_open (book, opid, EDB_ERROR (OFFLINE_UNAVAILABLE));
+               return;
        }
 
-       book_backend = E_BOOK_BACKEND (webdav);
+       suri = e_source_webdav_dup_soup_uri (webdav_extension);
 
-       if (book_view != NULL) {
-               e_data_book_view_notify_progress (book_view, -1,
-                               _("Loading Addressbook summary..."));
+       priv->uri = soup_uri_to_string (suri, FALSE);
+       if (!priv->uri || !*priv->uri) {
+               g_free (priv->uri);
+               priv->uri = NULL;
+               soup_uri_free (suri);
+               e_data_book_respond_open (book, opid, EDB_ERROR_EX (OTHER_ERROR, _("Cannot transform SoupURI 
to string")));
+               return;
        }
 
-       message = send_propfind (webdav, cancellable);
-       status  = message->status_code;
+       g_mutex_lock (&priv->cache_lock);
 
-       if (status == 401 || status == 407) {
-               g_object_unref (message);
-               g_free (new_ctag);
-               if (book_view)
-                       e_data_book_view_notify_progress (book_view, -1, NULL);
-               g_mutex_unlock (&priv->update_lock);
-               return webdav_handle_auth_request (webdav);
+       /* make sure the priv->uri ends with a forward slash */
+       if (priv->uri[strlen (priv->uri) - 1] != '/') {
+               gchar *tmp = priv->uri;
+               priv->uri = g_strconcat (tmp, "/", NULL);
+               g_free (tmp);
        }
-       if (status != 207) {
-               GError *error;
 
-               error = e_data_book_create_error_fmt (
-                       E_DATA_BOOK_STATUS_OTHER_ERROR,
-                       _("PROPFIND on webdav failed with HTTP status %d (%s)"),
-                       status,
-                       message->reason_phrase && *message->reason_phrase ? message->reason_phrase :
-                       (soup_status_get_phrase (message->status_code) ? soup_status_get_phrase 
(message->status_code) : _("Unknown error")));
+       if (!priv->cache) {
+               filename = g_build_filename (cache_dir, "cache.xml", NULL);
+               priv->cache = e_book_backend_cache_new (filename);
+               g_free (filename);
+       }
+       g_mutex_unlock (&priv->cache_lock);
 
-               g_object_unref (message);
-               g_free (new_ctag);
+       session = soup_session_sync_new ();
+       g_object_set (
+               session,
+               SOUP_SESSION_TIMEOUT, 90,
+               SOUP_SESSION_SSL_STRICT, TRUE,
+               SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
+               NULL);
 
-               if (book_view)
-                       e_data_book_view_notify_progress (book_view, -1, NULL);
+       e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
 
-               g_mutex_unlock (&priv->update_lock);
+       g_signal_connect (
+               session, "authenticate",
+               G_CALLBACK (soup_authenticate), webdav);
 
-               return error;
-       }
-       if (message->response_body == NULL) {
-               g_warning ("No response body in webdav PROPFIND result");
-               g_object_unref (message);
-               g_free (new_ctag);
+       priv->session = session;
+       priv->proxy = e_proxy_new ();
+       e_proxy_setup_proxy (priv->proxy);
+       g_signal_connect (
+               priv->proxy, "changed",
+               G_CALLBACK (proxy_settings_changed), priv);
+       proxy_settings_changed (priv->proxy, priv);
+       webdav_debug_setup (priv->session);
 
-               if (book_view)
-                       e_data_book_view_notify_progress (book_view, -1, NULL);
+       e_backend_set_online (E_BACKEND (backend), TRUE);
+       e_book_backend_set_writable (backend, TRUE);
 
-               g_mutex_unlock (&priv->update_lock);
+       if (e_source_authentication_required (auth_extension))
+               e_backend_authenticate_sync (
+                       E_BACKEND (backend),
+                       E_SOURCE_AUTHENTICATOR (backend),
+                       cancellable, &error);
 
-               return e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR, _("No response body in 
webdav PROPFIND result"));
-       }
+       soup_uri_free (suri);
 
-       /* parse response */
-       reader = xmlReaderForMemory (
-               message->response_body->data,
-               message->response_body->length, NULL, NULL,
-               XML_PARSE_NOWARNING);
+       /* This function frees the GError passed to it. */
+       e_data_book_respond_open (book, opid, error);
+}
 
-       elements = parse_propfind_response (reader);
+static void
+book_backend_webdav_create_contacts (EBookBackend *backend,
+                                     EDataBook *book,
+                                     guint32 opid,
+                                     GCancellable *cancellable,
+                                     const GSList *vcards)
+{
+       EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
+       EBookBackendWebdavPrivate *priv   = webdav->priv;
+       EContact                  *contact;
+       gchar                     *uid;
+       guint                      status;
+       gchar                     *status_reason = NULL;
+       const gchar               *vcard = (const gchar *) vcards->data;
+       GSList                     added_contacts = {NULL,};
 
-       /* count contacts */
-       count = 0;
-       for (element = elements; element != NULL; element = element->next) {
-               ++count;
+       /* We make the assumption that the vCard list we're passed is always exactly one element long, since 
we haven't specified "bulk-adds"
+        * in our static capability list. This is because there is no clean way to roll back changes in case 
of an error. */
+       if (vcards->next != NULL) {
+               e_data_book_respond_create_contacts (
+                       book, opid,
+                       EDB_ERROR_EX (NOT_SUPPORTED,
+                       _("The backend does not support bulk additions")),
+                       NULL);
+               return;
        }
 
-       /* download contacts */
-       i = 0;
-       for (element = elements; element != NULL; element = element->next, ++i) {
-               const gchar  *uri;
-               const gchar *etag;
-               EContact    *contact;
-               gchar *complete_uri;
-
-               /* stop downloading if search was aborted */
-               if (running != NULL && !e_flag_is_set (running))
-                       break;
+       if (!e_backend_get_online (E_BACKEND (backend))) {
+               e_data_book_respond_create_contacts (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
+               return;
+       }
 
-               if (book_view != NULL) {
-                       gfloat percent = 100.0 / count * i;
-                       gchar buf[100];
-                       snprintf (buf, sizeof (buf), _("Loading Contacts (%d%%)"), (gint) percent);
-                       e_data_book_view_notify_progress (book_view, -1, buf);
-               }
+       /* do 3 rand() calls to construct a unique ID... poor way but should be
+        * good enough for us */
+       uid = g_strdup_printf (
+               "%s%08X-%08X-%08X.vcf",
+               priv->uri, rand (), rand (), rand ());
 
-               /* skip collections */
-               uri = (const gchar *) element->href;
-               if (uri[strlen (uri) - 1] == '/')
-                       continue;
+       contact = e_contact_new_from_vcard_with_uid (vcard, uid);
 
-               /* uri might be relative, construct complete one */
-               if (uri[0] == '/') {
-                       SoupURI *soup_uri = soup_uri_new (priv->uri);
-                       soup_uri->path    = g_strdup (uri);
+       /* kill revision field (might have been set by some other backend) */
+       e_contact_set (contact, E_CONTACT_REV, NULL);
 
-                       complete_uri = soup_uri_to_string (soup_uri, FALSE);
-                       soup_uri_free (soup_uri);
+       status = upload_contact (webdav, contact, &status_reason, cancellable);
+       if (status != 201 && status != 204) {
+               g_object_unref (contact);
+               if (status == 401 || status == 407) {
+                       e_data_book_respond_create_contacts (book, opid, webdav_handle_auth_request (webdav), 
NULL);
                } else {
-                       complete_uri = g_strdup (uri);
+                       e_data_book_respond_create_contacts (
+                               book, opid,
+                               e_data_book_create_error_fmt (
+                               E_DATA_BOOK_STATUS_OTHER_ERROR,
+                               _("Create resource '%s' failed with HTTP status %d (%s)"),
+                               uid, status, status_reason),
+                                       NULL);
                }
+               g_free (uid);
+               g_free (status_reason);
+               return;
+       }
 
-               etag = (const gchar *) element->etag;
+       g_free (status_reason);
 
-               g_mutex_lock (&priv->cache_lock);
-               contact = e_book_backend_cache_get_contact (priv->cache, complete_uri);
-               g_mutex_unlock (&priv->cache_lock);
+       /* PUT request didn't return an etag? try downloading to get one */
+       if (e_contact_get_const (contact, E_CONTACT_REV) == NULL) {
+               const gchar *new_uid;
+               EContact *new_contact;
 
-               /* download contact if it is not cached or its ETag changed */
-               if (contact == NULL || etag == NULL ||
-                   strcmp (e_contact_get_const (contact, E_CONTACT_REV), etag) != 0) {
-                       contact = download_contact (webdav, complete_uri, cancellable);
-                       if (contact != NULL) {
-                               g_mutex_lock (&priv->cache_lock);
-                               if (e_book_backend_cache_remove_contact (priv->cache, complete_uri))
-                                       e_book_backend_notify_remove (book_backend, complete_uri);
-                               e_book_backend_cache_add_contact (priv->cache, contact);
-                               g_mutex_unlock (&priv->cache_lock);
-                               e_book_backend_notify_update (book_backend, contact);
-                       }
-               }
+               g_warning ("Server didn't return etag for new address resource");
+               new_uid = e_contact_get_const (contact, E_CONTACT_UID);
+               new_contact = download_contact (webdav, new_uid, cancellable);
+               g_object_unref (contact);
 
-               g_free (complete_uri);
+               if (new_contact == NULL) {
+                       e_data_book_respond_create_contacts (
+                               book, opid,
+                               EDB_ERROR (OTHER_ERROR), NULL);
+                       g_free (uid);
+                       return;
+               }
+               contact = new_contact;
        }
 
-       /* free element list */
-       for (element = elements; element != NULL; element = next) {
-               next = element->next;
+       g_mutex_lock (&priv->cache_lock);
+       e_book_backend_cache_add_contact (priv->cache, contact);
+       g_mutex_unlock (&priv->cache_lock);
 
-               xmlFree (element->href);
-               xmlFree (element->etag);
-               g_free (element);
-       }
+       added_contacts.data = contact;
+       e_data_book_respond_create_contacts (book, opid, EDB_ERROR (SUCCESS), &added_contacts);
 
-       xmlFreeTextReader (reader);
-       g_object_unref (message);
+       g_object_unref (contact);
+       g_free (uid);
+}
 
-       if (new_ctag) {
-               g_mutex_lock (&priv->cache_lock);
-               if (!e_file_cache_replace_object (E_FILE_CACHE (priv->cache), WEBDAV_CTAG_KEY, new_ctag))
-                       e_file_cache_add_object (E_FILE_CACHE (priv->cache), WEBDAV_CTAG_KEY, new_ctag);
-               g_mutex_unlock (&priv->cache_lock);
+static void
+book_backend_webdav_modify_contacts (EBookBackend *backend,
+                                     EDataBook *book,
+                                     guint32 opid,
+                                     GCancellable *cancellable,
+                                     const GSList *vcards)
+{
+       EBookBackendWebdav        *webdav  = E_BOOK_BACKEND_WEBDAV (backend);
+       EBookBackendWebdavPrivate *priv    = webdav->priv;
+       EContact                  *contact;
+       GSList                     modified_contacts = {NULL,};
+       const gchar                *uid;
+       const gchar                *etag;
+       guint status;
+       gchar *status_reason = NULL;
+       const gchar *vcard = vcards->data;
+
+       if (!e_backend_get_online (E_BACKEND (backend))) {
+               e_data_book_respond_create_contacts (
+                       book, opid,
+                       EDB_ERROR (REPOSITORY_OFFLINE), NULL);
+               return;
        }
-       g_free (new_ctag);
 
-       if (book_view)
-               e_data_book_view_notify_progress (book_view, -1, NULL);
+       /* We make the assumption that the vCard list we're passed is always exactly one element long, since 
we haven't specified "bulk-modifies"
+        * in our static capability list. This is because there is no clean way to roll back changes in case 
of an error. */
+       if (vcards->next != NULL) {
+               e_data_book_respond_modify_contacts (
+                       book, opid,
+                       EDB_ERROR_EX (
+                               NOT_SUPPORTED,
+                               _("The backend does not support bulk modifications")),
+                       NULL);
+               return;
+       }
 
-       g_mutex_unlock (&priv->update_lock);
+       /* modify contact */
+       contact = e_contact_new_from_vcard (vcard);
+       status = upload_contact (webdav, contact, &status_reason, cancellable);
+       if (status != 201 && status != 204) {
+               g_object_unref (contact);
+               if (status == 401 || status == 407) {
+                       e_data_book_respond_modify_contacts (book, opid, webdav_handle_auth_request (webdav), 
NULL);
+                       g_free (status_reason);
+                       return;
+               }
+               /* data changed on server while we were editing */
+               if (status == 412) {
+                       /* too bad no special error code in evolution for this... */
+                       e_data_book_respond_modify_contacts (book, opid,
+                               e_data_book_create_error_fmt (
+                               E_DATA_BOOK_STATUS_OTHER_ERROR,
+                               _("Contact on server changed -> not modifying")),
+                               NULL);
+                       g_free (status_reason);
+                       return;
+               }
 
-       return EDB_ERROR (SUCCESS);
-}
+               e_data_book_respond_modify_contacts (book, opid,
+                       e_data_book_create_error_fmt (
+                       E_DATA_BOOK_STATUS_OTHER_ERROR,
+                       _("Modify contact failed with HTTP status %d (%s)"),
+                       status, status_reason),
+                       NULL);
+               g_free (status_reason);
+               return;
+       }
 
-static gpointer
-book_view_thread (gpointer data)
-{
-       EDataBookView                          *book_view = data;
-       WebdavBackendSearchClosure             *closure   = get_closure (book_view);
-       EBookBackendWebdav                     *webdav    = closure->webdav;
-       GError *error;
+       g_free (status_reason);
 
-       e_flag_set (closure->running);
+       uid = e_contact_get_const (contact, E_CONTACT_UID);
+       g_mutex_lock (&priv->cache_lock);
+       e_book_backend_cache_remove_contact (priv->cache, uid);
 
-       /* ref the book view because it'll be removed and unrefed when/if
-        * it's stopped */
-       g_object_ref (book_view);
+       etag = e_contact_get_const (contact, E_CONTACT_REV);
 
-       error = download_contacts (webdav, closure->running, book_view, NULL);
+       /* PUT request didn't return an etag? try downloading to get one */
+       if (etag == NULL || (etag[0] == 'W' && etag[1] == '/')) {
+               EContact *new_contact;
 
-       g_object_unref (book_view);
+               g_warning ("Server didn't return etag for modified address resource");
+               new_contact = download_contact (webdav, uid, cancellable);
+               if (new_contact != NULL) {
+                       contact = new_contact;
+               }
+       }
+       e_book_backend_cache_add_contact (priv->cache, contact);
+       g_mutex_unlock (&priv->cache_lock);
 
-       if (error)
-               g_error_free (error);
+       modified_contacts.data = contact;
+       e_data_book_respond_modify_contacts (book, opid, EDB_ERROR (SUCCESS), &modified_contacts);
 
-       return NULL;
+       g_object_unref (contact);
 }
 
 static void
-e_book_backend_webdav_start_view (EBookBackend *backend,
-                                  EDataBookView *book_view)
+book_backend_webdav_remove_contacts (EBookBackend *backend,
+                                     EDataBook *book,
+                                     guint32 opid,
+                                     GCancellable *cancellable,
+                                     const GSList *id_list)
 {
-       EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
-       EBookBackendWebdavPrivate *priv   = webdav->priv;
-       EBookBackendSExp *sexp;
-       const gchar *query;
-       GList *contacts;
-       GList *l;
-
-       sexp = e_data_book_view_get_sexp (book_view);
-       query = e_book_backend_sexp_text (sexp);
-
-       g_mutex_lock (&priv->cache_lock);
-       contacts = e_book_backend_cache_get_contacts (priv->cache, query);
-       g_mutex_unlock (&priv->cache_lock);
+       EBookBackendWebdav        *webdav      = E_BOOK_BACKEND_WEBDAV (backend);
+       EBookBackendWebdavPrivate *priv        = webdav->priv;
+       gchar                     *uid         = id_list->data;
+       GSList                     deleted_ids = {NULL,};
+       guint                      status;
 
-       for (l = contacts; l != NULL; l = g_list_next (l)) {
-               EContact *contact = l->data;
-               e_data_book_view_notify_update (book_view, contact);
-               g_object_unref (contact);
+       if (!e_backend_get_online (E_BACKEND (backend))) {
+               e_data_book_respond_remove_contacts (
+                       book, opid,
+                       EDB_ERROR (REPOSITORY_OFFLINE), NULL);
+               return;
        }
-       g_list_free (contacts);
 
-       /* this way the UI is notified about cached contacts immediately,
-        * and the update thread notifies about possible changes only */
-       e_data_book_view_notify_complete (book_view, NULL /* Success */);
+       /* We make the assumption that the ID list we're passed is always exactly one element long, since we 
haven't specified "bulk-removes"
+        * in our static capability list. */
+       if (id_list->next != NULL) {
+               e_data_book_respond_remove_contacts (
+                       book, opid,
+                       EDB_ERROR_EX (NOT_SUPPORTED,
+                       _("The backend does not support bulk removals")),
+                       NULL);
+               return;
+       }
 
-       if (e_backend_get_online (E_BACKEND (backend))) {
-               WebdavBackendSearchClosure *closure
-                       = init_closure (book_view, E_BOOK_BACKEND_WEBDAV (backend));
+       status = delete_contact (webdav, uid, cancellable);
+       if (status != 204) {
+               if (status == 401 || status == 407) {
+                       e_data_book_respond_remove_contacts (
+                               book, opid,
+                               webdav_handle_auth_request (webdav), NULL);
+               } else {
+                       g_warning ("DELETE failed with HTTP status %d", status);
+                       e_data_book_respond_remove_contacts (
+                               book, opid,
+                               e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
+                               _("DELETE failed with HTTP status %d"), status),
+                               NULL);
+               }
+               return;
+       }
 
-               closure->thread
-                       = g_thread_new (NULL, book_view_thread, book_view);
+       g_mutex_lock (&priv->cache_lock);
+       e_book_backend_cache_remove_contact (priv->cache, uid);
+       g_mutex_unlock (&priv->cache_lock);
 
-               e_flag_wait (closure->running);
-       }
+       deleted_ids.data = uid;
+       e_data_book_respond_remove_contacts (book, opid, EDB_ERROR (SUCCESS), &deleted_ids);
 }
 
 static void
-e_book_backend_webdav_stop_view (EBookBackend *backend,
-                                 EDataBookView *book_view)
+book_backend_webdav_get_contact (EBookBackend *backend,
+                                 EDataBook *book,
+                                 guint32 opid,
+                                 GCancellable *cancellable,
+                                 const gchar *uid)
 {
-       WebdavBackendSearchClosure *closure;
-       gboolean                    need_join;
+       EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
+       EBookBackendWebdavPrivate *priv   = webdav->priv;
+       EContact                  *contact;
+       gchar                      *vcard;
 
-       if (!e_backend_get_online (E_BACKEND (backend)))
-               return;
+       if (!e_backend_get_online (E_BACKEND (backend))) {
+               g_mutex_lock (&priv->cache_lock);
+               contact = e_book_backend_cache_get_contact (priv->cache, uid);
+               g_mutex_unlock (&priv->cache_lock);
+       } else {
+               contact = download_contact (webdav, uid, cancellable);
+               /* update cache as we possibly have changes */
+               if (contact != NULL) {
+                       g_mutex_lock (&priv->cache_lock);
+                       e_book_backend_cache_remove_contact (priv->cache, uid);
+                       e_book_backend_cache_add_contact (priv->cache, contact);
+                       g_mutex_unlock (&priv->cache_lock);
+               }
+       }
 
-       closure = get_closure (book_view);
-       if (closure == NULL)
+       if (contact == NULL) {
+               e_data_book_respond_get_contact (book, opid, EDB_ERROR (CONTACT_NOT_FOUND), NULL);
                return;
-
-       need_join = e_flag_is_set (closure->running);
-       e_flag_clear (closure->running);
-
-       if (need_join) {
-               g_thread_join (closure->thread);
-               closure->thread = NULL;
        }
+
+       vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+       e_data_book_respond_get_contact (book, opid, EDB_ERROR (SUCCESS), vcard);
+       g_free (vcard);
+       g_object_unref (contact);
 }
 
 static void
-e_book_backend_webdav_get_contact_list (EBookBackend *backend,
-                                        EDataBook *book,
-                                        guint32 opid,
-                                        GCancellable *cancellable,
-                                        const gchar *query)
+book_backend_webdav_get_contact_list (EBookBackend *backend,
+                                      EDataBook *book,
+                                      guint32 opid,
+                                      GCancellable *cancellable,
+                                      const gchar *query)
 {
        EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
        EBookBackendWebdavPrivate *priv   = webdav->priv;
@@ -1283,11 +1509,11 @@ e_book_backend_webdav_get_contact_list (EBookBackend *backend,
 }
 
 static void
-e_book_backend_webdav_get_contact_list_uids (EBookBackend *backend,
-                                             EDataBook *book,
-                                             guint32 opid,
-                                             GCancellable *cancellable,
-                                             const gchar *query)
+book_backend_webdav_get_contact_list_uids (EBookBackend *backend,
+                                           EDataBook *book,
+                                           guint32 opid,
+                                           GCancellable *cancellable,
+                                           const gchar *query)
 {
        EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
        EBookBackendWebdavPrivate *priv   = webdav->priv;
@@ -1326,233 +1552,6 @@ e_book_backend_webdav_get_contact_list_uids (EBookBackend *backend,
        g_slist_free (uids_list);
 }
 
-/** authentication callback for libsoup */
-static void
-soup_authenticate (SoupSession *session,
-                   SoupMessage *message,
-                   SoupAuth *auth,
-                   gboolean retrying,
-                   gpointer data)
-{
-       EBookBackendWebdav        *webdav = data;
-       EBookBackendWebdavPrivate *priv   = webdav->priv;
-
-       if (retrying)
-               return;
-
-       if (priv->username != NULL) {
-               soup_auth_authenticate (auth, priv->username, priv->password);
-       }
-}
-
-static void
-proxy_settings_changed (EProxy *proxy,
-                        gpointer user_data)
-{
-       SoupURI *proxy_uri = NULL;
-       EBookBackendWebdavPrivate *priv = (EBookBackendWebdavPrivate *) user_data;
-
-       if (!priv || !priv->uri || !priv->session)
-               return;
-
-       /* use proxy if necessary */
-       if (e_proxy_require_proxy_for_uri (proxy, priv->uri)) {
-               proxy_uri = e_proxy_peek_uri_for (proxy, priv->uri);
-       }
-
-       g_object_set (priv->session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
-}
-
-static void
-e_book_backend_webdav_open (EBookBackend *backend,
-                            EDataBook *book,
-                            guint opid,
-                            GCancellable *cancellable,
-                            gboolean only_if_exists)
-{
-       EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
-       EBookBackendWebdavPrivate *priv   = webdav->priv;
-       ESourceAuthentication     *auth_extension;
-       ESourceOffline            *offline_extension;
-       ESourceWebdav             *webdav_extension;
-       ESource                   *source;
-       const gchar               *extension_name;
-       const gchar               *cache_dir;
-       gchar                     *filename;
-       SoupSession               *session;
-       SoupURI                   *suri;
-       GError                    *error = NULL;
-
-       /* will try fetch ctag for the first time, if it fails then sets this to FALSE */
-       priv->supports_getctag = TRUE;
-
-       source = e_backend_get_source (E_BACKEND (backend));
-       cache_dir = e_book_backend_get_cache_dir (backend);
-
-       extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
-       auth_extension = e_source_get_extension (source, extension_name);
-
-       extension_name = E_SOURCE_EXTENSION_OFFLINE;
-       offline_extension = e_source_get_extension (source, extension_name);
-
-       extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
-       webdav_extension = e_source_get_extension (source, extension_name);
-
-       priv->marked_for_offline =
-               e_source_offline_get_stay_synchronized (offline_extension);
-
-       if (!e_backend_get_online (E_BACKEND (backend)) && !priv->marked_for_offline ) {
-               e_data_book_respond_open (book, opid, EDB_ERROR (OFFLINE_UNAVAILABLE));
-               return;
-       }
-
-       suri = e_source_webdav_dup_soup_uri (webdav_extension);
-
-       priv->uri = soup_uri_to_string (suri, FALSE);
-       if (!priv->uri || !*priv->uri) {
-               g_free (priv->uri);
-               priv->uri = NULL;
-               soup_uri_free (suri);
-               e_data_book_respond_open (book, opid, EDB_ERROR_EX (OTHER_ERROR, _("Cannot transform SoupURI 
to string")));
-               return;
-       }
-
-       g_mutex_lock (&priv->cache_lock);
-
-       /* make sure the priv->uri ends with a forward slash */
-       if (priv->uri[strlen (priv->uri) - 1] != '/') {
-               gchar *tmp = priv->uri;
-               priv->uri = g_strconcat (tmp, "/", NULL);
-               g_free (tmp);
-       }
-
-       if (!priv->cache) {
-               filename = g_build_filename (cache_dir, "cache.xml", NULL);
-               priv->cache = e_book_backend_cache_new (filename);
-               g_free (filename);
-       }
-       g_mutex_unlock (&priv->cache_lock);
-
-       session = soup_session_sync_new ();
-       g_object_set (
-               session,
-               SOUP_SESSION_TIMEOUT, 90,
-               SOUP_SESSION_SSL_STRICT, TRUE,
-               SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
-               NULL);
-
-       e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
-
-       g_signal_connect (
-               session, "authenticate",
-               G_CALLBACK (soup_authenticate), webdav);
-
-       priv->session = session;
-       priv->proxy = e_proxy_new ();
-       e_proxy_setup_proxy (priv->proxy);
-       g_signal_connect (
-               priv->proxy, "changed",
-               G_CALLBACK (proxy_settings_changed), priv);
-       proxy_settings_changed (priv->proxy, priv);
-       webdav_debug_setup (priv->session);
-
-       e_backend_set_online (E_BACKEND (backend), TRUE);
-       e_book_backend_set_writable (backend, TRUE);
-
-       if (e_source_authentication_required (auth_extension))
-               e_backend_authenticate_sync (
-                       E_BACKEND (backend),
-                       E_SOURCE_AUTHENTICATOR (backend),
-                       cancellable, &error);
-
-       soup_uri_free (suri);
-
-       /* This function frees the GError passed to it. */
-       e_data_book_respond_open (book, opid, error);
-}
-
-static void
-e_book_backend_webdav_notify_online_cb (EBookBackend *backend,
-                                        GParamSpec *pspec)
-{
-       gboolean online;
-
-       /* set_mode is called before the backend is loaded */
-       if (!e_book_backend_is_opened (backend))
-               return;
-
-       /* XXX Could just use a property binding for this.
-        *     EBackend:online --> EBookBackend:writable */
-       online = e_backend_get_online (E_BACKEND (backend));
-       e_book_backend_set_writable (backend, online);
-}
-
-static gchar *
-e_book_backend_webdav_get_backend_property (EBookBackend *backend,
-                                            const gchar *prop_name)
-{
-       g_return_val_if_fail (prop_name != NULL, NULL);
-
-       if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
-               return g_strdup ("net,do-initial-query,contact-lists");
-
-       } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
-               return g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
-
-       } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
-               GString *fields;
-               gint ii;
-
-               fields = g_string_sized_new (1024);
-
-               /* we support everything */
-               for (ii = 1; ii < E_CONTACT_FIELD_LAST; ii++) {
-                       if (fields->len > 0)
-                               g_string_append_c (fields, ',');
-                       g_string_append (fields, e_contact_field_name (ii));
-               }
-
-               return g_string_free (fields, FALSE);
-       }
-
-       /* Chain up to parent's get_backend_property() method. */
-       return E_BOOK_BACKEND_CLASS (e_book_backend_webdav_parent_class)->
-               get_backend_property (backend, prop_name);
-}
-
-static void
-e_book_backend_webdav_dispose (GObject *object)
-{
-       EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (object);
-       EBookBackendWebdavPrivate *priv   = webdav->priv;
-
-       #define do_unref(x) { if (x) g_object_unref (x); x = NULL; }
-
-       do_unref (priv->session);
-       do_unref (priv->proxy);
-       do_unref (priv->cache);
-
-       g_free (priv->uri);
-       g_free (priv->username);
-       g_free (priv->password);
-
-       #undef do_unref
-
-       G_OBJECT_CLASS (e_book_backend_webdav_parent_class)->dispose (object);
-}
-
-static void
-e_book_backend_webdav_finalize (GObject *object)
-{
-       EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (object);
-       EBookBackendWebdavPrivate *priv = webdav->priv;
-
-       g_mutex_clear (&priv->cache_lock);
-       g_mutex_clear (&priv->update_lock);
-
-       G_OBJECT_CLASS (e_book_backend_webdav_parent_class)->finalize (object);
-}
-
 static ESourceAuthenticationResult
 book_backend_webdav_try_password_sync (ESourceAuthenticator *authenticator,
                                        const GString *password,
@@ -1614,23 +1613,21 @@ e_book_backend_webdav_class_init (EBookBackendWebdavClass *class)
 
        g_type_class_add_private (class, sizeof (EBookBackendWebdavPrivate));
 
-       backend_class = E_BOOK_BACKEND_CLASS (class);
+       object_class = G_OBJECT_CLASS (class);
+       object_class->dispose = book_backend_webdav_dispose;
+       object_class->finalize = book_backend_webdav_finalize;
 
-       /* Set the virtual methods. */
-       backend_class->open                     = e_book_backend_webdav_open;
-       backend_class->get_backend_property     = e_book_backend_webdav_get_backend_property;
-
-       backend_class->create_contacts          = e_book_backend_webdav_create_contacts;
-       backend_class->remove_contacts          = e_book_backend_webdav_remove_contacts;
-       backend_class->modify_contacts          = e_book_backend_webdav_modify_contacts;
-       backend_class->get_contact              = e_book_backend_webdav_get_contact;
-       backend_class->get_contact_list         = e_book_backend_webdav_get_contact_list;
-       backend_class->get_contact_list_uids    = e_book_backend_webdav_get_contact_list_uids;
-       backend_class->start_view               = e_book_backend_webdav_start_view;
-       backend_class->stop_view                = e_book_backend_webdav_stop_view;
-
-       object_class->dispose                   = e_book_backend_webdav_dispose;
-       object_class->finalize                  = e_book_backend_webdav_finalize;
+       backend_class = E_BOOK_BACKEND_CLASS (class);
+       backend_class->get_backend_property = book_backend_webdav_get_backend_property;
+       backend_class->open = book_backend_webdav_open;
+       backend_class->create_contacts = book_backend_webdav_create_contacts;
+       backend_class->modify_contacts = book_backend_webdav_modify_contacts;
+       backend_class->remove_contacts = book_backend_webdav_remove_contacts;
+       backend_class->get_contact = book_backend_webdav_get_contact;
+       backend_class->get_contact_list = book_backend_webdav_get_contact_list;
+       backend_class->get_contact_list_uids = book_backend_webdav_get_contact_list_uids;
+       backend_class->start_view = e_book_backend_webdav_start_view;
+       backend_class->stop_view = e_book_backend_webdav_stop_view;
 }
 
 static void


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