[evolution-data-server/wip/offline-cache] Make EWebDAVDiscover use EWebDAVSession



commit d93f5ab215900aaa184105f5e906d6861bd2d51b
Author: Milan Crha <mcrha redhat com>
Date:   Tue May 16 14:50:54 2017 +0200

    Make EWebDAVDiscover use EWebDAVSession

 src/libedataserver/e-webdav-discover.c | 1927 +++++---------------------------
 src/libedataserver/e-webdav-session.c  |    2 +-
 2 files changed, 287 insertions(+), 1642 deletions(-)
---
diff --git a/src/libedataserver/e-webdav-discover.c b/src/libedataserver/e-webdav-discover.c
index b25577a..43eaeff 100644
--- a/src/libedataserver/e-webdav-discover.c
+++ b/src/libedataserver/e-webdav-discover.c
@@ -21,1582 +21,329 @@
 
 #include <libsoup/soup.h>
 
-#include <libxml/tree.h>
-#include <libxml/xpath.h>
-#include <libxml/xpathInternals.h>
-
-#include "e-soup-auth-bearer.h"
-#include "e-soup-ssl-trust.h"
-#include "e-source-authentication.h"
-#include "e-source-credentials-provider-impl-google.h"
 #include "e-source-webdav.h"
-#include "e-webdav-discover.h"
-
-#define XC(string) ((xmlChar *) string)
-
-/* Standard Namespaces */
-#define NS_WEBDAV  "DAV:"
-#define NS_CALDAV  "urn:ietf:params:xml:ns:caldav"
-#define NS_CARDDAV "urn:ietf:params:xml:ns:carddav"
-
-/* Application-Specific Namespaces */
-#define NS_ICAL    "http://apple.com/ns/ical/";
+#include "e-webdav-session.h"
+#include "e-xml-utils.h"
 
-/* Mainly for readability. */
-enum {
-       DEPTH_0 = 0,
-       DEPTH_1 = 1
-};
+#include "e-webdav-discover.h"
 
-typedef struct _EWebDAVDiscoverContext {
-       ESource *source;
-       gchar *url_use_path;
+typedef struct _WebDAVDiscoverData {
+       GHashTable *covered_hrefs;
+       GSList *addressbooks;
+       GSList *calendars;
        guint32 only_supports;
-       ENamedParameters *credentials;
-       gchar *out_certificate_pem;
-       GTlsCertificateFlags out_certificate_errors;
-       GSList *out_discovered_sources;
-       GSList *out_calendar_user_addresses;
-} EWebDAVDiscoverContext;
-
-static EWebDAVDiscoverContext *
-e_webdav_discover_context_new (ESource *source,
-                              const gchar *url_use_path,
-                              guint32 only_supports,
-                              const ENamedParameters *credentials)
-{
-       EWebDAVDiscoverContext *context;
-
-       context = g_new0 (EWebDAVDiscoverContext, 1);
-       context->source = g_object_ref (source);
-       context->url_use_path = g_strdup (url_use_path);
-       context->only_supports = only_supports;
-       context->credentials = e_named_parameters_new_clone (credentials);
-       context->out_certificate_pem = NULL;
-       context->out_certificate_errors = 0;
-       context->out_discovered_sources = NULL;
-       context->out_calendar_user_addresses = NULL;
-
-       return context;
-}
+       GSList **out_calendar_user_addresses;
+       GCancellable *cancellable;
+       GError **error;
+} WebDAVDiscoverData;
 
 static void
-e_webdav_discover_context_free (gpointer ptr)
-{
-       EWebDAVDiscoverContext *context = ptr;
-
-       if (!context)
-               return;
-
-       g_clear_object (&context->source);
-       g_free (context->url_use_path);
-       e_named_parameters_free (context->credentials);
-       g_free (context->out_certificate_pem);
-       e_webdav_discover_free_discovered_sources (context->out_discovered_sources);
-       g_slist_free_full (context->out_calendar_user_addresses, g_free);
-       g_free (context);
-}
-
-static gchar *
-e_webdav_discover_make_href_full_uri (SoupURI *base_uri,
-                                     const gchar *href)
+e_webdav_discover_split_resources (WebDAVDiscoverData *wdd,
+                                  const GSList *resources)
 {
-       SoupURI *soup_uri;
-       gchar *full_uri;
-
-       if (!base_uri || !href)
-               return g_strdup (href);
-
-       if (strstr (href, "://"))
-               return g_strdup (href);
-
-       soup_uri = soup_uri_copy (base_uri);
-       soup_uri_set_path (soup_uri, href);
-       soup_uri_set_user (soup_uri, NULL);
-       soup_uri_set_password (soup_uri, NULL);
-
-       full_uri = soup_uri_to_string (soup_uri, FALSE);
-
-       soup_uri_free (soup_uri);
-
-       return full_uri;
-}
-
-static void
-e_webdav_discover_redirect (SoupMessage *message,
-                           SoupSession *session)
-{
-       SoupURI *soup_uri;
-       const gchar *location;
-
-       if (!SOUP_STATUS_IS_REDIRECTION (message->status_code))
-               return;
-
-       location = soup_message_headers_get_list (message->response_headers, "Location");
-
-       if (location == NULL)
-               return;
-
-       soup_uri = soup_uri_new_with_base (soup_message_get_uri (message), location);
-
-       if (soup_uri == NULL) {
-               soup_message_set_status_full (
-                       message, SOUP_STATUS_MALFORMED,
-                       _("Invalid Redirect URL"));
-               return;
-       }
-
-       soup_message_set_uri (message, soup_uri);
-       soup_session_requeue_message (session, message);
-
-       soup_uri_free (soup_uri);
-}
-
-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
-}
-
-static G_GNUC_NULL_TERMINATED SoupMessage *
-e_webdav_discover_new_propfind (SoupSession *session,
-                               SoupURI *soup_uri,
-                               gint depth,
-                               ...)
-{
-       GHashTable *namespaces;
-       SoupMessage *message;
-       xmlDocPtr doc;
-       xmlNodePtr root;
-       xmlNodePtr node;
-       xmlNsPtr ns;
-       xmlOutputBufferPtr output;
-       gconstpointer content;
-       gsize length;
-       gpointer key;
-       va_list va;
-
-       /* Construct the XML content. */
-
-       doc = xmlNewDoc (XC ("1.0"));
-       node = xmlNewDocNode (doc, NULL, XC ("propfind"), NULL);
-
-       /* Build a hash table of namespace URIs to xmlNs structs. */
-       namespaces = g_hash_table_new (NULL, NULL);
-
-       ns = xmlNewNs (node, XC (NS_CALDAV), XC ("C"));
-       g_hash_table_insert (namespaces, (gpointer) NS_CALDAV, ns);
-
-       ns = xmlNewNs (node, XC (NS_CARDDAV), XC ("A"));
-       g_hash_table_insert (namespaces, (gpointer) NS_CARDDAV, ns);
-
-       ns = xmlNewNs (node, XC (NS_ICAL), XC ("IC"));
-       g_hash_table_insert (namespaces, (gpointer) NS_ICAL, ns);
-
-       /* Add WebDAV last since we use it below. */
-       ns = xmlNewNs (node, XC (NS_WEBDAV), XC ("D"));
-       g_hash_table_insert (namespaces, (gpointer) NS_WEBDAV, ns);
-
-       xmlSetNs (node, ns);
-       xmlDocSetRootElement (doc, node);
-
-       node = xmlNewTextChild (node, ns, XC ("prop"), NULL);
-
-       va_start (va, depth);
-       while ((key = va_arg (va, gpointer)) != NULL) {
-               xmlChar *name;
-
-               ns = g_hash_table_lookup (namespaces, key);
-               name = va_arg (va, xmlChar *);
-
-               if (ns != NULL && name != NULL)
-                       xmlNewTextChild (node, ns, name, NULL);
-               else
-                       g_warn_if_reached ();
-       }
-       va_end (va);
-
-       g_hash_table_destroy (namespaces);
-
-       /* Construct the SoupMessage. */
-
-       message = soup_message_new_from_uri (SOUP_METHOD_PROPFIND, soup_uri);
-
-       soup_message_set_flags (message, SOUP_MESSAGE_NO_REDIRECT);
-
-       soup_message_headers_append (
-               message->request_headers,
-               "User-Agent", "Evolution/" VERSION);
-
-       soup_message_headers_append (
-               message->request_headers,
-               "Connection", "close");
-
-       soup_message_headers_append (
-               message->request_headers,
-               "Depth", (depth == 0) ? "0" : "1");
-
-       output = xmlAllocOutputBuffer (NULL);
-
-       root = xmlDocGetRootElement (doc);
-       xmlNodeDumpOutput (output, doc, root, 0, 1, NULL);
-       xmlOutputBufferFlush (output);
-
-       content = compat_libxml_output_buffer_get_content (output, &length);
-
-       soup_message_set_request (
-               message, "application/xml", SOUP_MEMORY_COPY,
-               content, length);
-
-       xmlOutputBufferClose (output);
-       xmlFreeDoc (doc);
-
-       soup_message_add_header_handler (
-               message, "got-body", "Location",
-               G_CALLBACK (e_webdav_discover_redirect), session);
-
-       return message;
-}
-
-static xmlXPathObjectPtr
-e_webdav_discover_get_xpath (xmlXPathContextPtr xp_ctx,
-                            const gchar *path_format,
-                            ...)
-{
-       xmlXPathObjectPtr xp_obj;
-       va_list va;
-       gchar *path;
-
-       va_start (va, path_format);
-       path = g_strdup_vprintf (path_format, va);
-       va_end (va);
-
-       xp_obj = xmlXPathEvalExpression (XC (path), xp_ctx);
-
-       g_free (path);
-
-       if (xp_obj == NULL)
-               return NULL;
-
-       if (xp_obj->type != XPATH_NODESET) {
-               xmlXPathFreeObject (xp_obj);
-               return NULL;
-       }
-
-       if (xmlXPathNodeSetGetLength (xp_obj->nodesetval) == 0) {
-               xmlXPathFreeObject (xp_obj);
-               return NULL;
-       }
-
-       return xp_obj;
-}
-
-static gchar *
-e_webdav_discover_get_xpath_string (xmlXPathContextPtr xp_ctx,
-                                   const gchar *path_format,
-                                   ...)
-{
-       xmlXPathObjectPtr xp_obj;
-       va_list va;
-       gchar *path;
-       gchar *expression;
-       gchar *string = NULL;
-
-       va_start (va, path_format);
-       path = g_strdup_vprintf (path_format, va);
-       va_end (va);
-
-       expression = g_strdup_printf ("string(%s)", path);
-       xp_obj = xmlXPathEvalExpression (XC (expression), xp_ctx);
-       g_free (expression);
-
-       g_free (path);
-
-       if (xp_obj == NULL)
-               return NULL;
-
-       if (xp_obj->type == XPATH_STRING)
-               string = g_strdup ((gchar *) xp_obj->stringval);
-
-       /* If the string is empty, return NULL. */
-       if (string != NULL && *string == '\0') {
-               g_free (string);
-               string = NULL;
+       const GSList *link;
+
+       g_return_if_fail (wdd != NULL);
+
+       for (link = resources; link; link = g_slist_next (link)) {
+               const EWebDAVResource *resource = link->data;
+
+               if (resource && (
+                   resource->kind == E_WEBDAV_RESOURCE_KIND_ADDRESSBOOK ||
+                   resource->kind == E_WEBDAV_RESOURCE_KIND_CALENDAR)) {
+                       EWebDAVDiscoveredSource *discovered;
+
+                       if (resource->kind == E_WEBDAV_RESOURCE_KIND_CALENDAR &&
+                           wdd->only_supports != E_WEBDAV_DISCOVER_SUPPORTS_NONE &&
+                           (resource->supports & wdd->only_supports) == 0)
+                               continue;
+
+                       discovered = g_new0 (EWebDAVDiscoveredSource, 1);
+                       discovered->href = g_strdup (resource->href);
+                       discovered->supports = resource->supports;
+                       discovered->display_name = g_strdup (resource->display_name);
+                       discovered->description = g_strdup (resource->description);
+                       discovered->color = g_strdup (resource->color);
+
+                       if (resource->kind == E_WEBDAV_RESOURCE_KIND_ADDRESSBOOK) {
+                               wdd->addressbooks = g_slist_prepend (wdd->addressbooks, discovered);
+                       } else {
+                               wdd->calendars = g_slist_prepend (wdd->calendars, discovered);
+                       }
+               }
        }
-
-       xmlXPathFreeObject (xp_obj);
-
-       return string;
 }
 
 static gboolean
-e_webdav_discover_setup_bearer_auth (ESource *source,
-                                    const ENamedParameters *credentials,
-                                    ESoupAuthBearer *bearer,
-                                    GCancellable *cancellable,
-                                    GError **error)
-{
-       gchar *access_token = NULL;
-       gint expires_in_seconds = -1;
-       gboolean success = FALSE;
-
-       g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
-       g_return_val_if_fail (credentials != NULL, FALSE);
-
-       success = e_util_get_source_oauth2_access_token_sync (source, credentials,
-               &access_token, &expires_in_seconds, cancellable, error);
-
-       if (success)
-               e_soup_auth_bearer_set_access_token (bearer, access_token, expires_in_seconds);
-
-       g_free (access_token);
-
-       return success;
-}
-
-typedef struct _AuthenticateData {
-       ESource *source;
-       const ENamedParameters *credentials;
-} AuthenticateData;
+e_webdav_discover_propfind_uri_sync (EWebDAVSession *webdav,
+                                    WebDAVDiscoverData *wdd,
+                                    const gchar *uri,
+                                    gboolean only_sets);
 
-static void
-e_webdav_discover_authenticate_cb (SoupSession *session,
-                                  SoupMessage *msg,
-                                  SoupAuth *auth,
-                                  gboolean retrying,
-                                  gpointer user_data)
+static gboolean
+e_webdav_discover_traverse_propfind_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)
 {
-       AuthenticateData *auth_data = user_data;
-
-       g_return_if_fail (auth_data != NULL);
-
-       if (retrying)
-               return;
-
-       if (E_IS_SOUP_AUTH_BEARER (auth)) {
-               GError *local_error = NULL;
-
-               e_webdav_discover_setup_bearer_auth (auth_data->source, auth_data->credentials,
-                       E_SOUP_AUTH_BEARER (auth), NULL, &local_error);
-
-               if (local_error != NULL) {
-                       soup_message_set_status_full (msg, SOUP_STATUS_FORBIDDEN, local_error->message);
-
-                       g_error_free (local_error);
-               }
-       } else {
-               gchar *auth_user = NULL;
+       WebDAVDiscoverData *wdd = user_data;
 
-               if (e_named_parameters_get (auth_data->credentials, E_SOURCE_CREDENTIAL_USERNAME))
-                       auth_user = g_strdup (e_named_parameters_get (auth_data->credentials, 
E_SOURCE_CREDENTIAL_USERNAME));
-
-               if (auth_user && !*auth_user) {
-                       g_free (auth_user);
-                       auth_user = NULL;
-               }
+       g_return_val_if_fail (wdd != NULL, FALSE);
 
-               if (!auth_user) {
-                       ESourceAuthentication *auth_extension;
-
-                       auth_extension = e_source_get_extension (auth_data->source, 
E_SOURCE_EXTENSION_AUTHENTICATION);
-                       auth_user = e_source_authentication_dup_user (auth_extension);
-               }
+       if (!xpath_prop_prefix) {
+               e_xml_xpath_context_register_namespaces (xpath_ctx,
+                       "C", E_WEBDAV_NS_CALDAV,
+                       "A", E_WEBDAV_NS_CARDDAV,
+                       NULL);
+       } else if (status_code == SOUP_STATUS_OK) {
+               xmlXPathObjectPtr xpath_obj;
+               gchar *principal_href, *full_href;
 
-               if (!auth_user || !*auth_user || !auth_data->credentials || !e_named_parameters_get 
(auth_data->credentials, E_SOURCE_CREDENTIAL_PASSWORD))
-                       soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
-               else
-                       soup_auth_authenticate (auth, auth_user, e_named_parameters_get 
(auth_data->credentials, E_SOURCE_CREDENTIAL_PASSWORD));
+               xpath_obj = e_xml_xpath_eval (xpath_ctx, "%s/A:addressbook-home-set", xpath_prop_prefix);
+               if (xpath_obj) {
+                       gint ii, length;
 
-               g_free (auth_user);
-       }
-}
+                       length = xmlXPathNodeSetGetLength (xpath_obj->nodesetval);
 
-static gboolean
-e_webdav_discover_check_successful (SoupMessage *message,
-                                   gchar **out_certificate_pem,
-                                   GTlsCertificateFlags *out_certificate_errors,
-                                   GError **error)
-{
-       GIOErrorEnum error_code;
+                       for (ii = 0; ii < length; ii++) {
+                               gchar *home_set_href;
 
-       g_return_val_if_fail (message != NULL, FALSE);
+                               full_href = NULL;
 
-       /* Loosely copied from the GVFS DAV backend. */
+                               home_set_href = e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/A:addressbook-home-set/D:href[%d]", xpath_prop_prefix, ii + 1);
+                               if (home_set_href && *home_set_href) {
+                                       GSList *resources = NULL;
 
-       if (SOUP_STATUS_IS_SUCCESSFUL (message->status_code))
-               return TRUE;
+                                       full_href = e_webdav_session_ensure_full_uri (webdav, request_uri, 
home_set_href);
+                                       if (full_href && *full_href && !g_hash_table_contains 
(wdd->covered_hrefs, full_href) &&
+                                           e_webdav_session_list_sync (webdav, full_href, 
E_WEBDAV_DEPTH_THIS_AND_CHILDREN,
+                                               E_WEBDAV_LIST_SUPPORTS | E_WEBDAV_LIST_DISPLAY_NAME | 
E_WEBDAV_LIST_DESCRIPTION | E_WEBDAV_LIST_COLOR,
+                                               &resources, wdd->cancellable, wdd->error)) {
+                                               e_webdav_discover_split_resources (wdd, resources);
+                                               g_slist_free_full (resources, e_webdav_resource_free);
+                                       }
 
-       switch (message->status_code) {
-               case SOUP_STATUS_CANCELLED:
-                       error_code = G_IO_ERROR_CANCELLED;
-                       break;
-               case SOUP_STATUS_NOT_FOUND:
-                       error_code = G_IO_ERROR_NOT_FOUND;
-                       break;
-               case SOUP_STATUS_UNAUTHORIZED:
-               case SOUP_STATUS_FORBIDDEN:
-                       g_set_error (
-                               error, SOUP_HTTP_ERROR, message->status_code,
-                               _("HTTP Error: %s"), message->reason_phrase);
-                       return FALSE;
-               case SOUP_STATUS_PAYMENT_REQUIRED:
-                       error_code = G_IO_ERROR_PERMISSION_DENIED;
-                       break;
-               case SOUP_STATUS_REQUEST_TIMEOUT:
-                       error_code = G_IO_ERROR_TIMED_OUT;
-                       break;
-               case SOUP_STATUS_CANT_RESOLVE:
-                       error_code = G_IO_ERROR_HOST_NOT_FOUND;
-                       break;
-               case SOUP_STATUS_NOT_IMPLEMENTED:
-                       error_code = G_IO_ERROR_NOT_SUPPORTED;
-                       break;
-               case SOUP_STATUS_INSUFFICIENT_STORAGE:
-                       error_code = G_IO_ERROR_NO_SPACE;
-                       break;
-               case SOUP_STATUS_SSL_FAILED:
-                       if (out_certificate_pem) {
-                               GTlsCertificate *certificate = NULL;
-
-                               g_free (*out_certificate_pem);
-                               *out_certificate_pem = NULL;
-
-                               g_object_get (G_OBJECT (message), "tls-certificate", &certificate, NULL);
-
-                               if (certificate) {
-                                       g_object_get (certificate, "certificate-pem", out_certificate_pem, 
NULL);
-                                       g_object_unref (certificate);
+                                       if (full_href && *full_href)
+                                               g_hash_table_insert (wdd->covered_hrefs, g_strdup 
(full_href), GINT_TO_POINTER (1));
                                }
-                       }
 
-                       if (out_certificate_errors) {
-                               *out_certificate_errors = 0;
-                               g_object_get (G_OBJECT (message), "tls-errors", out_certificate_errors, NULL);
+                               g_free (home_set_href);
+                               g_free (full_href);
                        }
 
-                       g_set_error (
-                               error, SOUP_HTTP_ERROR, message->status_code,
-                               _("HTTP Error: %s"), message->reason_phrase);
-                       return FALSE;
-               default:
-                       error_code = G_IO_ERROR_FAILED;
-                       break;
-       }
-
-       g_set_error (
-               error, G_IO_ERROR, error_code,
-               _("HTTP Error: %s"), message->reason_phrase);
-
-       return FALSE;
-}
-
-static xmlDocPtr
-e_webdav_discover_parse_xml (SoupMessage *message,
-                            const gchar *expected_name,
-                            gchar **out_certificate_pem,
-                            GTlsCertificateFlags *out_certificate_errors,
-                            GError **error)
-{
-       xmlDocPtr doc;
-       xmlNodePtr root;
-
-       if (!e_webdav_discover_check_successful (message, out_certificate_pem, out_certificate_errors, error))
-               return NULL;
-
-       doc = xmlReadMemory (
-               message->response_body->data,
-               message->response_body->length,
-               "response.xml", NULL,
-               XML_PARSE_NONET |
-               XML_PARSE_NOWARNING |
-               XML_PARSE_NOCDATA |
-               XML_PARSE_COMPACT);
-
-       if (doc == NULL) {
-               g_set_error_literal (
-                       error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       _("Could not parse response"));
-               return NULL;
-       }
-
-       root = xmlDocGetRootElement (doc);
-
-       if (root == NULL || root->children == NULL) {
-               g_set_error_literal (
-                       error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       _("Empty response"));
-               xmlFreeDoc (doc);
-               return NULL;
-       }
-
-       if (g_strcmp0 ((gchar *) root->name, expected_name) != 0) {
-               g_set_error_literal (
-                       error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       _("Unexpected reply from server"));
-               xmlFreeDoc (doc);
-               return NULL;
-       }
-
-       return doc;
-}
-
-static void
-e_webdav_discover_process_user_address_set (xmlXPathContextPtr xp_ctx,
-                                           GSList **out_calendar_user_addresses)
-{
-       xmlXPathObjectPtr xp_obj;
-       gint ii, length;
-
-       if (!out_calendar_user_addresses)
-               return;
-
-       xp_obj = e_webdav_discover_get_xpath (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response"
-               "/D:propstat"
-               "/D:prop"
-               "/C:calendar-user-address-set");
-
-       if (xp_obj == NULL)
-               return;
-
-       length = xmlXPathNodeSetGetLength (xp_obj->nodesetval);
-
-       for (ii = 0; ii < length; ii++) {
-               GSList *duplicate;
-               const gchar *address;
-               gchar *href;
-
-               href = e_webdav_discover_get_xpath_string (
-                       xp_ctx,
-                       "/D:multistatus"
-                       "/D:response"
-                       "/D:propstat"
-                       "/D:prop"
-                       "/C:calendar-user-address-set"
-                       "/D:href[%d]", ii + 1);
-
-               if (href == NULL)
-                       continue;
-
-               if (!g_str_has_prefix (href, "mailto:";)) {
-                       g_free (href);
-                       continue;
-               }
-
-               /* strlen("mailto:";) == 7 */
-               address = href + 7;
-
-               /* Avoid duplicates. */
-               duplicate = g_slist_find_custom (
-                       *out_calendar_user_addresses,
-                       address, (GCompareFunc) g_ascii_strcasecmp);
-
-               if (duplicate != NULL) {
-                       g_free (href);
-                       continue;
+                       xmlXPathFreeObject (xpath_obj);
                }
 
-               *out_calendar_user_addresses = g_slist_prepend (
-                       *out_calendar_user_addresses, g_strdup (address));
-
-               g_free (href);
-       }
-
-       xmlXPathFreeObject (xp_obj);
-}
-
-static guint32
-e_webdav_discover_get_supported_component_set (xmlXPathContextPtr xp_ctx,
-                                              gint response_index,
-                                              gint propstat_index)
-{
-       xmlXPathObjectPtr xp_obj;
-       guint32 set = 0;
-       gint ii, length;
-
-       xp_obj = e_webdav_discover_get_xpath (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response[%d]"
-               "/D:propstat[%d]"
-               "/D:prop"
-               "/C:supported-calendar-component-set"
-               "/C:comp",
-               response_index,
-               propstat_index);
-
-       /* If the property is not present, assume all component
-        * types are supported.  (RFC 4791, Section 5.2.3) */
-       if (xp_obj == NULL)
-               return E_WEBDAV_DISCOVER_SUPPORTS_EVENTS |
-                      E_WEBDAV_DISCOVER_SUPPORTS_MEMOS |
-                      E_WEBDAV_DISCOVER_SUPPORTS_TASKS;
-
-       length = xmlXPathNodeSetGetLength (xp_obj->nodesetval);
-
-       for (ii = 0; ii < length; ii++) {
-               gchar *name;
-
-               name = e_webdav_discover_get_xpath_string (
-                       xp_ctx,
-                       "/D:multistatus"
-                       "/D:response[%d]"
-                       "/D:propstat[%d]"
-                       "/D:prop"
-                       "/C:supported-calendar-component-set"
-                       "/C:comp[%d]"
-                       "/@name",
-                       response_index,
-                       propstat_index,
-                       ii + 1);
-
-               if (name == NULL)
-                       continue;
-
-               if (g_ascii_strcasecmp (name, "VEVENT") == 0)
-                       set |= E_WEBDAV_DISCOVER_SUPPORTS_EVENTS;
-               else if (g_ascii_strcasecmp (name, "VJOURNAL") == 0)
-                       set |= E_WEBDAV_DISCOVER_SUPPORTS_MEMOS;
-               else if (g_ascii_strcasecmp (name, "VTODO") == 0)
-                       set |= E_WEBDAV_DISCOVER_SUPPORTS_TASKS;
-
-               g_free (name);
-       }
-
-       xmlXPathFreeObject (xp_obj);
-
-       return set;
-}
-
-static void
-e_webdav_discover_process_calendar_response_propstat (SoupMessage *message,
-                                                     xmlXPathContextPtr xp_ctx,
-                                                     gint response_index,
-                                                     gint propstat_index,
-                                                     GSList **out_discovered_sources)
-{
-       xmlXPathObjectPtr xp_obj;
-       guint32 comp_set;
-       gchar *color_spec;
-       gchar *display_name;
-       gchar *description;
-       gchar *href_encoded;
-       gchar *status_line;
-       guint status;
-       gboolean success;
-       EWebDAVDiscoveredSource *discovered_source;
-
-       if (!out_discovered_sources)
-               return;
-
-       status_line = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response[%d]"
-               "/D:propstat[%d]"
-               "/D:status",
-               response_index,
-               propstat_index);
-
-       if (status_line == NULL)
-               return;
-
-       success = soup_headers_parse_status_line (
-               status_line, NULL, &status, NULL);
-
-       g_free (status_line);
+               xpath_obj = e_xml_xpath_eval (xpath_ctx, "%s/C:calendar-home-set", xpath_prop_prefix);
+               if (xpath_obj) {
+                       gint ii, length;
 
-       if (!success || status != SOUP_STATUS_OK)
-               return;
+                       length = xmlXPathNodeSetGetLength (xpath_obj->nodesetval);
 
-       comp_set = e_webdav_discover_get_supported_component_set (xp_ctx, response_index, propstat_index);
-       if (comp_set == E_WEBDAV_DISCOVER_SUPPORTS_NONE)
-               return;
+                       for (ii = 0; ii < length; ii++) {
+                               gchar *home_set_href, *full_href = NULL;
 
-       href_encoded = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response[%d]"
-               "/D:href",
-               response_index);
+                               home_set_href = e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/C:calendar-home-set/D:href[%d]", xpath_prop_prefix, ii + 1);
+                               if (home_set_href && *home_set_href) {
+                                       GSList *resources = NULL;
 
-       if (href_encoded == NULL)
-               return;
-
-       /* Make sure the resource is a calendar. */
-
-       xp_obj = e_webdav_discover_get_xpath (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response[%d]"
-               "/D:propstat[%d]"
-               "/D:prop"
-               "/D:resourcetype"
-               "/C:calendar",
-               response_index,
-               propstat_index);
-
-       if (xp_obj == NULL) {
-               g_free (href_encoded);
-               return;
-       }
+                                       full_href = e_webdav_session_ensure_full_uri (webdav, request_uri, 
home_set_href);
+                                       if (full_href && *full_href && !g_hash_table_contains 
(wdd->covered_hrefs, full_href) &&
+                                           e_webdav_session_list_sync (webdav, full_href, 
E_WEBDAV_DEPTH_THIS_AND_CHILDREN,
+                                               E_WEBDAV_LIST_SUPPORTS | E_WEBDAV_LIST_DISPLAY_NAME | 
E_WEBDAV_LIST_DESCRIPTION | E_WEBDAV_LIST_COLOR,
+                                               &resources, wdd->cancellable, wdd->error)) {
+                                               e_webdav_discover_split_resources (wdd, resources);
+                                               g_slist_free_full (resources, e_webdav_resource_free);
+                                       }
 
-       xmlXPathFreeObject (xp_obj);
-
-       /* Get the display name or fall back to the href. */
-
-       display_name = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response[%d]"
-               "/D:propstat[%d]"
-               "/D:prop"
-               "/D:displayname",
-               response_index,
-               propstat_index);
-
-       if (display_name == NULL) {
-               gchar *href_decoded = soup_uri_decode (href_encoded);
-
-               if (href_decoded) {
-                       gchar *cp;
-
-                       /* Use the last non-empty path segment. */
-                       while ((cp = strrchr (href_decoded, '/')) != NULL) {
-                               if (*(cp + 1) == '\0')
-                                       *cp = '\0';
-                               else {
-                                       display_name = g_strdup (cp + 1);
-                                       break;
+                                       if (full_href && *full_href)
+                                               g_hash_table_insert (wdd->covered_hrefs, g_strdup 
(full_href), GINT_TO_POINTER (1));
                                }
-                       }
-               }
-
-               g_free (href_decoded);
-       }
-
-       description = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response[%d]"
-               "/D:propstat[%d]"
-               "/D:prop"
-               "/C:calendar-description",
-               response_index,
-               propstat_index);
-
-       /* Get the color specification string. */
-
-       color_spec = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response[%d]"
-               "/D:propstat[%d]"
-               "/D:prop"
-               "/IC:calendar-color",
-               response_index,
-               propstat_index);
-
-       discovered_source = g_new0 (EWebDAVDiscoveredSource, 1);
-       discovered_source->href = e_webdav_discover_make_href_full_uri (soup_message_get_uri (message), 
href_encoded);
-       discovered_source->supports = comp_set;
-       discovered_source->display_name = g_strdup (display_name);
-       discovered_source->description = g_strdup (description);
-       discovered_source->color = g_strdup (color_spec);
-
-       *out_discovered_sources = g_slist_prepend (*out_discovered_sources, discovered_source);
-
-       g_free (href_encoded);
-       g_free (display_name);
-       g_free (description);
-       g_free (color_spec);
-}
 
-static void
-e_webdav_discover_traverse_responses (SoupMessage *message,
-                                     xmlXPathContextPtr xp_ctx,
-                                     GSList **out_discovered_sources,
-                                     void (* func) (
-                                               SoupMessage *message,
-                                               xmlXPathContextPtr xp_ctx,
-                                               gint response_index,
-                                               gint propstat_index,
-                                               GSList **out_discovered_sources))
-{
-       xmlXPathObjectPtr xp_obj_response;
-
-       xp_obj_response = e_webdav_discover_get_xpath (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response");
-
-       if (xp_obj_response != NULL) {
-               gint response_index, response_length;
+                               g_free (home_set_href);
+                               g_free (full_href);
+                       }
 
-               response_length = xmlXPathNodeSetGetLength (xp_obj_response->nodesetval);
+                       xmlXPathFreeObject (xpath_obj);
+               }
 
-               for (response_index = 0; response_index < response_length; response_index++) {
-                       xmlXPathObjectPtr xp_obj_propstat;
+               xpath_obj = e_xml_xpath_eval (xpath_ctx, "%s/C:calendar-user-address-set", xpath_prop_prefix);
+               if (xpath_obj) {
+                       gint ii, length;
 
-                       xp_obj_propstat = e_webdav_discover_get_xpath (
-                               xp_ctx,
-                               "/D:multistatus"
-                               "/D:response[%d]"
-                               "/D:propstat",
-                               response_index + 1);
+                       length = xmlXPathNodeSetGetLength (xpath_obj->nodesetval);
 
-                       if (xp_obj_propstat != NULL) {
-                               gint propstat_index, propstat_length;
+                       for (ii = 0; ii < length; ii++) {
+                               gchar *address_href;
 
-                               propstat_length = xmlXPathNodeSetGetLength (xp_obj_propstat->nodesetval);
+                               address_href = e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/C:calendar-user-address-set/D:href[%d]", xpath_prop_prefix, ii + 1);
+                               if (address_href && g_ascii_strncasecmp (address_href, "mailto:";, 7) == 0) {
+                                       /* Skip the "mailto:"; prefix */
+                                       const gchar *address = address_href + 7;
 
-                               for (propstat_index = 0; propstat_index < propstat_length; propstat_index++) {
-                                       func (message, xp_ctx, response_index + 1, propstat_index + 1, 
out_discovered_sources);
+                                       /* Avoid duplicates and empty values */
+                                       if (*address &&
+                                           !g_slist_find_custom (*wdd->out_calendar_user_addresses, address, 
(GCompareFunc) g_ascii_strcasecmp)) {
+                                               *wdd->out_calendar_user_addresses = g_slist_prepend (
+                                                       *wdd->out_calendar_user_addresses, g_strdup 
(address));
+                                       }
                                }
 
-                               xmlXPathFreeObject (xp_obj_propstat);
+                               g_free (address_href);
                        }
-               }
 
-               xmlXPathFreeObject (xp_obj_response);
-       }
-}
-
-static gboolean
-e_webdav_discover_get_calendar_collection_details (SoupSession *session,
-                                                  SoupMessage *message,
-                                                  const gchar *path_or_uri,
-                                                  ESource *source,
-                                                  gchar **out_certificate_pem,
-                                                  GTlsCertificateFlags *out_certificate_errors,
-                                                  GSList **out_discovered_sources,
-                                                  GCancellable *cancellable,
-                                                  GError **error)
-{
-       xmlDocPtr doc;
-       xmlXPathContextPtr xp_ctx;
-       SoupURI *soup_uri;
-       GError *local_error = NULL;
-
-       if (g_cancellable_is_cancelled (cancellable))
-               return FALSE;
-
-       soup_uri = soup_uri_new (path_or_uri);
-       if (!soup_uri ||
-           !soup_uri_get_scheme (soup_uri) ||
-           !soup_uri_get_host (soup_uri) ||
-           !soup_uri_get_path (soup_uri) ||
-           !*soup_uri_get_scheme (soup_uri) ||
-           !*soup_uri_get_host (soup_uri) ||
-           !*soup_uri_get_path (soup_uri)) {
-               /* it's a path only, not full uri */
-               if (soup_uri)
-                       soup_uri_free (soup_uri);
-               soup_uri = soup_uri_copy (soup_message_get_uri (message));
-               soup_uri_set_path (soup_uri, path_or_uri);
-       }
+                       xmlXPathFreeObject (xpath_obj);
+               }
 
-       message = e_webdav_discover_new_propfind (
-               session, soup_uri, DEPTH_1,
-               NS_WEBDAV, XC ("displayname"),
-               NS_WEBDAV, XC ("resourcetype"),
-               NS_CALDAV, XC ("calendar-description"),
-               NS_CALDAV, XC ("supported-calendar-component-set"),
-               NS_CALDAV, XC ("calendar-user-address-set"),
-               NS_ICAL,   XC ("calendar-color"),
-               NULL);
-
-       e_soup_ssl_trust_connect (message, source);
-
-       /* This takes ownership of the message. */
-       soup_session_send_message (session, message);
-
-       if (message->status_code == SOUP_STATUS_BAD_REQUEST) {
-               g_clear_object (&message);
-
-               message = e_webdav_discover_new_propfind (
-                       session, soup_uri, DEPTH_0,
-                       NS_WEBDAV, XC ("displayname"),
-                       NS_WEBDAV, XC ("resourcetype"),
-                       NS_CALDAV, XC ("calendar-description"),
-                       NS_CALDAV, XC ("supported-calendar-component-set"),
-                       NS_CALDAV, XC ("calendar-user-address-set"),
-                       NS_ICAL,   XC ("calendar-color"),
-                       NULL);
+               principal_href = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:current-user-principal/D:href", 
xpath_prop_prefix);
+               if (principal_href && *principal_href) {
+                       full_href = e_webdav_session_ensure_full_uri (webdav, request_uri, principal_href);
 
-               e_soup_ssl_trust_connect (message, source);
-               soup_session_send_message (session, message);
-       }
+                       if (full_href && *full_href)
+                               e_webdav_discover_propfind_uri_sync (webdav, wdd, full_href, TRUE);
 
-       soup_uri_free (soup_uri);
-
-       doc = e_webdav_discover_parse_xml (message, "multistatus", out_certificate_pem, 
out_certificate_errors, &local_error);
-       if (!doc) {
-               g_clear_object (&message);
+                       g_free (full_href);
+                       g_free (principal_href);
 
-               if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
-                   g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
-                       /* Ignore these errors, but still propagate them. */
-                       g_propagate_error (error, local_error);
                        return TRUE;
-               } else if (local_error) {
-                       g_propagate_error (error, local_error);
                }
 
-               return FALSE;
-       }
-
-       xp_ctx = xmlXPathNewContext (doc);
-       xmlXPathRegisterNs (xp_ctx, XC ("D"), XC (NS_WEBDAV));
-       xmlXPathRegisterNs (xp_ctx, XC ("C"), XC (NS_CALDAV));
-       xmlXPathRegisterNs (xp_ctx, XC ("A"), XC (NS_CARDDAV));
-       xmlXPathRegisterNs (xp_ctx, XC ("IC"), XC (NS_ICAL));
-
-       e_webdav_discover_traverse_responses (message, xp_ctx, out_discovered_sources,
-               e_webdav_discover_process_calendar_response_propstat);
-
-       xmlXPathFreeContext (xp_ctx);
-       xmlFreeDoc (doc);
-
-       g_clear_object (&message);
-
-       return TRUE;
-}
-
-static gboolean
-e_webdav_discover_process_calendar_home_set (SoupSession *session,
-                                            SoupMessage *message,
-                                            ESource *source,
-                                            gchar **out_certificate_pem,
-                                            GTlsCertificateFlags *out_certificate_errors,
-                                            GSList **out_discovered_sources,
-                                            GSList **out_calendar_user_addresses,
-                                            GCancellable *cancellable,
-                                            GError **error)
-{
-       SoupURI *soup_uri;
-       xmlDocPtr doc;
-       xmlXPathContextPtr xp_ctx;
-       xmlXPathObjectPtr xp_obj;
-       gchar *calendar_home_set;
-       GError *local_error = NULL;
-       gboolean success;
+               g_free (principal_href);
 
-       g_return_val_if_fail (SOUP_IS_SESSION (session), FALSE);
-       g_return_val_if_fail (SOUP_IS_MESSAGE (message), FALSE);
-       g_return_val_if_fail (out_discovered_sources != NULL, FALSE);
-       g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+               principal_href = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:principal-URL/D:href", 
xpath_prop_prefix);
+               if (principal_href && *principal_href) {
+                       full_href = e_webdav_session_ensure_full_uri (webdav, request_uri, principal_href);
 
-       if (g_cancellable_is_cancelled (cancellable))
-               return FALSE;
+                       if (full_href && *full_href)
+                               e_webdav_discover_propfind_uri_sync (webdav, wdd, full_href, TRUE);
 
-       doc = e_webdav_discover_parse_xml (message, "multistatus", out_certificate_pem, 
out_certificate_errors, &local_error);
+                       g_free (full_href);
+                       g_free (principal_href);
 
-       if (!doc) {
-               if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
-                   g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
-                       /* Ignore these errors, but still propagate them. */
-                       g_propagate_error (error, local_error);
                        return TRUE;
-               } else if (local_error) {
-                       g_propagate_error (error, local_error);
                }
 
-               return FALSE;
-       }
+               g_free (principal_href);
 
-       xp_ctx = xmlXPathNewContext (doc);
-       xmlXPathRegisterNs (xp_ctx, XC ("D"), XC (NS_WEBDAV));
-       xmlXPathRegisterNs (xp_ctx, XC ("C"), XC (NS_CALDAV));
-       xmlXPathRegisterNs (xp_ctx, XC ("A"), XC (NS_CARDDAV));
-
-       /* Record any "C:calendar-user-address-set" properties. */
-       e_webdav_discover_process_user_address_set (xp_ctx, out_calendar_user_addresses);
-
-       /* Try to find the calendar home URL using the
-        * following properties in order of preference:
-        *
-        *   "C:calendar-home-set"
-        *   "D:current-user-principal"
-        *   "D:principal-URL"
-        *
-        * If the second or third URL preference is used, rerun
-        * the PROPFIND method on that URL at Depth=1 in hopes
-        * of getting a proper "C:calendar-home-set" property.
-        */
-
-       /* FIXME There can be multiple "D:href" elements for a
-        *       "C:calendar-home-set".  We're only processing
-        *       the first one.  Need to iterate over them. */
-
-       calendar_home_set = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response"
-               "/D:propstat"
-               "/D:prop"
-               "/C:calendar-home-set"
-               "/D:href");
-
-       if (calendar_home_set != NULL)
-               goto get_collection_details;
-
-       g_free (calendar_home_set);
-
-       calendar_home_set = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response"
-               "/D:propstat"
-               "/D:prop"
-               "/D:current-user-principal"
-               "/D:href");
-
-       if (calendar_home_set != NULL)
-               goto retry_propfind;
-
-       g_free (calendar_home_set);
-
-       calendar_home_set = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response"
-               "/D:propstat"
-               "/D:prop"
-               "/D:principal-URL"
-               "/D:href");
-
-       if (calendar_home_set != NULL)
-               goto retry_propfind;
-
-       g_free (calendar_home_set);
-       calendar_home_set = NULL;
-
-       /* None of the aforementioned properties are present.  If the
-        * user-supplied CalDAV URL is a calendar resource, use that. */
-
-       xp_obj = e_webdav_discover_get_xpath (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response"
-               "/D:propstat"
-               "/D:prop"
-               "/D:resourcetype"
-               "/C:calendar");
-
-       if (xp_obj != NULL) {
-               soup_uri = soup_message_get_uri (message);
-
-               if (soup_uri->path != NULL && *soup_uri->path != '\0') {
-                       gchar *slash;
-
-                       soup_uri = soup_uri_copy (soup_uri);
-
-                       slash = strrchr (soup_uri->path, '/');
-                       while (slash != NULL && slash != soup_uri->path) {
-
-                               if (slash[1] != '\0') {
-                                       slash[1] = '\0';
-                                       calendar_home_set =
-                                               g_strdup (soup_uri->path);
-                                       break;
-                               }
+               if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:resourcetype/C:calendar", xpath_prop_prefix) ||
+                   e_xml_xpath_eval_exists (xpath_ctx, "%s/D:resourcetype/A:addressbook", 
xpath_prop_prefix)) {
+                       GSList *resources = NULL;
 
-                               slash[0] = '\0';
-                               slash = strrchr (soup_uri->path, '/');
+                       if (!g_hash_table_contains (wdd->covered_hrefs, href) &&
+                           e_webdav_session_list_sync (webdav, href, E_WEBDAV_DEPTH_THIS,
+                               E_WEBDAV_LIST_SUPPORTS | E_WEBDAV_LIST_DISPLAY_NAME | 
E_WEBDAV_LIST_DESCRIPTION | E_WEBDAV_LIST_COLOR,
+                               &resources, wdd->cancellable, wdd->error)) {
+                               e_webdav_discover_split_resources (wdd, resources);
+                               g_slist_free_full (resources, e_webdav_resource_free);
                        }
 
-                       soup_uri_free (soup_uri);
+                       g_hash_table_insert (wdd->covered_hrefs, g_strdup (href), GINT_TO_POINTER (1));
                }
-
-               xmlXPathFreeObject (xp_obj);
        }
 
-       if (calendar_home_set == NULL || *calendar_home_set == '\0') {
-               g_free (calendar_home_set);
-               xmlXPathFreeContext (xp_ctx);
-               xmlFreeDoc (doc);
-               return TRUE;
-       }
-
- get_collection_details:
-
-       xmlXPathFreeContext (xp_ctx);
-       xmlFreeDoc (doc);
-
-       if (!e_webdav_discover_get_calendar_collection_details (
-               session, message, calendar_home_set, source,
-               out_certificate_pem, out_certificate_errors, out_discovered_sources,
-               cancellable, error)) {
-               g_free (calendar_home_set);
-               return FALSE;
-       }
-
-       g_free (calendar_home_set);
-
        return TRUE;
-
- retry_propfind:
-
-       xmlXPathFreeContext (xp_ctx);
-       xmlFreeDoc (doc);
-
-       soup_uri = soup_uri_copy (soup_message_get_uri (message));
-       soup_uri_set_path (soup_uri, calendar_home_set);
-
-       /* Note that we omit "D:resourcetype", "D:current-user-principal"
-        * and "D:principal-URL" in order to short-circuit the recursion. */
-       message = e_webdav_discover_new_propfind (
-               session, soup_uri, DEPTH_1,
-               NS_CALDAV, XC ("calendar-home-set"),
-               NS_CALDAV, XC ("calendar-user-address-set"),
-               NULL);
-
-       e_soup_ssl_trust_connect (message, source);
-
-       /* This takes ownership of the message. */
-       soup_session_send_message (session, message);
-
-       if (message->status_code == SOUP_STATUS_BAD_REQUEST) {
-               g_clear_object (&message);
-
-               message = e_webdav_discover_new_propfind (
-                       session, soup_uri, DEPTH_0,
-                       NS_CALDAV, XC ("calendar-home-set"),
-                       NS_CALDAV, XC ("calendar-user-address-set"),
-                       NULL);
-
-               e_soup_ssl_trust_connect (message, source);
-               soup_session_send_message (session, message);
-       }
-
-       soup_uri_free (soup_uri);
-
-       g_free (calendar_home_set);
-
-       success = e_webdav_discover_process_calendar_home_set (session, message, source,
-               out_certificate_pem, out_certificate_errors, out_discovered_sources, 
out_calendar_user_addresses,
-               cancellable, error);
-
-       g_object_unref (message);
-
-       return success;
 }
 
-static void
-e_webdav_discover_process_addressbook_response_propstat (SoupMessage *message,
-                                                        xmlXPathContextPtr xp_ctx,
-                                                        gint response_index,
-                                                        gint propstat_index,
-                                                        GSList **out_discovered_sources)
+static gboolean
+e_webdav_discover_propfind_uri_sync (EWebDAVSession *webdav,
+                                    WebDAVDiscoverData *wdd,
+                                    const gchar *uri,
+                                    gboolean only_sets)
 {
-       xmlXPathObjectPtr xp_obj;
-       gchar *display_name;
-       gchar *description;
-       gchar *href_encoded;
-       gchar *status_line;
-       guint status;
+       EXmlDocument *xml;
        gboolean success;
-       EWebDAVDiscoveredSource *discovered_source;
-
-       if (!out_discovered_sources)
-               return;
-
-       status_line = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response[%d]"
-               "/D:propstat[%d]"
-               "/D:status",
-               response_index,
-               propstat_index);
-
-       if (status_line == NULL)
-               return;
 
-       success = soup_headers_parse_status_line (
-               status_line, NULL, &status, NULL);
+       g_return_val_if_fail (E_IS_WEBDAV_SESSION (webdav), FALSE);
+       g_return_val_if_fail (wdd != NULL, FALSE);
+       g_return_val_if_fail (uri && *uri, FALSE);
 
-       g_free (status_line);
+       if (g_hash_table_contains (wdd->covered_hrefs, uri))
+               return TRUE;
 
-       if (!success || status != SOUP_STATUS_OK)
-               return;
+       g_hash_table_insert (wdd->covered_hrefs, g_strdup (uri), GINT_TO_POINTER (1));
 
-       href_encoded = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response[%d]"
-               "/D:href",
-               response_index);
+       xml = e_xml_document_new (E_WEBDAV_NS_DAV, "propfind");
+       g_return_val_if_fail (xml != NULL, FALSE);
 
-       if (href_encoded == NULL)
-               return;
+       e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "prop");
 
-       /* Make sure the resource is an addressbook. */
-
-       xp_obj = e_webdav_discover_get_xpath (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response[%d]"
-               "/D:propstat[%d]"
-               "/D:prop"
-               "/D:resourcetype"
-               "/A:addressbook",
-               response_index,
-               propstat_index);
-
-       if (xp_obj == NULL) {
-               g_free (href_encoded);
-               return;
+       if (!only_sets) {
+               e_xml_document_add_empty_element (xml, E_WEBDAV_NS_DAV, "resourcetype");
+               e_xml_document_add_empty_element (xml, E_WEBDAV_NS_DAV, "current-user-principal");
+               e_xml_document_add_empty_element (xml, E_WEBDAV_NS_DAV, "principal-URL");
        }
 
-       xmlXPathFreeObject (xp_obj);
-
-       /* Get the display name or fall back to the href. */
-
-       display_name = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response[%d]"
-               "/D:propstat[%d]"
-               "/D:prop"
-               "/D:displayname",
-               response_index,
-               propstat_index);
-
-       if (display_name == NULL) {
-               gchar *href_decoded = soup_uri_decode (href_encoded);
-
-               if (href_decoded) {
-                       gchar *cp;
-
-                       /* Use the last non-empty path segment. */
-                       while ((cp = strrchr (href_decoded, '/')) != NULL) {
-                               if (*(cp + 1) == '\0')
-                                       *cp = '\0';
-                               else {
-                                       display_name = g_strdup (cp + 1);
-                                       break;
-                               }
-                       }
-               }
-
-               g_free (href_decoded);
+       if ((wdd->only_supports == E_WEBDAV_DISCOVER_SUPPORTS_NONE ||
+           (wdd->only_supports & (E_WEBDAV_DISCOVER_SUPPORTS_EVENTS | E_WEBDAV_DISCOVER_SUPPORTS_MEMOS | 
E_WEBDAV_DISCOVER_SUPPORTS_TASKS)) != 0)) {
+               e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CALDAV, "calendar-home-set");
+               e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CALDAV, "calendar-user-address-set");
        }
 
-       description = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response[%d]"
-               "/D:propstat[%d]"
-               "/D:prop"
-               "/A:addressbook-description",
-               response_index,
-               propstat_index);
-
-       discovered_source = g_new0 (EWebDAVDiscoveredSource, 1);
-       discovered_source->href = e_webdav_discover_make_href_full_uri (soup_message_get_uri (message), 
href_encoded);
-       discovered_source->supports = E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS;
-       discovered_source->display_name = g_strdup (display_name);
-       discovered_source->description = g_strdup (description);
-       discovered_source->color = NULL;
-
-       *out_discovered_sources = g_slist_prepend (*out_discovered_sources, discovered_source);
-
-       g_free (href_encoded);
-       g_free (display_name);
-       g_free (description);
-}
-
-static gboolean
-e_webdav_discover_get_addressbook_collection_details (SoupSession *session,
-                                                     SoupMessage *message,
-                                                     const gchar *path_or_uri,
-                                                     ESource *source,
-                                                     gchar **out_certificate_pem,
-                                                     GTlsCertificateFlags *out_certificate_errors,
-                                                     GSList **out_discovered_sources,
-                                                     GCancellable *cancellable,
-                                                     GError **error)
-{
-       xmlDocPtr doc;
-       xmlXPathContextPtr xp_ctx;
-       SoupURI *soup_uri;
-       GError *local_error = NULL;
-
-       if (g_cancellable_is_cancelled (cancellable))
-               return FALSE;
-
-       soup_uri = soup_uri_new (path_or_uri);
-       if (!soup_uri ||
-           !soup_uri_get_scheme (soup_uri) ||
-           !soup_uri_get_host (soup_uri) ||
-           !soup_uri_get_path (soup_uri) ||
-           !*soup_uri_get_scheme (soup_uri) ||
-           !*soup_uri_get_host (soup_uri) ||
-           !*soup_uri_get_path (soup_uri)) {
-               /* it's a path only, not full uri */
-               if (soup_uri)
-                       soup_uri_free (soup_uri);
-               soup_uri = soup_uri_copy (soup_message_get_uri (message));
-               soup_uri_set_path (soup_uri, path_or_uri);
+       if ((wdd->only_supports == E_WEBDAV_DISCOVER_SUPPORTS_NONE ||
+           (wdd->only_supports & (E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS)) != 0)) {
+               e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CARDDAV, "addressbook-home-set");
        }
 
-       message = e_webdav_discover_new_propfind (
-               session, soup_uri, DEPTH_1,
-               NS_WEBDAV, XC ("displayname"),
-               NS_WEBDAV, XC ("resourcetype"),
-               NS_CARDDAV, XC ("addressbook-description"),
-               NULL);
-
-       e_soup_ssl_trust_connect (message, source);
+       e_xml_document_end_element (xml); /* prop */
 
-       /* This takes ownership of the message. */
-       soup_session_send_message (session, message);
-
-       soup_uri_free (soup_uri);
-
-       doc = e_webdav_discover_parse_xml (message, "multistatus", out_certificate_pem, 
out_certificate_errors, &local_error);
-       if (!doc) {
-               g_clear_object (&message);
-
-               if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
-                   g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
-                       /* Ignore these errors, but still propagate them. */
-                       g_propagate_error (error, local_error);
-                       return TRUE;
-               } else if (local_error) {
-                       g_propagate_error (error, local_error);
-               }
+       success = e_webdav_session_propfind_sync (webdav, uri, E_WEBDAV_DEPTH_THIS, xml,
+               e_webdav_discover_traverse_propfind_response_cb, wdd, wdd->cancellable, wdd->error);
 
-               return FALSE;
-       }
+       g_clear_object (&xml);
 
-       xp_ctx = xmlXPathNewContext (doc);
-       xmlXPathRegisterNs (xp_ctx, XC ("D"), XC (NS_WEBDAV));
-       xmlXPathRegisterNs (xp_ctx, XC ("C"), XC (NS_CALDAV));
-       xmlXPathRegisterNs (xp_ctx, XC ("A"), XC (NS_CARDDAV));
-       xmlXPathRegisterNs (xp_ctx, XC ("IC"), XC (NS_ICAL));
+       return success;
+}
 
-       e_webdav_discover_traverse_responses (message, xp_ctx, out_discovered_sources,
-               e_webdav_discover_process_addressbook_response_propstat);
+typedef struct _EWebDAVDiscoverContext {
+       ESource *source;
+       gchar *url_use_path;
+       guint32 only_supports;
+       ENamedParameters *credentials;
+       gchar *out_certificate_pem;
+       GTlsCertificateFlags out_certificate_errors;
+       GSList *out_discovered_sources;
+       GSList *out_calendar_user_addresses;
+} EWebDAVDiscoverContext;
 
-       xmlXPathFreeContext (xp_ctx);
-       xmlFreeDoc (doc);
+static EWebDAVDiscoverContext *
+e_webdav_discover_context_new (ESource *source,
+                              const gchar *url_use_path,
+                              guint32 only_supports,
+                              const ENamedParameters *credentials)
+{
+       EWebDAVDiscoverContext *context;
 
-       g_clear_object (&message);
+       context = g_new0 (EWebDAVDiscoverContext, 1);
+       context->source = g_object_ref (source);
+       context->url_use_path = g_strdup (url_use_path);
+       context->only_supports = only_supports;
+       context->credentials = e_named_parameters_new_clone (credentials);
+       context->out_certificate_pem = NULL;
+       context->out_certificate_errors = 0;
+       context->out_discovered_sources = NULL;
+       context->out_calendar_user_addresses = NULL;
 
-       return TRUE;
+       return context;
 }
 
-static gboolean
-e_webdav_discover_process_addressbook_home_set (SoupSession *session,
-                                               SoupMessage *message,
-                                               ESource *source,
-                                               gchar **out_certificate_pem,
-                                               GTlsCertificateFlags *out_certificate_errors,
-                                               GSList **out_discovered_sources,
-                                               GCancellable *cancellable,
-                                               GError **error)
+static void
+e_webdav_discover_context_free (gpointer ptr)
 {
-       SoupURI *soup_uri;
-       xmlDocPtr doc;
-       xmlXPathContextPtr xp_ctx;
-       xmlXPathObjectPtr xp_obj;
-       gchar *addressbook_home_set;
-       GError *local_error = NULL;
-       gboolean success;
-
-       g_return_val_if_fail (SOUP_IS_SESSION (session), FALSE);
-       g_return_val_if_fail (SOUP_IS_MESSAGE (message), FALSE);
-       g_return_val_if_fail (out_discovered_sources != NULL, FALSE);
-       g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
-
-       if (g_cancellable_is_cancelled (cancellable))
-               return FALSE;
-
-       doc = e_webdav_discover_parse_xml (message, "multistatus", out_certificate_pem, 
out_certificate_errors, &local_error);
-       if (!doc) {
-               if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
-                   g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
-                       /* Ignore these errors, but still propagate them. */
-                       g_propagate_error (error, local_error);
-                       return TRUE;
-               } else if (local_error) {
-                       g_propagate_error (error, local_error);
-               }
-
-               return FALSE;
-       }
-
-       xp_ctx = xmlXPathNewContext (doc);
-       xmlXPathRegisterNs (xp_ctx, XC ("D"), XC (NS_WEBDAV));
-       xmlXPathRegisterNs (xp_ctx, XC ("C"), XC (NS_CALDAV));
-       xmlXPathRegisterNs (xp_ctx, XC ("A"), XC (NS_CARDDAV));
-
-       /* Try to find the addressbook home URL using the
-        * following properties in order of preference:
-        *
-        *   "A:addressbook-home-set"
-        *   "D:current-user-principal"
-        *   "D:principal-URL"
-        *
-        * If the second or third URL preference is used, rerun
-        * the PROPFIND method on that URL at Depth=1 in hopes
-        * of getting a proper "A:addressbook-home-set" property.
-        */
-
-       /* FIXME There can be multiple "D:href" elements for a
-        *       "A:addressbook-home-set".  We're only processing
-        *       the first one.  Need to iterate over them. */
-
-       addressbook_home_set = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response"
-               "/D:propstat"
-               "/D:prop"
-               "/A:addressbook-home-set"
-               "/D:href");
-
-       if (addressbook_home_set != NULL)
-               goto get_collection_details;
-
-       g_free (addressbook_home_set);
-
-       addressbook_home_set = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response"
-               "/D:propstat"
-               "/D:prop"
-               "/D:current-user-principal"
-               "/D:href");
-
-       if (addressbook_home_set != NULL)
-               goto retry_propfind;
-
-       g_free (addressbook_home_set);
-
-       addressbook_home_set = e_webdav_discover_get_xpath_string (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response"
-               "/D:propstat"
-               "/D:prop"
-               "/D:principal-URL"
-               "/D:href");
-
-       if (addressbook_home_set != NULL)
-               goto retry_propfind;
-
-       g_free (addressbook_home_set);
-       addressbook_home_set = NULL;
-
-       /* None of the aforementioned properties are present.  If the
-        * user-supplied CardDAV URL is an addressbook resource, use that. */
-
-       xp_obj = e_webdav_discover_get_xpath (
-               xp_ctx,
-               "/D:multistatus"
-               "/D:response"
-               "/D:propstat"
-               "/D:prop"
-               "/D:resourcetype"
-               "/A:addressbook");
-
-       if (xp_obj != NULL) {
-               soup_uri = soup_message_get_uri (message);
-
-               if (soup_uri->path != NULL && *soup_uri->path != '\0') {
-                       gchar *slash;
-
-                       soup_uri = soup_uri_copy (soup_uri);
-
-                       slash = strrchr (soup_uri->path, '/');
-                       while (slash != NULL && slash != soup_uri->path) {
-
-                               if (slash[1] != '\0') {
-                                       slash[1] = '\0';
-                                       addressbook_home_set =
-                                               g_strdup (soup_uri->path);
-                                       break;
-                               }
-
-                               slash[0] = '\0';
-                               slash = strrchr (soup_uri->path, '/');
-                       }
-
-                       soup_uri_free (soup_uri);
-               }
-
-               xmlXPathFreeObject (xp_obj);
-       }
-
-       if (addressbook_home_set == NULL || *addressbook_home_set == '\0') {
-               g_free (addressbook_home_set);
-               xmlXPathFreeContext (xp_ctx);
-               xmlFreeDoc (doc);
-               return TRUE;
-       }
-
- get_collection_details:
-
-       xmlXPathFreeContext (xp_ctx);
-       xmlFreeDoc (doc);
-
-       if (!e_webdav_discover_get_addressbook_collection_details (
-               session, message, addressbook_home_set, source,
-               out_certificate_pem, out_certificate_errors, out_discovered_sources,
-               cancellable, error)) {
-               g_free (addressbook_home_set);
-               return FALSE;
-       }
-
-       g_free (addressbook_home_set);
-
-       return TRUE;
-
- retry_propfind:
-
-       xmlXPathFreeContext (xp_ctx);
-       xmlFreeDoc (doc);
-
-       soup_uri = soup_uri_copy (soup_message_get_uri (message));
-       soup_uri_set_path (soup_uri, addressbook_home_set);
-
-       /* Note that we omit "D:resourcetype", "D:current-user-principal"
-        * and "D:principal-URL" in order to short-circuit the recursion. */
-       message = e_webdav_discover_new_propfind (
-               session, soup_uri, DEPTH_1,
-               NS_CARDDAV, XC ("addressbook-home-set"),
-               NULL);
-
-       e_soup_ssl_trust_connect (message, source);
-
-       /* This takes ownership of the message. */
-       soup_session_send_message (session, message);
-
-       soup_uri_free (soup_uri);
-
-       g_free (addressbook_home_set);
-
-       success = e_webdav_discover_process_addressbook_home_set (session, message, source,
-               out_certificate_pem, out_certificate_errors, out_discovered_sources,
-               cancellable, error);
+       EWebDAVDiscoverContext *context = ptr;
 
-       g_object_unref (message);
+       if (!context)
+               return;
 
-       return success;
+       g_clear_object (&context->source);
+       g_free (context->url_use_path);
+       e_named_parameters_free (context->credentials);
+       g_free (context->out_certificate_pem);
+       e_webdav_discover_free_discovered_sources (context->out_discovered_sources);
+       g_slist_free_full (context->out_calendar_user_addresses, g_free);
+       g_free (context);
 }
 
 static void
@@ -1779,13 +526,6 @@ e_webdav_discover_sources_finish (ESource *source,
        return g_task_propagate_boolean (G_TASK (result), error);
 }
 
-static void
-e_webdav_discover_cancelled_cb (GCancellable *cancellable,
-                               SoupSession *session)
-{
-       soup_session_abort (session);
-}
-
 /**
  * e_webdav_discover_sources_sync:
  * @source: an #ESource from which to take connection details
@@ -1843,11 +583,8 @@ e_webdav_discover_sources_sync (ESource *source,
                                GError **error)
 {
        ESourceWebdav *webdav_extension;
-       AuthenticateData auth_data;
-       SoupSession *session;
-       SoupMessage *message;
+       EWebDAVSession *webdav;
        SoupURI *soup_uri;
-       gulong cancelled_handler_id = 0, authenticate_handler_id;
        gboolean success;
 
        g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
@@ -1889,166 +626,76 @@ e_webdav_discover_sources_sync (ESource *source,
                g_string_free (new_path, TRUE);
        }
 
-       session = soup_session_new ();
-       g_object_set (
-               session,
-               SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
-               NULL);
-
-       message = e_webdav_discover_new_propfind (
-               session, soup_uri, DEPTH_0,
-               NS_WEBDAV, XC ("resourcetype"),
-               NS_WEBDAV, XC ("current-user-principal"),
-               NS_WEBDAV, XC ("principal-URL"),
-               NS_CALDAV, XC ("calendar-home-set"),
-               NS_CALDAV, XC ("calendar-user-address-set"),
-               NS_CARDDAV, XC ("addressbook-home-set"),
-               NS_CARDDAV, XC ("principal-address"),
-               NULL);
-
-       if (!message) {
-               soup_uri_free (soup_uri);
-               g_object_unref (session);
-               return FALSE;
-       }
+       webdav = e_webdav_session_new (source);
+       e_soup_session_setup_logging (E_SOUP_SESSION (webdav), g_getenv ("WEBDAV_DEBUG"));
+       e_soup_session_set_credentials (E_SOUP_SESSION (webdav), credentials);
 
-       if (g_getenv ("WEBDAV_DEBUG") != NULL) {
-               SoupLogger *logger;
+       if (!g_cancellable_set_error_if_cancelled (cancellable, error)) {
+               WebDAVDiscoverData wdd;
+               gchar *uri;
+               GError *local_error = NULL;
 
-               logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, 100 * 1024 * 1024);
-               soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
-               g_object_unref (logger);
-       }
+               wdd.covered_hrefs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+               wdd.addressbooks = NULL;
+               wdd.calendars = NULL;
+               wdd.only_supports = only_supports;
+               wdd.out_calendar_user_addresses = out_calendar_user_addresses;
+               wdd.cancellable = cancellable;
+               wdd.error = &local_error;
 
-       if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
-               SoupSessionFeature *feature;
-               ESourceAuthentication *auth_extension;
-               gchar *auth_method;
+               uri = soup_uri_to_string (soup_uri, FALSE);
 
-               feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
+               success = uri && *uri && e_webdav_discover_propfind_uri_sync (webdav, &wdd, uri, FALSE);
 
-               success = TRUE;
+               g_free (uri);
 
-               auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
-               auth_method = e_source_authentication_dup_method (auth_extension);
+               if (success && !wdd.calendars && (only_supports == E_WEBDAV_DISCOVER_SUPPORTS_NONE ||
+                  (only_supports & (E_WEBDAV_DISCOVER_SUPPORTS_EVENTS | E_WEBDAV_DISCOVER_SUPPORTS_MEMOS | 
E_WEBDAV_DISCOVER_SUPPORTS_TASKS)) != 0) &&
+                  (!soup_uri_get_path (soup_uri) || !strstr (soup_uri_get_path (soup_uri), 
"/.well-known/"))) {
+                       gchar *saved_path;
 
-               if (g_strcmp0 (auth_method, "OAuth2") == 0 || g_strcmp0 (auth_method, "Google") == 0) {
-                       SoupAuth *soup_auth;
+                       saved_path = g_strdup (soup_uri_get_path (soup_uri));
 
-                       soup_auth = g_object_new (E_TYPE_SOUP_AUTH_BEARER, SOUP_AUTH_HOST, soup_uri->host, 
NULL);
+                       soup_uri_set_path (soup_uri, "/.well-known/caldav");
 
-                       success = e_webdav_discover_setup_bearer_auth (source, credentials,
-                               E_SOUP_AUTH_BEARER (soup_auth), cancellable, error);
+                       uri = soup_uri_to_string (soup_uri, FALSE);
 
-                       if (success) {
-                               soup_session_feature_add_feature (feature, E_TYPE_SOUP_AUTH_BEARER);
-                               soup_auth_manager_use_auth (
-                                       SOUP_AUTH_MANAGER (feature),
-                                       soup_uri, soup_auth);
-                       }
+                       /* Ignore errors here */
+                       wdd.error = NULL;
+                       wdd.only_supports = E_WEBDAV_DISCOVER_SUPPORTS_EVENTS | 
E_WEBDAV_DISCOVER_SUPPORTS_MEMOS | E_WEBDAV_DISCOVER_SUPPORTS_TASKS;
 
-                       g_object_unref (soup_auth);
-               }
+                       success = uri && *uri && e_webdav_discover_propfind_uri_sync (webdav, &wdd, uri, 
FALSE);
 
-               g_free (auth_method);
+                       g_free (uri);
 
-               if (!success) {
-                       soup_uri_free (soup_uri);
-                       g_object_unref (message);
-                       g_object_unref (session);
-                       return FALSE;
+                       soup_uri_set_path (soup_uri, saved_path);
+                       g_free (saved_path);
                }
-       }
-
-       auth_data.source = source;
-       auth_data.credentials = credentials;
-
-       authenticate_handler_id = g_signal_connect (session, "authenticate",
-               G_CALLBACK (e_webdav_discover_authenticate_cb), &auth_data);
-
-       if (cancellable)
-               cancelled_handler_id = g_cancellable_connect (cancellable, G_CALLBACK 
(e_webdav_discover_cancelled_cb), session, NULL);
 
-       if (!g_cancellable_set_error_if_cancelled (cancellable, error)) {
-               GSList *calendars = NULL, *addressbooks = NULL;
-               GError *local_error = NULL;
-
-               e_soup_ssl_trust_connect (message, source);
-               soup_session_send_message (session, message);
-
-               success = TRUE;
-
-               if (only_supports == E_WEBDAV_DISCOVER_SUPPORTS_NONE ||
-                  (only_supports & (E_WEBDAV_DISCOVER_SUPPORTS_EVENTS | E_WEBDAV_DISCOVER_SUPPORTS_MEMOS | 
E_WEBDAV_DISCOVER_SUPPORTS_TASKS)) != 0) {
-                       success = e_webdav_discover_process_calendar_home_set (session, message, source, 
out_certificate_pem,
-                               out_certificate_errors, &calendars, out_calendar_user_addresses, cancellable, 
&local_error);
-
-                       if (!calendars && !g_cancellable_is_cancelled (cancellable) && (!soup_uri_get_path 
(soup_uri) ||
-                           !strstr (soup_uri_get_path (soup_uri), "/.well-known/"))) {
-                               SoupMessage *well_known_message;
-                               gchar *saved_path;
-
-                               saved_path = g_strdup (soup_uri_get_path (soup_uri));
+               if (success && !wdd.addressbooks && (only_supports == E_WEBDAV_DISCOVER_SUPPORTS_NONE ||
+                   (only_supports & (E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS)) != 0) &&
+                   (!soup_uri_get_path (soup_uri) || !strstr (soup_uri_get_path (soup_uri), 
"/.well-known/"))) {
+                       gchar *saved_path;
 
-                               soup_uri_set_path (soup_uri, "/.well-known/caldav");
+                       saved_path = g_strdup (soup_uri_get_path (soup_uri));
 
-                               well_known_message = e_webdav_discover_new_propfind (
-                                       session, soup_uri, DEPTH_0,
-                                       NS_WEBDAV, XC ("resourcetype"),
-                                       NS_WEBDAV, XC ("current-user-principal"),
-                                       NS_WEBDAV, XC ("principal-URL"),
-                                       NS_CALDAV, XC ("calendar-home-set"),
-                                       NS_CALDAV, XC ("calendar-user-address-set"),
-                                       NULL);
+                       soup_uri_set_path (soup_uri, "/.well-known/carddav");
 
-                               soup_uri_set_path (soup_uri, saved_path);
-                               g_free (saved_path);
+                       uri = soup_uri_to_string (soup_uri, FALSE);
 
-                               if (well_known_message) {
-                                       e_soup_ssl_trust_connect (well_known_message, source);
-                                       soup_session_send_message (session, well_known_message);
+                       /* Ignore errors here */
+                       wdd.error = NULL;
+                       wdd.only_supports = E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS;
 
-                                       /* Ignore errors here */
-                                       e_webdav_discover_process_calendar_home_set (session, 
well_known_message, source, out_certificate_pem,
-                                               out_certificate_errors, &calendars, 
out_calendar_user_addresses, cancellable, NULL);
+                       success = uri && *uri && e_webdav_discover_propfind_uri_sync (webdav, &wdd, uri, 
FALSE);
 
-                                       g_clear_object (&well_known_message);
-                               }
-                       }
-               }
+                       g_free (uri);
 
-               if (success && (only_supports == E_WEBDAV_DISCOVER_SUPPORTS_NONE ||
-                   (only_supports & (E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS)) != 0)) {
-                       success = e_webdav_discover_process_addressbook_home_set (session, message, source, 
out_certificate_pem,
-                               out_certificate_errors, &addressbooks, cancellable, local_error ? NULL : 
&local_error);
-
-                       if (!addressbooks && !g_cancellable_is_cancelled (cancellable) && (!soup_uri_get_path 
(soup_uri) ||
-                           !strstr (soup_uri_get_path (soup_uri), "/.well-known/"))) {
-                               g_clear_object (&message);
-
-                               soup_uri_set_path (soup_uri, "/.well-known/carddav");
-
-                               message = e_webdav_discover_new_propfind (
-                                       session, soup_uri, DEPTH_0,
-                                       NS_WEBDAV, XC ("resourcetype"),
-                                       NS_WEBDAV, XC ("current-user-principal"),
-                                       NS_WEBDAV, XC ("principal-URL"),
-                                       NS_CARDDAV, XC ("addressbook-home-set"),
-                                       NS_CARDDAV, XC ("principal-address"),
-                                       NULL);
-
-                               if (message) {
-                                       e_soup_ssl_trust_connect (message, source);
-                                       soup_session_send_message (session, message);
-
-                                       /* Ignore errors here */
-                                       e_webdav_discover_process_addressbook_home_set (session, message, 
source, out_certificate_pem,
-                                               out_certificate_errors, &addressbooks, cancellable, NULL);
-                               }
-                       }
+                       soup_uri_set_path (soup_uri, saved_path);
+                       g_free (saved_path);
                }
 
-               if (calendars || addressbooks) {
+               if (wdd.calendars || wdd.addressbooks) {
                        success = TRUE;
                        g_clear_error (&local_error);
                } else if (local_error) {
@@ -2056,13 +703,13 @@ e_webdav_discover_sources_sync (ESource *source,
                }
 
                if (out_discovered_sources) {
-                       if (calendars)
-                               *out_discovered_sources = g_slist_concat (*out_discovered_sources, calendars);
-                       if (addressbooks)
-                               *out_discovered_sources = g_slist_concat (*out_discovered_sources, 
addressbooks);
+                       if (wdd.calendars)
+                               *out_discovered_sources = g_slist_concat (*out_discovered_sources, 
wdd.calendars);
+                       if (wdd.addressbooks)
+                               *out_discovered_sources = g_slist_concat (*out_discovered_sources, 
wdd.addressbooks);
                } else {
-                       e_webdav_discover_free_discovered_sources (calendars);
-                       e_webdav_discover_free_discovered_sources (addressbooks);
+                       e_webdav_discover_free_discovered_sources (wdd.calendars);
+                       e_webdav_discover_free_discovered_sources (wdd.addressbooks);
                }
 
                if (out_calendar_user_addresses && *out_calendar_user_addresses)
@@ -2070,19 +717,17 @@ e_webdav_discover_sources_sync (ESource *source,
 
                if (out_discovered_sources && *out_discovered_sources)
                        *out_discovered_sources = g_slist_reverse (*out_discovered_sources);
+
+               g_hash_table_destroy (wdd.covered_hrefs);
        } else {
                success = FALSE;
        }
 
-       if (cancellable && cancelled_handler_id)
-               g_cancellable_disconnect (cancellable, cancelled_handler_id);
-
-       if (authenticate_handler_id)
-               g_signal_handler_disconnect (session, authenticate_handler_id);
+       if (!success)
+               e_soup_session_get_ssl_error_details (E_SOUP_SESSION (webdav), out_certificate_pem, 
out_certificate_errors);
 
        soup_uri_free (soup_uri);
-       g_clear_object (&message);
-       g_object_unref (session);
+       g_object_unref (webdav);
 
        return success;
 }
diff --git a/src/libedataserver/e-webdav-session.c b/src/libedataserver/e-webdav-session.c
index 29f2f8a..cfb0652 100644
--- a/src/libedataserver/e-webdav-session.c
+++ b/src/libedataserver/e-webdav-session.c
@@ -966,7 +966,7 @@ e_webdav_session_replace_with_detailed_error (EWebDAVSession *webdav,
                                e_soup_session_util_status_to_string (status_code, reason_phrase),
                                detail_text);
                }
-       } else if (!SOUP_STATUS_IS_SUCCESSFUL (status_code)) {
+       } else if (status_code && !SOUP_STATUS_IS_SUCCESSFUL (status_code)) {
                error_set = TRUE;
 
                g_clear_error (inout_error);


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