[evolution-data-server] I#223 - WebDAV: Inefficient processing of returned XML data



commit ecb1470c68341b18bbd7146039e8345d308ffc0b
Author: Milan Crha <mcrha redhat com>
Date:   Mon Jun 22 22:15:01 2020 +0200

    I#223 - WebDAV: Inefficient processing of returned XML data
    
    Closes https://gitlab.gnome.org/GNOME/evolution-data-server/-/issues/223

 CMakeLists.txt                                     |   2 +-
 .../backends/carddav/e-book-backend-carddav.c      |  73 +-
 .../backends/caldav/e-cal-backend-caldav.c         | 228 +++---
 .../webdav-notes/e-cal-backend-webdav-notes.c      |  17 +-
 src/libedataserver/e-webdav-discover.c             | 291 ++++---
 src/libedataserver/e-webdav-session.c              | 897 +++++++++------------
 src/libedataserver/e-webdav-session.h              |  12 +-
 src/libedataserver/e-xml-utils.c                   | 366 +++++++++
 src/libedataserver/e-xml-utils.h                   |  28 +
 9 files changed, 1083 insertions(+), 831 deletions(-)
---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a3ba458f2..73bda6d85 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -56,7 +56,7 @@ set(LIBEBACKEND_CURRENT 10)
 set(LIBEBACKEND_REVISION 0)
 set(LIBEBACKEND_AGE 0)
 
-set(LIBEDATASERVER_CURRENT 24)
+set(LIBEDATASERVER_CURRENT 25)
 set(LIBEDATASERVER_REVISION 0)
 set(LIBEDATASERVER_AGE 0)
 
diff --git a/src/addressbook/backends/carddav/e-book-backend-carddav.c 
b/src/addressbook/backends/carddav/e-book-backend-carddav.c
index d3a33097f..d60507d4a 100644
--- a/src/addressbook/backends/carddav/e-book-backend-carddav.c
+++ b/src/addressbook/backends/carddav/e-book-backend-carddav.c
@@ -389,8 +389,7 @@ ebb_carddav_update_nfo_with_contact (EBookMetaBackendInfo *nfo,
 
 static gboolean
 ebb_carddav_multiget_response_cb (EWebDAVSession *webdav,
-                                 xmlXPathContextPtr xpath_ctx,
-                                 const gchar *xpath_prop_prefix,
+                                 xmlNodePtr prop_node,
                                  const SoupURI *request_uri,
                                  const gchar *href,
                                  guint status_code,
@@ -400,27 +399,33 @@ ebb_carddav_multiget_response_cb (EWebDAVSession *webdav,
 
        g_return_val_if_fail (from_link != NULL, FALSE);
 
-       if (!xpath_prop_prefix) {
-               e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CARDDAV, NULL);
-       } else if (status_code == SOUP_STATUS_OK) {
-               gchar *address_data, *etag;
+       if (status_code == SOUP_STATUS_OK) {
+               const xmlChar *address_data, *etag;
+               xmlNodePtr address_data_node = NULL, etag_node = NULL;
 
                g_return_val_if_fail (href != NULL, FALSE);
 
-               address_data = e_xml_xpath_eval_as_string (xpath_ctx, "%s/C:address-data", xpath_prop_prefix);
-               etag = e_webdav_session_util_maybe_dequote (e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/D:getetag", xpath_prop_prefix));
+               e_xml_find_children_nodes (prop_node, 2,
+                       E_WEBDAV_NS_CARDDAV, "address-data", &address_data_node,
+                       E_WEBDAV_NS_DAV, "getetag", &etag_node);
+
+               address_data = e_xml_get_node_text (address_data_node);
+               etag = e_xml_get_node_text (etag_node);
 
                if (address_data) {
                        EContact *contact;
 
-                       contact = e_contact_new_from_vcard (address_data);
+                       contact = e_contact_new_from_vcard ((const gchar *) address_data);
                        if (contact) {
                                const gchar *uid;
 
                                uid = e_contact_get_const (contact, E_CONTACT_UID);
                                if (uid) {
+                                       gchar *dequoted_etag;
                                        GSList *link;
 
+                                       dequoted_etag = e_webdav_session_util_maybe_dequote (g_strdup ((const 
gchar *) etag));
+
                                        for (link = *from_link; link; link = g_slist_next (link)) {
                                                EBookMetaBackendInfo *nfo = link->data;
 
@@ -433,19 +438,18 @@ ebb_carddav_multiget_response_cb (EWebDAVSession *webdav,
                                                        if (link == *from_link)
                                                                *from_link = g_slist_next (*from_link);
 
-                                                       ebb_carddav_update_nfo_with_contact (nfo, contact, 
etag);
+                                                       ebb_carddav_update_nfo_with_contact (nfo, contact, 
dequoted_etag);
 
                                                        break;
                                                }
                                        }
+
+                                       g_free (dequoted_etag);
                                }
 
                                g_object_unref (contact);
                        }
                }
-
-               g_free (address_data);
-               g_free (etag);
        } else if (status_code == SOUP_STATUS_NOT_FOUND) {
                GSList *link;
 
@@ -548,8 +552,7 @@ ebb_carddav_multiget_from_sets_sync (EBookBackendCardDAV *bbdav,
 
 static gboolean
 ebb_carddav_get_contact_items_cb (EWebDAVSession *webdav,
-                                 xmlXPathContextPtr xpath_ctx,
-                                 const gchar *xpath_prop_prefix,
+                                 xmlNodePtr prop_node,
                                  const SoupURI *request_uri,
                                  const gchar *href,
                                  guint status_code,
@@ -557,11 +560,10 @@ ebb_carddav_get_contact_items_cb (EWebDAVSession *webdav,
 {
        GHashTable *known_items = user_data; /* gchar *href ~> EBookMetaBackendInfo * */
 
-       g_return_val_if_fail (xpath_ctx != NULL, FALSE);
+       g_return_val_if_fail (prop_node != NULL, FALSE);
        g_return_val_if_fail (known_items != NULL, FALSE);
 
-       if (xpath_prop_prefix &&
-           status_code == SOUP_STATUS_OK) {
+       if (status_code == SOUP_STATUS_OK) {
                EBookMetaBackendInfo *nfo;
                gchar *etag;
 
@@ -573,7 +575,7 @@ ebb_carddav_get_contact_items_cb (EWebDAVSession *webdav,
                        return TRUE;
                }
 
-               etag = e_webdav_session_util_maybe_dequote (e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/D:getetag", xpath_prop_prefix));
+               etag = e_webdav_session_util_maybe_dequote (g_strdup ((const gchar *) 
e_xml_find_child_and_get_text (prop_node, E_WEBDAV_NS_DAV, "getetag")));
                /* Return 'TRUE' to not stop on faulty data from the server */
                g_return_val_if_fail (etag != NULL, TRUE);
 
@@ -802,8 +804,7 @@ ebb_carddav_get_changes_sync (EBookMetaBackend *meta_backend,
 
 static gboolean
 ebb_carddav_extract_existing_cb (EWebDAVSession *webdav,
-                                xmlXPathContextPtr xpath_ctx,
-                                const gchar *xpath_prop_prefix,
+                                xmlNodePtr prop_node,
                                 const SoupURI *request_uri,
                                 const gchar *href,
                                 guint status_code,
@@ -813,38 +814,42 @@ ebb_carddav_extract_existing_cb (EWebDAVSession *webdav,
 
        g_return_val_if_fail (out_existing_objects != NULL, FALSE);
 
-       if (!xpath_prop_prefix) {
-               e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CARDDAV, NULL);
-       } else if (status_code == SOUP_STATUS_OK) {
-               gchar *etag;
-               gchar *address_data;
+       if (status_code == SOUP_STATUS_OK) {
+               const xmlChar *address_data, *etag;
+               xmlNodePtr address_data_node = NULL, etag_node = NULL;
 
                g_return_val_if_fail (href != NULL, FALSE);
 
-               etag = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:getetag", xpath_prop_prefix);
-               address_data = e_xml_xpath_eval_as_string (xpath_ctx, "%s/C:address-data", xpath_prop_prefix);
+               e_xml_find_children_nodes (prop_node, 2,
+                       E_WEBDAV_NS_CARDDAV, "address-data", &address_data_node,
+                       E_WEBDAV_NS_DAV, "getetag", &etag_node);
+
+               address_data = e_xml_get_node_text (address_data_node);
+               etag = e_xml_get_node_text (etag_node);
 
                if (address_data) {
                        EContact *contact;
 
-                       contact = e_contact_new_from_vcard (address_data);
+                       contact = e_contact_new_from_vcard ((const gchar *) address_data);
                        if (contact) {
                                const gchar *uid;
 
                                uid = e_contact_get_const (contact, E_CONTACT_UID);
 
                                if (uid) {
-                                       etag = e_webdav_session_util_maybe_dequote (etag);
+                                       gchar *dequoted_etag;
+
+                                       dequoted_etag = e_webdav_session_util_maybe_dequote (g_strdup ((const 
gchar *) etag));
+
                                        *out_existing_objects = g_slist_prepend (*out_existing_objects,
-                                               e_book_meta_backend_info_new (uid, etag, NULL, href));
+                                               e_book_meta_backend_info_new (uid, dequoted_etag, NULL, 
href));
+
+                                       g_free (dequoted_etag);
                                }
 
                                g_object_unref (contact);
                        }
                }
-
-               g_free (address_data);
-               g_free (etag);
        }
 
        return TRUE;
diff --git a/src/calendar/backends/caldav/e-cal-backend-caldav.c 
b/src/calendar/backends/caldav/e-cal-backend-caldav.c
index f316426d6..11af2d87d 100644
--- a/src/calendar/backends/caldav/e-cal-backend-caldav.c
+++ b/src/calendar/backends/caldav/e-cal-backend-caldav.c
@@ -416,8 +416,7 @@ typedef struct _MultigetData {
 
 static gboolean
 ecb_caldav_multiget_response_cb (EWebDAVSession *webdav,
-                                xmlXPathContextPtr xpath_ctx,
-                                const gchar *xpath_prop_prefix,
+                                xmlNodePtr prop_node,
                                 const SoupURI *request_uri,
                                 const gchar *href,
                                 guint status_code,
@@ -428,27 +427,33 @@ ecb_caldav_multiget_response_cb (EWebDAVSession *webdav,
        g_return_val_if_fail (md != NULL, FALSE);
        g_return_val_if_fail (md->from_link != NULL, FALSE);
 
-       if (!xpath_prop_prefix) {
-               e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CALDAV, NULL);
-       } else if (status_code == SOUP_STATUS_OK) {
-               gchar *calendar_data, *etag;
+       if (status_code == SOUP_STATUS_OK) {
+               const xmlChar *calendar_data, *etag;
+               xmlNodePtr calendar_data_node = NULL, etag_node = NULL;
 
                g_return_val_if_fail (href != NULL, FALSE);
 
-               calendar_data = e_xml_xpath_eval_as_string (xpath_ctx, "%s/C:calendar-data", 
xpath_prop_prefix);
-               etag = e_webdav_session_util_maybe_dequote (e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/D:getetag", xpath_prop_prefix));
+               e_xml_find_children_nodes (prop_node, 2,
+                       E_WEBDAV_NS_CALDAV, "calendar-data", &calendar_data_node,
+                       E_WEBDAV_NS_DAV, "getetag", &etag_node);
+
+               calendar_data = e_xml_get_node_text (calendar_data_node);
+               etag = e_xml_get_node_text (etag_node);
 
                if (calendar_data) {
                        ICalComponent *vcalendar;
 
-                       vcalendar = i_cal_component_new_from_string (calendar_data);
+                       vcalendar = i_cal_component_new_from_string ((const gchar *) calendar_data);
                        if (vcalendar) {
                                const gchar *uid;
 
                                uid = ecb_caldav_get_vcalendar_uid (vcalendar);
                                if (uid) {
+                                       gchar *dequoted_etag;
                                        GSList *link;
 
+                                       dequoted_etag = e_webdav_session_util_maybe_dequote (g_strdup ((const 
gchar *) etag));
+
                                        for (link = md->from_link; link; link = g_slist_next (link)) {
                                                ECalMetaBackendInfo *nfo = link->data;
 
@@ -461,19 +466,18 @@ ecb_caldav_multiget_response_cb (EWebDAVSession *webdav,
                                                        if (link == md->from_link)
                                                                md->from_link = g_slist_next (md->from_link);
 
-                                                       ecb_caldav_update_nfo_with_vcalendar (nfo, vcalendar, 
etag);
+                                                       ecb_caldav_update_nfo_with_vcalendar (nfo, vcalendar, 
dequoted_etag);
 
                                                        break;
                                                }
                                        }
+
+                                       g_free (dequoted_etag);
                                }
 
                                g_object_unref (vcalendar);
                        }
                }
-
-               g_free (calendar_data);
-               g_free (etag);
        } else if (status_code == SOUP_STATUS_NOT_FOUND) {
                GSList *link;
 
@@ -688,43 +692,40 @@ ecb_caldav_multiget_from_sets_sync (ECalBackendCalDAV *cbdav,
 
 static gboolean
 ecb_caldav_get_calendar_items_cb (EWebDAVSession *webdav,
-                                 xmlXPathContextPtr xpath_ctx,
-                                 const gchar *xpath_prop_prefix,
+                                 xmlNodePtr prop_node,
                                  const SoupURI *request_uri,
                                  const gchar *href,
                                  guint status_code,
                                  gpointer user_data)
 {
        GHashTable *known_items = user_data; /* gchar *href ~> ECalMetaBackendInfo * */
+       ECalMetaBackendInfo *nfo;
+       gchar *etag;
 
-       g_return_val_if_fail (xpath_ctx != NULL, FALSE);
+       g_return_val_if_fail (prop_node != NULL, FALSE);
        g_return_val_if_fail (known_items != NULL, FALSE);
 
-       if (!xpath_prop_prefix) {
-               e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CALDAV, NULL);
-       } else if (status_code == SOUP_STATUS_OK) {
-               ECalMetaBackendInfo *nfo;
-               gchar *etag;
+       if (status_code != SOUP_STATUS_OK)
+               return TRUE;
 
-               g_return_val_if_fail (href != NULL, FALSE);
+       g_return_val_if_fail (href != NULL, FALSE);
 
-               /* Skip collection resource, if returned by the server (like iCloud.com does) */
-               if (g_str_has_suffix (href, "/") ||
-                   (request_uri && request_uri->path && g_str_has_suffix (href, request_uri->path)))
-                       return TRUE;
+       /* Skip collection resource, if returned by the server (like iCloud.com does) */
+       if (g_str_has_suffix (href, "/") ||
+           (request_uri && request_uri->path && g_str_has_suffix (href, request_uri->path)))
+               return TRUE;
 
-               etag = e_webdav_session_util_maybe_dequote (e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/D:getetag", xpath_prop_prefix));
-               /* Return 'TRUE' to not stop on faulty data from the server */
-               g_return_val_if_fail (etag != NULL, TRUE);
+       etag = e_webdav_session_util_maybe_dequote (g_strdup ((const gchar *) e_xml_find_child_and_get_text 
(prop_node, E_WEBDAV_NS_DAV, "getetag")));
+       /* Return 'TRUE' to not stop on faulty data from the server */
+       g_return_val_if_fail (etag != NULL, TRUE);
 
-               /* UID is unknown at this moment */
-               nfo = e_cal_meta_backend_info_new ("", etag, NULL, href);
+       /* UID is unknown at this moment */
+       nfo = e_cal_meta_backend_info_new ("", etag, NULL, href);
 
-               g_free (etag);
-               g_return_val_if_fail (nfo != NULL, FALSE);
+       g_free (etag);
+       g_return_val_if_fail (nfo != NULL, FALSE);
 
-               g_hash_table_insert (known_items, g_strdup (href), nfo);
-       }
+       g_hash_table_insert (known_items, g_strdup (href), nfo);
 
        return TRUE;
 }
@@ -1030,8 +1031,7 @@ ecb_caldav_get_changes_sync (ECalMetaBackend *meta_backend,
 
 static gboolean
 ecb_caldav_extract_existing_cb (EWebDAVSession *webdav,
-                               xmlXPathContextPtr xpath_ctx,
-                               const gchar *xpath_prop_prefix,
+                               xmlNodePtr prop_node,
                                const SoupURI *request_uri,
                                const gchar *href,
                                guint status_code,
@@ -1041,38 +1041,42 @@ ecb_caldav_extract_existing_cb (EWebDAVSession *webdav,
 
        g_return_val_if_fail (out_existing_objects != NULL, FALSE);
 
-       if (!xpath_prop_prefix) {
-               e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CALDAV, NULL);
-       } else if (status_code == SOUP_STATUS_OK) {
-               gchar *etag;
-               gchar *calendar_data;
+       if (status_code == SOUP_STATUS_OK) {
+               const xmlChar *calendar_data, *etag;
+               xmlNodePtr calendar_data_node = NULL, etag_node = NULL;
 
                g_return_val_if_fail (href != NULL, FALSE);
 
-               etag = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:getetag", xpath_prop_prefix);
-               calendar_data = e_xml_xpath_eval_as_string (xpath_ctx, "%s/C:calendar-data", 
xpath_prop_prefix);
+               e_xml_find_children_nodes (prop_node, 2,
+                       E_WEBDAV_NS_CALDAV, "calendar-data", &calendar_data_node,
+                       E_WEBDAV_NS_DAV, "getetag", &etag_node);
+
+               calendar_data = e_xml_get_node_text (calendar_data_node);
+               etag = e_xml_get_node_text (etag_node);
 
                if (calendar_data) {
                        ICalComponent *vcalendar;
 
-                       vcalendar = i_cal_component_new_from_string (calendar_data);
+                       vcalendar = i_cal_component_new_from_string ((const gchar *) calendar_data);
                        if (vcalendar) {
                                const gchar *uid;
 
                                uid = ecb_caldav_get_vcalendar_uid (vcalendar);
 
                                if (uid) {
-                                       etag = e_webdav_session_util_maybe_dequote (etag);
+                                       gchar *dequoted_etag;
+
+                                       dequoted_etag = e_webdav_session_util_maybe_dequote (g_strdup ((const 
gchar *) etag));
+
                                        *out_existing_objects = g_slist_prepend (*out_existing_objects,
-                                               e_cal_meta_backend_info_new (uid, etag, NULL, href));
+                                               e_cal_meta_backend_info_new (uid, dequoted_etag, NULL, href));
+
+                                       g_free (dequoted_etag);
                                }
 
                                g_object_unref (vcalendar);
                        }
                }
-
-               g_free (calendar_data);
-               g_free (etag);
        }
 
        return TRUE;
@@ -1643,10 +1647,41 @@ ecb_caldav_get_ssl_error_details (ECalMetaBackend *meta_backend,
        return res;
 }
 
+static gboolean
+ecb_caldav_dup_href_node_value (EWebDAVSession *webdav,
+                               const SoupURI *request_uri,
+                               xmlNodePtr from_node,
+                               const gchar *parent_ns_href,
+                               const gchar *parent_name,
+                               gchar **out_href)
+{
+       xmlNodePtr node;
+
+       g_return_val_if_fail (out_href != NULL, FALSE);
+
+       if (!from_node)
+               return FALSE;
+
+       node = e_xml_find_in_hierarchy (from_node, parent_ns_href, parent_name, E_WEBDAV_NS_DAV, "href", 
NULL, NULL);
+
+       if (node) {
+               const xmlChar *href;
+
+               href = e_xml_get_node_text (node);
+
+               if (href && *href) {
+                       *out_href = e_webdav_session_ensure_full_uri (webdav, request_uri, (const gchar *) 
href);
+
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
 static gboolean
 ecb_caldav_propfind_get_owner_cb (EWebDAVSession *webdav,
-                                 xmlXPathContextPtr xpath_ctx,
-                                 const gchar *xpath_prop_prefix,
+                                 xmlNodePtr prop_node,
                                  const SoupURI *request_uri,
                                  const gchar *href,
                                  guint status_code,
@@ -1654,17 +1689,11 @@ ecb_caldav_propfind_get_owner_cb (EWebDAVSession *webdav,
 {
        gchar **out_owner_href = user_data;
 
+       g_return_val_if_fail (prop_node != NULL, FALSE);
        g_return_val_if_fail (out_owner_href != NULL, FALSE);
 
-       if (xpath_prop_prefix &&
-           status_code == SOUP_STATUS_OK) {
-               gchar *tmp = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:owner/D:href", xpath_prop_prefix);
-
-               if (tmp && *tmp)
-                       *out_owner_href = e_webdav_session_ensure_full_uri (webdav, request_uri, tmp);
-
-               g_free (tmp);
-
+       if (status_code == SOUP_STATUS_OK &&
+           ecb_caldav_dup_href_node_value (webdav, request_uri, prop_node, E_WEBDAV_NS_DAV, "owner", 
out_owner_href)) {
                return FALSE;
        }
 
@@ -1673,8 +1702,7 @@ ecb_caldav_propfind_get_owner_cb (EWebDAVSession *webdav,
 
 static gboolean
 ecb_caldav_propfind_get_schedule_outbox_url_cb (EWebDAVSession *webdav,
-                                               xmlXPathContextPtr xpath_ctx,
-                                               const gchar *xpath_prop_prefix,
+                                               xmlNodePtr prop_node,
                                                const SoupURI *request_uri,
                                                const gchar *href,
                                                guint status_code,
@@ -1684,16 +1712,8 @@ ecb_caldav_propfind_get_schedule_outbox_url_cb (EWebDAVSession *webdav,
 
        g_return_val_if_fail (out_schedule_outbox_url != NULL, FALSE);
 
-       if (!xpath_prop_prefix) {
-               e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CALDAV, NULL);
-       } else if (status_code == SOUP_STATUS_OK) {
-               gchar *tmp = e_xml_xpath_eval_as_string (xpath_ctx, "%s/C:schedule-outbox-URL/D:href", 
xpath_prop_prefix);
-
-               if (tmp && *tmp)
-                       *out_schedule_outbox_url = e_webdav_session_ensure_full_uri (webdav, request_uri, 
tmp);
-
-               g_free (tmp);
-
+       if (status_code == SOUP_STATUS_OK &&
+           ecb_caldav_dup_href_node_value (webdav, request_uri, prop_node, E_WEBDAV_NS_CALDAV, 
"schedule-outbox-URL", out_schedule_outbox_url)) {
                return FALSE;
        }
 
@@ -1981,7 +2001,7 @@ ecb_caldav_get_free_busy_from_schedule_outbox_sync (ECalBackendCalDAV *cbdav,
            response) {
                /* parse returned xml */
                xmlDocPtr doc;
-               xmlXPathContextPtr xpath_ctx = NULL;
+               xmlNodePtr schedule_response = NULL;
 
                doc = e_xml_parse_data (response->data, response->len);
 
@@ -1989,56 +2009,44 @@ ecb_caldav_get_free_busy_from_schedule_outbox_sync (ECalBackendCalDAV *cbdav,
                        g_set_error_literal (&local_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
                                _("Failed to parse response data"));
                } else {
-                       xpath_ctx = e_xml_new_xpath_context_with_namespaces (doc,
-                               "D", E_WEBDAV_NS_DAV,
-                               "C", E_WEBDAV_NS_CALDAV,
-                               NULL);
+                       schedule_response = e_xml_find_sibling (xmlDocGetRootElement (doc), 
E_WEBDAV_NS_CALDAV, "schedule-response");
                }
 
-               if (xpath_ctx) {
-                       xmlXPathObjectPtr xpath_obj_response;
+               if (schedule_response) {
+                       xmlNodePtr response_node;
 
-                       xpath_obj_response = e_xml_xpath_eval (xpath_ctx, "/C:schedule-response/C:response");
+                       for (response_node = e_xml_find_child (schedule_response, E_WEBDAV_NS_CALDAV, 
"response");
+                            response_node && !g_cancellable_is_cancelled (cancellable);
+                            response_node = e_xml_find_next_sibling (response_node, E_WEBDAV_NS_CALDAV, 
"response")) {
+                               const xmlChar *calendar_data;
 
-                       if (xpath_obj_response) {
-                               gint response_index, response_length;
+                               calendar_data = e_xml_find_child_and_get_text (response_node, 
E_WEBDAV_NS_CALDAV, "calendar-data");
 
-                               response_length = xmlXPathNodeSetGetLength (xpath_obj_response->nodesetval);
+                               if (calendar_data && *calendar_data) {
+                                       GSList *objects = NULL;
 
-                               for (response_index = 0; response_index < response_length; response_index++) {
-                                       gchar *tmp;
+                                       icomp = i_cal_parser_parse_string ((const gchar *) calendar_data);
 
-                                       tmp = e_xml_xpath_eval_as_string 
(xpath_ctx,"/C:schedule-response/C:response[%d]/C:calendar-data", response_index + 1);
-                                       if (tmp && *tmp) {
-                                               GSList *objects = NULL;
+                                       if (icomp)
+                                               ecb_caldav_extract_objects (icomp, I_CAL_VFREEBUSY_COMPONENT, 
&objects, &local_error);
 
-                                               icomp = i_cal_parser_parse_string (tmp);
-                                               if (icomp)
-                                                       ecb_caldav_extract_objects (icomp, 
I_CAL_VFREEBUSY_COMPONENT, &objects, &local_error);
-                                               if (icomp && !local_error) {
-                                                       for (link = objects; link; link = g_slist_next 
(link)) {
-                                                               gchar *obj_str = 
i_cal_component_as_ical_string (link->data);
+                                       if (icomp && !local_error) {
+                                               for (link = objects; link; link = g_slist_next (link)) {
+                                                       gchar *obj_str = i_cal_component_as_ical_string 
(link->data);
 
-                                                               if (obj_str && *obj_str)
-                                                                       *out_freebusy = g_slist_prepend 
(*out_freebusy, obj_str);
-                                                               else
-                                                                       g_free (obj_str);
-                                                       }
+                                                       if (obj_str && *obj_str)
+                                                               *out_freebusy = g_slist_prepend 
(*out_freebusy, obj_str);
+                                                       else
+                                                               g_free (obj_str);
                                                }
-
-                                               g_slist_free_full (objects, g_object_unref);
-
-                                               g_clear_object (&icomp);
-                                               g_clear_error (&local_error);
                                        }
 
-                                       g_free (tmp);
-                               }
+                                       g_slist_free_full (objects, g_object_unref);
 
-                               xmlXPathFreeObject (xpath_obj_response);
+                                       g_clear_object (&icomp);
+                                       g_clear_error (&local_error);
+                               }
                        }
-
-                       xmlXPathFreeContext (xpath_ctx);
                }
 
                if (doc)
diff --git a/src/calendar/backends/webdav-notes/e-cal-backend-webdav-notes.c 
b/src/calendar/backends/webdav-notes/e-cal-backend-webdav-notes.c
index dd9e05eaa..b397fadac 100644
--- a/src/calendar/backends/webdav-notes/e-cal-backend-webdav-notes.c
+++ b/src/calendar/backends/webdav-notes/e-cal-backend-webdav-notes.c
@@ -356,29 +356,22 @@ ecb_webdav_notes_check_credentials_error (ECalBackendWebDAVNotes *cbnotes,
 
 static gboolean
 ecb_webdav_notes_getetag_cb (EWebDAVSession *webdav,
-                            xmlXPathContextPtr xpath_ctx,
-                            const gchar *xpath_prop_prefix,
+                            xmlNodePtr prop_node,
                             const SoupURI *request_uri,
                             const gchar *href,
                             guint status_code,
                             gpointer user_data)
 {
-       if (!xpath_prop_prefix)
-               return TRUE;
-
        if (status_code == SOUP_STATUS_OK) {
                gchar **out_etag = user_data;
-               gchar *etag;
+               const xmlChar *etag;
 
                g_return_val_if_fail (out_etag != NULL, FALSE);
 
-               etag = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:getetag", xpath_prop_prefix);
+               etag = e_xml_find_child_and_get_text (prop_node, E_WEBDAV_NS_DAV, "getetag");
 
-               if (etag && *etag) {
-                       *out_etag = e_webdav_session_util_maybe_dequote (etag);
-               } else {
-                       g_free (etag);
-               }
+               if (etag && *etag)
+                       *out_etag = e_webdav_session_util_maybe_dequote (g_strdup ((const gchar *) etag));
        }
 
        return FALSE;
diff --git a/src/libedataserver/e-webdav-discover.c b/src/libedataserver/e-webdav-discover.c
index 204923f3a..9f822e079 100644
--- a/src/libedataserver/e-webdav-discover.c
+++ b/src/libedataserver/e-webdav-discover.c
@@ -110,215 +110,196 @@ e_webdav_discover_propfind_uri_sync (EWebDAVSession *webdav,
 
 static gboolean
 e_webdav_discover_traverse_propfind_response_cb (EWebDAVSession *webdav,
-                                                xmlXPathContextPtr xpath_ctx,
-                                                const gchar *xpath_prop_prefix,
+                                                xmlNodePtr prop_node,
                                                 const SoupURI *request_uri,
                                                 const gchar *href,
                                                 guint status_code,
                                                 gpointer user_data)
 {
        WebDAVDiscoverData *wdd = user_data;
+       xmlNodePtr set_node, node;
+       const xmlChar *href_value;
+       gboolean is_calendar, is_addressbook;
 
        g_return_val_if_fail (wdd != NULL, FALSE);
 
-       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;
-               gboolean is_calendar, is_addressbook;
-
-               xpath_obj = e_xml_xpath_eval (xpath_ctx, "%s/A:addressbook-home-set/D:href", 
xpath_prop_prefix);
-               if (xpath_obj) {
-                       gint ii, length;
-
-                       length = xmlXPathNodeSetGetLength (xpath_obj->nodesetval);
-
-                       for (ii = 0; ii < length && !g_cancellable_is_cancelled (wdd->cancellable); ii++) {
-                               gchar *home_set_href;
-
-                               full_href = NULL;
-
-                               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;
-                                       GError *local_error = NULL;
-
-                                       full_href = e_webdav_session_ensure_full_uri (webdav, request_uri, 
home_set_href);
-                                       if (full_href && *full_href && GPOINTER_TO_INT (g_hash_table_contains 
(wdd->covered_hrefs, full_href)) != 2 &&
-                                           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 | E_WEBDAV_LIST_ONLY_ADDRESSBOOK | 
E_WEBDAV_LIST_ALL,
-                                               &resources, wdd->cancellable, &local_error)) {
-                                               e_webdav_discover_split_resources (wdd, resources);
-                                               g_slist_free_full (resources, e_webdav_resource_free);
-                                       }
+       if (status_code != SOUP_STATUS_OK)
+               return TRUE;
 
-                                       if (full_href && *full_href)
-                                               g_hash_table_insert (wdd->covered_hrefs, g_strdup 
(full_href), GINT_TO_POINTER (2));
+       set_node = e_xml_find_child (prop_node, E_WEBDAV_NS_CARDDAV, "addressbook-home-set");
 
-                                       if (local_error && wdd->error && !*wdd->error)
-                                               g_propagate_error (wdd->error, local_error);
-                                       else
-                                               g_clear_error (&local_error);
-                               }
+       for (node = e_xml_find_child (set_node, E_WEBDAV_NS_DAV, "href");
+            node && !g_cancellable_is_cancelled (wdd->cancellable);
+            node = e_xml_find_next_sibling (node, E_WEBDAV_NS_DAV, "href")) {
+               const xmlChar *home_set_href;
+
+               home_set_href = e_xml_get_node_text (node);
+
+               if (home_set_href && *home_set_href) {
+                       GSList *resources = NULL;
+                       GError *local_error = NULL;
+                       gchar *full_href;
+
+                       full_href = e_webdav_session_ensure_full_uri (webdav, request_uri, (const gchar *) 
home_set_href);
 
-                               g_free (home_set_href);
-                               g_free (full_href);
+                       if (full_href && *full_href && GPOINTER_TO_INT (g_hash_table_contains 
(wdd->covered_hrefs, full_href)) != 2 &&
+                           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 | E_WEBDAV_LIST_ONLY_ADDRESSBOOK | E_WEBDAV_LIST_ALL,
+                               &resources, wdd->cancellable, &local_error)) {
+                               e_webdav_discover_split_resources (wdd, resources);
+                               g_slist_free_full (resources, e_webdav_resource_free);
                        }
 
-                       xmlXPathFreeObject (xpath_obj);
-               }
+                       if (full_href && *full_href)
+                               g_hash_table_insert (wdd->covered_hrefs, g_strdup (full_href), 
GINT_TO_POINTER (2));
 
-               xpath_obj = e_xml_xpath_eval (xpath_ctx, "%s/C:calendar-home-set/D:href", xpath_prop_prefix);
-               if (xpath_obj) {
-                       gint ii, length;
+                       if (local_error && wdd->error && !*wdd->error)
+                               g_propagate_error (wdd->error, local_error);
+                       else
+                               g_clear_error (&local_error);
 
-                       length = xmlXPathNodeSetGetLength (xpath_obj->nodesetval);
+                       g_free (full_href);
+               }
+       }
 
-                       for (ii = 0; ii < length && !g_cancellable_is_cancelled (wdd->cancellable); ii++) {
-                               gchar *home_set_href, *full_href = NULL;
+       set_node = e_xml_find_child (prop_node, E_WEBDAV_NS_CALDAV, "calendar-home-set");
 
-                               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;
-                                       GError *local_error = NULL;
+       for (node = e_xml_find_child (set_node, E_WEBDAV_NS_DAV, "href");
+            node && !g_cancellable_is_cancelled (wdd->cancellable);
+            node = e_xml_find_next_sibling (node, E_WEBDAV_NS_DAV, "href")) {
+               const xmlChar *home_set_href;
 
-                                       full_href = e_webdav_session_ensure_full_uri (webdav, request_uri, 
home_set_href);
-                                       if (full_href && *full_href && GPOINTER_TO_INT (g_hash_table_contains 
(wdd->covered_hrefs, full_href)) != 2 &&
-                                           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 | E_WEBDAV_LIST_ONLY_CALENDAR | 
E_WEBDAV_LIST_ALL,
-                                               &resources, wdd->cancellable, &local_error)) {
-                                               e_webdav_discover_split_resources (wdd, resources);
-                                               g_slist_free_full (resources, e_webdav_resource_free);
-                                       }
+               home_set_href = e_xml_get_node_text (node);
 
-                                       if (full_href && *full_href)
-                                               g_hash_table_insert (wdd->covered_hrefs, g_strdup 
(full_href), GINT_TO_POINTER (2));
+               if (home_set_href && *home_set_href) {
+                       GSList *resources = NULL;
+                       GError *local_error = NULL;
+                       gchar *full_href;
 
-                                       if (local_error && wdd->error && !*wdd->error)
-                                               g_propagate_error (wdd->error, local_error);
-                                       else
-                                               g_clear_error (&local_error);
-                               }
+                       full_href = e_webdav_session_ensure_full_uri (webdav, request_uri, (const gchar *) 
home_set_href);
 
-                               g_free (home_set_href);
-                               g_free (full_href);
+                       if (full_href && *full_href && GPOINTER_TO_INT (g_hash_table_contains 
(wdd->covered_hrefs, full_href)) != 2 &&
+                           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 | E_WEBDAV_LIST_ONLY_CALENDAR | E_WEBDAV_LIST_ALL,
+                               &resources, wdd->cancellable, &local_error)) {
+                               e_webdav_discover_split_resources (wdd, resources);
+                               g_slist_free_full (resources, e_webdav_resource_free);
                        }
 
-                       xmlXPathFreeObject (xpath_obj);
+                       if (full_href && *full_href)
+                               g_hash_table_insert (wdd->covered_hrefs, g_strdup (full_href), 
GINT_TO_POINTER (2));
+
+                       if (local_error && wdd->error && !*wdd->error)
+                               g_propagate_error (wdd->error, local_error);
+                       else
+                               g_clear_error (&local_error);
+
+                       g_free (full_href);
                }
+       }
 
-               xpath_obj = e_xml_xpath_eval (xpath_ctx, "%s/C:calendar-user-address-set/D:href", 
xpath_prop_prefix);
-               if (xpath_obj) {
-                       gint ii, length;
+       if (wdd->out_calendar_user_addresses) {
+               set_node = e_xml_find_child (prop_node, E_WEBDAV_NS_CALDAV, "calendar-user-address-set");
 
-                       if (wdd->out_calendar_user_addresses)
-                               length = xmlXPathNodeSetGetLength (xpath_obj->nodesetval);
-                       else
-                               length = 0;
+               for (node = e_xml_find_child (set_node, E_WEBDAV_NS_DAV, "href");
+                    node && !g_cancellable_is_cancelled (wdd->cancellable);
+                    node = e_xml_find_next_sibling (node, E_WEBDAV_NS_DAV, "href")) {
+                       const xmlChar *address_href;
 
-                       for (ii = 0; ii < length; ii++) {
-                               gchar *address_href;
+                       address_href = e_xml_get_node_text (node);
 
-                               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;
+                       if (address_href && g_ascii_strncasecmp ((const gchar *) address_href, "mailto:";, 7) 
== 0) {
+                               /* Skip the "mailto:"; prefix */
+                               const gchar *address = (const gchar *) (address_href + 7);
 
-                                       /* 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));
-                                       }
+                               /* 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));
                                }
-
-                               g_free (address_href);
                        }
-
-                       xmlXPathFreeObject (xpath_obj);
                }
+       }
 
-               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 && !g_cancellable_is_cancelled (wdd->cancellable)) {
-                       full_href = e_webdav_session_ensure_full_uri (webdav, request_uri, principal_href);
+       node = e_xml_find_in_hierarchy (prop_node, E_WEBDAV_NS_DAV, "current-user-principal", 
E_WEBDAV_NS_DAV, "href", NULL, NULL);
+       href_value = e_xml_get_node_text (node);
 
-                       if (full_href && *full_href)
-                               e_webdav_discover_propfind_uri_sync (webdav, wdd, full_href, TRUE);
+       if (href_value && *href_value && !g_cancellable_is_cancelled (wdd->cancellable)) {
+               gchar *full_href;
 
-                       g_free (full_href);
-                       g_free (principal_href);
+               full_href = e_webdav_session_ensure_full_uri (webdav, request_uri, (const gchar *) 
href_value);
 
-                       return TRUE;
-               }
+               if (full_href && *full_href)
+                       e_webdav_discover_propfind_uri_sync (webdav, wdd, full_href, TRUE);
 
-               g_free (principal_href);
+               g_free (full_href);
 
-               principal_href = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:principal-URL/D:href", 
xpath_prop_prefix);
-               if (principal_href && *principal_href && !g_cancellable_is_cancelled (wdd->cancellable)) {
-                       full_href = e_webdav_session_ensure_full_uri (webdav, request_uri, principal_href);
+               return TRUE;
+       }
 
-                       if (full_href && *full_href)
-                               e_webdav_discover_propfind_uri_sync (webdav, wdd, full_href, TRUE);
+       node = e_xml_find_in_hierarchy (prop_node, E_WEBDAV_NS_DAV, "principal-URL", E_WEBDAV_NS_DAV, "href", 
NULL, NULL);
+       href_value = e_xml_get_node_text (node);
 
-                       g_free (full_href);
-                       g_free (principal_href);
+       if (href_value && *href_value && !g_cancellable_is_cancelled (wdd->cancellable)) {
+               gchar *full_href;
 
-                       return TRUE;
-               }
+               full_href = e_webdav_session_ensure_full_uri (webdav, request_uri, (const gchar *) 
href_value);
 
-               g_free (principal_href);
+               if (full_href && *full_href)
+                       e_webdav_discover_propfind_uri_sync (webdav, wdd, full_href, TRUE);
 
-               is_calendar = e_xml_xpath_eval_exists (xpath_ctx, "%s/D:resourcetype/C:calendar", 
xpath_prop_prefix);
-               is_addressbook = e_xml_xpath_eval_exists (xpath_ctx, "%s/D:resourcetype/A:addressbook", 
xpath_prop_prefix);
+               g_free (full_href);
 
-               if (is_calendar || is_addressbook) {
-                       GSList *resources = NULL;
-                       GError *local_error = NULL;
+               return TRUE;
+       }
 
-                       if (GPOINTER_TO_INT (g_hash_table_contains (wdd->covered_hrefs, href)) != 2 &&
-                           !g_cancellable_is_cancelled (wdd->cancellable) &&
-                           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 |
-                               (is_calendar ? E_WEBDAV_LIST_ONLY_CALENDAR : 0) | (is_addressbook ? 
E_WEBDAV_LIST_ONLY_ADDRESSBOOK : 0) | E_WEBDAV_LIST_ALL,
-                               &resources, wdd->cancellable, &local_error)) {
-                               e_webdav_discover_split_resources (wdd, resources);
-                               g_slist_free_full (resources, e_webdav_resource_free);
-                       }
+       node = e_xml_find_child (prop_node, E_WEBDAV_NS_DAV, "resourcetype");
+       is_calendar = e_xml_find_child (node, E_WEBDAV_NS_CALDAV, "calendar") != NULL;
+       is_addressbook = e_xml_find_child (node, E_WEBDAV_NS_CARDDAV, "addressbook") != NULL;
 
-                       g_hash_table_insert (wdd->covered_hrefs, g_strdup (href), GINT_TO_POINTER (2));
+       if (is_calendar || is_addressbook) {
+               GSList *resources = NULL;
+               GError *local_error = NULL;
 
-                       if (local_error && wdd->error && !*wdd->error)
-                               g_propagate_error (wdd->error, local_error);
-                       else
-                               g_clear_error (&local_error);
+               if (GPOINTER_TO_INT (g_hash_table_contains (wdd->covered_hrefs, href)) != 2 &&
+                   !g_cancellable_is_cancelled (wdd->cancellable) &&
+                   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 |
+                       (is_calendar ? E_WEBDAV_LIST_ONLY_CALENDAR : 0) | (is_addressbook ? 
E_WEBDAV_LIST_ONLY_ADDRESSBOOK : 0) | E_WEBDAV_LIST_ALL,
+                       &resources, wdd->cancellable, &local_error)) {
+                       e_webdav_discover_split_resources (wdd, resources);
+                       g_slist_free_full (resources, e_webdav_resource_free);
                }
 
-               if (((wdd->only_supports & (~CUSTOM_SUPPORTS_FLAGS)) == E_WEBDAV_DISCOVER_SUPPORTS_NONE ||
-                   (wdd->only_supports & E_WEBDAV_DISCOVER_SUPPORTS_WEBDAV_NOTES) != 0) &&
-                   (g_str_has_suffix (href, "/Notes") ||
-                   g_str_has_suffix (href, "/Notes/")) &&
-                   !e_webdav_discovery_already_discovered (href, wdd->calendars) &&
-                   e_xml_xpath_eval_exists (xpath_ctx, "%s/D:resourcetype/D:collection", xpath_prop_prefix)) 
{
-                       GSList *resources = NULL;
+               g_hash_table_insert (wdd->covered_hrefs, g_strdup (href), GINT_TO_POINTER (2));
 
-                       resources = g_slist_prepend (NULL,
-                               e_webdav_resource_new (E_WEBDAV_RESOURCE_KIND_WEBDAV_NOTES,
-                                       E_WEBDAV_RESOURCE_SUPPORTS_WEBDAV_NOTES, href, NULL,
-                                       _("Notes"),
-                                       NULL, 0, 0, 0, NULL, NULL));
+               if (local_error && wdd->error && !*wdd->error)
+                       g_propagate_error (wdd->error, local_error);
+               else
+                       g_clear_error (&local_error);
+       }
 
-                       e_webdav_discover_split_resources (wdd, resources);
+       if (((wdd->only_supports & (~CUSTOM_SUPPORTS_FLAGS)) == E_WEBDAV_DISCOVER_SUPPORTS_NONE ||
+           (wdd->only_supports & E_WEBDAV_DISCOVER_SUPPORTS_WEBDAV_NOTES) != 0) &&
+           (g_str_has_suffix (href, "/Notes") || g_str_has_suffix (href, "/Notes/")) &&
+           !e_webdav_discovery_already_discovered (href, wdd->calendars) &&
+           e_xml_find_in_hierarchy (prop_node, E_WEBDAV_NS_DAV, "resourcetype", E_WEBDAV_NS_DAV, 
"collection", NULL, NULL)) {
+               GSList *resources = NULL;
 
-                       g_slist_free_full (resources, e_webdav_resource_free);
+               resources = g_slist_prepend (NULL,
+                       e_webdav_resource_new (E_WEBDAV_RESOURCE_KIND_WEBDAV_NOTES,
+                               E_WEBDAV_RESOURCE_SUPPORTS_WEBDAV_NOTES, href, NULL,
+                               _("Notes"),
+                               NULL, 0, 0, 0, NULL, NULL));
 
-                       g_hash_table_insert (wdd->covered_hrefs, g_strdup (href), GINT_TO_POINTER (2));
-               }
+               e_webdav_discover_split_resources (wdd, resources);
+
+               g_slist_free_full (resources, e_webdav_resource_free);
+
+               g_hash_table_insert (wdd->covered_hrefs, g_strdup (href), GINT_TO_POINTER (2));
        }
 
        return TRUE;
diff --git a/src/libedataserver/e-webdav-session.c b/src/libedataserver/e-webdav-session.c
index 50b747f7d..a8d07b9ed 100644
--- a/src/libedataserver/e-webdav-session.c
+++ b/src/libedataserver/e-webdav-session.c
@@ -752,8 +752,7 @@ e_webdav_session_new_request (EWebDAVSession *webdav,
 
 static gboolean
 e_webdav_session_extract_propstat_error_cb (EWebDAVSession *webdav,
-                                           xmlXPathContextPtr xpath_ctx,
-                                           const gchar *xpath_prop_prefix,
+                                           xmlNodePtr prop_node,
                                            const SoupURI *request_uri,
                                            const gchar *href,
                                            guint status_code,
@@ -763,26 +762,31 @@ e_webdav_session_extract_propstat_error_cb (EWebDAVSession *webdav,
 
        g_return_val_if_fail (error != NULL, FALSE);
 
-       if (!xpath_prop_prefix)
-               return TRUE;
-
        if (status_code != SOUP_STATUS_OK && (
            status_code != SOUP_STATUS_FAILED_DEPENDENCY ||
            !*error)) {
-               gchar *description;
+               xmlNodePtr parent;
+               const xmlChar *description = NULL;
 
-               description = e_xml_xpath_eval_as_string (xpath_ctx, "%s/../D:responsedescription", 
xpath_prop_prefix);
-               if (!description || !*description) {
-                       g_free (description);
+               parent = prop_node->parent;
+               if (parent) {
+                       description = e_xml_find_child_and_get_text (parent, E_WEBDAV_NS_DAV, 
"responsedescription");
 
-                       description = e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/../../D:responsedescription", xpath_prop_prefix);
+                       if (!description || !*description) {
+                               description = NULL;
+                               parent = parent->parent;
+                               if (parent) {
+                                       description = e_xml_find_child_and_get_text (parent, E_WEBDAV_NS_DAV, 
"responsedescription");
+
+                                       if (!description || !*description)
+                                               description = NULL;
+                               }
+                       }
                }
 
                g_clear_error (error);
                g_set_error_literal (error, SOUP_HTTP_ERROR, status_code,
-                       e_soup_session_util_status_to_string (status_code, description));
-
-               g_free (description);
+                       e_soup_session_util_status_to_string (status_code, (const gchar *) description));
        }
 
        return TRUE;
@@ -1413,13 +1417,7 @@ e_webdav_session_post_sync (EWebDAVSession *webdav,
  *
  * Issues PROPFIND request on the provided @uri, or, in case it's %NULL, on the URI
  * defined in associated #ESource. On success, calls @func for each returned
- * DAV:propstat. The provided XPath context has registered %E_WEBDAV_NS_DAV namespace
- * with prefix "D". It doesn't have any other namespace registered.
- *
- * The @func is called always at least once, with %NULL xpath_prop_prefix, which
- * is meant to let the caller setup the xpath_ctx, like to register its own namespaces
- * to it with e_xml_xpath_context_register_namespaces(). All other invocations of @func
- * will have xpath_prop_prefix non-%NULL.
+ * DAV:propstat.
  *
  * The @xml can be %NULL, in which case the server should behave like DAV:allprop request.
  *
@@ -1586,15 +1584,11 @@ e_webdav_session_proppatch_sync (EWebDAVSession *webdav,
  *
  * Issues REPORT request on the provided @uri, or, in case it's %NULL, on the URI
  * defined in associated #ESource. On success, calls @func for each returned
- * DAV:propstat. The provided XPath context has registered %E_WEBDAV_NS_DAV namespace
- * with prefix "D". It doesn't have any other namespace registered.
+ * DAV:propstat.
  *
  * The report can result in a multistatus response, but also to raw data. In case
  * the @func is provided and the result is a multistatus response, then it is traversed
- * using this @func. The @func is called always at least once, with %NULL xpath_prop_prefix,
- * which is meant to let the caller setup the xpath_ctx, like to register its own namespaces
- * to it with e_xml_xpath_context_register_namespaces(). All other invocations of @func
- * will have xpath_prop_prefix non-%NULL.
+ * using this @func.
  *
  * The optional @out_content_type can be used to get content type of the response.
  * Free it with g_free(), when no longer needed.
@@ -3128,20 +3122,22 @@ e_webdav_session_traverse_propstat_response (EWebDAVSession *webdav,
                                             const SoupMessage *message,
                                             const GByteArray *xml_data,
                                             gboolean require_multistatus,
-                                            const gchar *additional_ns_prefix,
-                                            const gchar *additional_ns,
-                                            const gchar *propstat_path_prefix,
+                                            const gchar *top_path_ns_href1,
+                                            const gchar *top_path_name1,
+                                            const gchar *top_path_ns_href2,
+                                            const gchar *top_path_name2,
                                             EWebDAVPropstatTraverseFunc func,
                                             gpointer func_user_data,
                                             GError **error)
 {
        SoupURI *request_uri = NULL;
        xmlDocPtr doc;
-       xmlXPathContextPtr xpath_ctx;
+       xmlNodePtr top_node, node;
+       gboolean do_stop = FALSE;
 
        g_return_val_if_fail (E_IS_WEBDAV_SESSION (webdav), FALSE);
        g_return_val_if_fail (xml_data != NULL, FALSE);
-       g_return_val_if_fail (propstat_path_prefix != NULL, FALSE);
+       g_return_val_if_fail (top_path_name1 != NULL, FALSE);
        g_return_val_if_fail (func != NULL, FALSE);
 
        if (message) {
@@ -3182,75 +3178,81 @@ e_webdav_session_traverse_propstat_response (EWebDAVSession *webdav,
                return FALSE;
        }
 
-       xpath_ctx = e_xml_new_xpath_context_with_namespaces (doc,
-               "D", E_WEBDAV_NS_DAV,
-               additional_ns_prefix, additional_ns,
-               NULL);
+       top_node = xmlDocGetRootElement (doc);
 
-       if (xpath_ctx &&
-           func (webdav, xpath_ctx, NULL, request_uri, NULL, SOUP_STATUS_NONE, func_user_data)) {
-               xmlXPathObjectPtr xpath_obj_response;
+       if (!top_node) {
+               g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+                       _("XML data does not have root node"));
 
-               xpath_obj_response = e_xml_xpath_eval (xpath_ctx, "%s", propstat_path_prefix);
+               xmlFreeDoc (doc);
 
-               if (xpath_obj_response) {
-                       gboolean do_stop = FALSE;
-                       gint response_index, response_length;
+               return FALSE;
+       }
 
-                       response_length = xmlXPathNodeSetGetLength (xpath_obj_response->nodesetval);
+       top_node = e_xml_find_sibling (top_node, top_path_ns_href1, top_path_name1);
 
-                       for (response_index = 0; response_index < response_length && !do_stop; 
response_index++) {
-                               xmlXPathObjectPtr xpath_obj_propstat;
+       if (top_path_name2)
+               top_node = e_xml_find_child (top_node, top_path_ns_href2, top_path_name2);
 
-                               xpath_obj_propstat = e_xml_xpath_eval (xpath_ctx,
-                                       "%s[%d]/D:propstat",
-                                       propstat_path_prefix, response_index + 1);
+       if (!top_node) {
+               gchar *tmp;
 
-                               if (xpath_obj_propstat) {
-                                       gchar *href;
-                                       gint propstat_index, propstat_length;
+               tmp = g_strconcat (
+                       top_path_ns_href1 ? top_path_ns_href1 : "",
+                       top_path_ns_href1 ? ":" : "",
+                       top_path_name1,
+                       top_path_name2 ? "/" : "",
+                       (top_path_name2 && top_path_ns_href2) ? top_path_ns_href2 : "",
+                       (top_path_name2 && top_path_ns_href2) ? ":" : "",
+                       top_path_name2 ? top_path_name2 : "",
+                       NULL);
 
-                                       href = e_xml_xpath_eval_as_string (xpath_ctx, "%s[%d]/D:href", 
propstat_path_prefix, response_index + 1);
-                                       if (href) {
-                                               gchar *full_uri;
+               g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+                       _("XML data doesn't have required structure (%s)"), tmp);
 
-                                               full_uri = e_webdav_session_ensure_full_uri (webdav, 
request_uri, href);
-                                               if (full_uri) {
-                                                       g_free (href);
-                                                       href = full_uri;
-                                               }
-                                       }
+               xmlFreeDoc (doc);
+               g_free (tmp);
 
-                                       propstat_length = xmlXPathNodeSetGetLength 
(xpath_obj_propstat->nodesetval);
+               return FALSE;
+       }
 
-                                       for (propstat_index = 0; propstat_index < propstat_length && 
!do_stop; propstat_index++) {
-                                               gchar *status, *propstat_prefix;
-                                               guint status_code;
+       for (node = top_node; node && !do_stop; node = xmlNextElementSibling (node)) {
+               xmlNodePtr href_node = NULL, propstat_node = NULL;
+               xmlNodePtr status_node = NULL, prop_node = NULL;
+               const xmlChar *href_content, *status_content;
+               guint status_code;
+               gchar *full_uri;
 
-                                               propstat_prefix = g_strdup_printf 
("%s[%d]/D:propstat[%d]/D:prop",
-                                                       propstat_path_prefix, response_index + 1, 
propstat_index + 1);
+               e_xml_find_children_nodes (node, 2,
+                       E_WEBDAV_NS_DAV, "href", &href_node,
+                       E_WEBDAV_NS_DAV, "propstat", &propstat_node);
 
-                                               status = e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/../D:status", propstat_prefix);
-                                               if (!status || !soup_headers_parse_status_line (status, NULL, 
&status_code, NULL))
-                                                       status_code = 0;
-                                               g_free (status);
+               if (!href_node || !propstat_node)
+                       continue;
 
-                                               do_stop = !func (webdav, xpath_ctx, propstat_prefix, 
request_uri, href, status_code, func_user_data);
+               href_content = e_xml_get_node_text (href_node);
+               g_warn_if_fail (href_content != NULL);
 
-                                               g_free (propstat_prefix);
-                                       }
+               if (!href_content)
+                       continue;
 
-                                       xmlXPathFreeObject (xpath_obj_propstat);
-                                       g_free (href);
-                               }
-                       }
+               full_uri = e_webdav_session_ensure_full_uri (webdav, request_uri, (const gchar *) 
href_content);
 
-                       xmlXPathFreeObject (xpath_obj_response);
-               }
+               e_xml_find_children_nodes (propstat_node, 2,
+                       E_WEBDAV_NS_DAV, "status", &status_node,
+                       E_WEBDAV_NS_DAV, "prop", &prop_node);
+
+               status_content = e_xml_get_node_text (status_node);
+
+               if (!status_content || !soup_headers_parse_status_line ((const gchar *) status_content, NULL, 
&status_code, NULL))
+                       status_code = 0;
+
+               if (prop_node && prop_node->children)
+                       do_stop = !func (webdav, prop_node, request_uri, full_uri ? full_uri : (const gchar 
*) href_content, status_code, func_user_data);
+
+               g_free (full_uri);
        }
 
-       if (xpath_ctx)
-               xmlXPathFreeContext (xpath_ctx);
        xmlFreeDoc (doc);
 
        return TRUE;
@@ -3266,17 +3268,10 @@ e_webdav_session_traverse_propstat_response (EWebDAVSession *webdav,
  * @error: return location for a #GError, or %NULL
  *
  * Traverses a DAV:multistatus response and calls @func for each returned DAV:propstat.
- * The provided XPath context has registered %E_WEBDAV_NS_DAV namespace with prefix "D".
- * It doesn't have any other namespace registered.
  *
  * The @message, if provided, is used to verify that the response is a multi-status
  * and that the Content-Type is properly set. It's used to get a request URI as well.
  *
- * The @func is called always at least once, with %NULL xpath_prop_prefix, which
- * is meant to let the caller setup the xpath_ctx, like to register its own namespaces
- * to it with e_xml_xpath_context_register_namespaces(). All other invocations of @func
- * will have xpath_prop_prefix non-%NULL.
- *
  * Returns: Whether succeeded.
  *
  * Since: 3.26
@@ -3294,7 +3289,8 @@ e_webdav_session_traverse_multistatus_response (EWebDAVSession *webdav,
        g_return_val_if_fail (func != NULL, FALSE);
 
        return e_webdav_session_traverse_propstat_response (webdav, message, xml_data, TRUE,
-               NULL, NULL, "/D:multistatus/D:response",
+               E_WEBDAV_NS_DAV, "multistatus",
+               E_WEBDAV_NS_DAV, "response",
                func, func_user_data, error);
 }
 
@@ -3308,17 +3304,10 @@ e_webdav_session_traverse_multistatus_response (EWebDAVSession *webdav,
  * @error: return location for a #GError, or %NULL
  *
  * Traverses a DAV:mkcol-response response and calls @func for each returned DAV:propstat.
- * The provided XPath context has registered %E_WEBDAV_NS_DAV namespace with prefix "D".
- * It doesn't have any other namespace registered.
  *
  * The @message, if provided, is used to verify that the response is an XML Content-Type.
  * It's used to get the request URI as well.
  *
- * The @func is called always at least once, with %NULL xpath_prop_prefix, which
- * is meant to let the caller setup the xpath_ctx, like to register its own namespaces
- * to it with e_xml_xpath_context_register_namespaces(). All other invocations of @func
- * will have xpath_prop_prefix non-%NULL.
- *
  * Returns: Whether succeeded.
  *
  * Since: 3.26
@@ -3336,7 +3325,8 @@ e_webdav_session_traverse_mkcol_response (EWebDAVSession *webdav,
        g_return_val_if_fail (func != NULL, FALSE);
 
        return e_webdav_session_traverse_propstat_response (webdav, message, xml_data, FALSE,
-               NULL, NULL, "/D:mkcol-response",
+               E_WEBDAV_NS_DAV, "mkcol-response",
+               NULL, NULL,
                func, func_user_data, error);
 }
 
@@ -3350,17 +3340,10 @@ e_webdav_session_traverse_mkcol_response (EWebDAVSession *webdav,
  * @error: return location for a #GError, or %NULL
  *
  * Traverses a CALDAV:mkcalendar-response response and calls @func for each returned DAV:propstat.
- * The provided XPath context has registered %E_WEBDAV_NS_DAV namespace with prefix "D" and
- * %E_WEBDAV_NS_CALDAV namespace with prefix "C". It doesn't have any other namespace registered.
  *
  * The @message, if provided, is used to verify that the response is an XML Content-Type.
  * It's used to get the request URI as well.
  *
- * The @func is called always at least once, with %NULL xpath_prop_prefix, which
- * is meant to let the caller setup the xpath_ctx, like to register its own namespaces
- * to it with e_xml_xpath_context_register_namespaces(). All other invocations of @func
- * will have xpath_prop_prefix non-%NULL.
- *
  * Returns: Whether succeeded.
  *
  * Since: 3.26
@@ -3378,40 +3361,29 @@ e_webdav_session_traverse_mkcalendar_response (EWebDAVSession *webdav,
        g_return_val_if_fail (func != NULL, FALSE);
 
        return e_webdav_session_traverse_propstat_response (webdav, message, xml_data, FALSE,
-               "C", E_WEBDAV_NS_CALDAV, "/C:mkcalendar-response",
+               E_WEBDAV_NS_CALDAV, "mkcalendar-response",
+               NULL, NULL,
                func, func_user_data, error);
 }
 
 static gboolean
 e_webdav_session_getctag_cb (EWebDAVSession *webdav,
-                            xmlXPathContextPtr xpath_ctx,
-                            const gchar *xpath_prop_prefix,
+                            xmlNodePtr prop_node,
                             const SoupURI *request_uri,
                             const gchar *href,
                             guint status_code,
                             gpointer user_data)
 {
-       if (!xpath_prop_prefix) {
-               e_xml_xpath_context_register_namespaces (xpath_ctx,
-                       "CS", E_WEBDAV_NS_CALENDARSERVER,
-                       NULL);
-
-               return TRUE;
-       }
-
        if (status_code == SOUP_STATUS_OK) {
+               const xmlChar *ctag_content;
                gchar **out_ctag = user_data;
-               gchar *ctag;
 
                g_return_val_if_fail (out_ctag != NULL, FALSE);
 
-               ctag = e_xml_xpath_eval_as_string (xpath_ctx, "%s/CS:getctag", xpath_prop_prefix);
+               ctag_content = e_xml_find_child_and_get_text (prop_node, E_WEBDAV_NS_CALENDARSERVER, 
"getctag");
 
-               if (ctag && *ctag) {
-                       *out_ctag = e_webdav_session_util_maybe_dequote (ctag);
-               } else {
-                       g_free (ctag);
-               }
+               if (ctag_content && *ctag_content)
+                       *out_ctag = e_webdav_session_util_maybe_dequote (g_strdup ((const gchar *) 
ctag_content));
        }
 
        return FALSE;
@@ -3470,79 +3442,79 @@ e_webdav_session_getctag_sync (EWebDAVSession *webdav,
 }
 
 static EWebDAVResourceKind
-e_webdav_session_extract_kind (xmlXPathContextPtr xpath_ctx,
-                              const gchar *xpath_prop_prefix)
+e_webdav_session_extract_kind (xmlNodePtr parent_node)
 {
-       g_return_val_if_fail (xpath_ctx != NULL, E_WEBDAV_RESOURCE_KIND_UNKNOWN);
-       g_return_val_if_fail (xpath_prop_prefix != NULL, E_WEBDAV_RESOURCE_KIND_UNKNOWN);
+       xmlNodePtr resourcetype;
+
+       g_return_val_if_fail (parent_node != NULL, E_WEBDAV_RESOURCE_KIND_UNKNOWN);
+
+       resourcetype = e_xml_find_child (parent_node, E_WEBDAV_NS_DAV, "resourcetype");
 
-       if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:resourcetype/A:addressbook", xpath_prop_prefix))
+       if (e_xml_find_child (resourcetype, E_WEBDAV_NS_CARDDAV, "addressbook"))
                return E_WEBDAV_RESOURCE_KIND_ADDRESSBOOK;
 
-       if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:resourcetype/C:calendar", xpath_prop_prefix))
+       if (e_xml_find_child (resourcetype, E_WEBDAV_NS_CALDAV, "calendar"))
                return E_WEBDAV_RESOURCE_KIND_CALENDAR;
 
        /* These are subscribed iCalendar files, aka 'On The Web' calendars */
-       if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:resourcetype/D:collection", xpath_prop_prefix) &&
-           e_xml_xpath_eval_exists (xpath_ctx, "%s/D:resourcetype/CS:subscribed", xpath_prop_prefix) &&
-           e_xml_xpath_eval_exists (xpath_ctx, "%s/CS:source/D:href", xpath_prop_prefix))
+       if (e_xml_find_child (resourcetype, E_WEBDAV_NS_DAV, "collection") &&
+           e_xml_find_child (resourcetype, E_WEBDAV_NS_CALENDARSERVER, "subscribed") &&
+           e_xml_find_in_hierarchy (parent_node, E_WEBDAV_NS_CALENDARSERVER, "source", E_WEBDAV_NS_DAV, 
"href", NULL, NULL))
                return E_WEBDAV_RESOURCE_KIND_SUBSCRIBED_ICALENDAR;
 
-       if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:resourcetype/D:principal", xpath_prop_prefix))
+       if (e_xml_find_child (resourcetype, E_WEBDAV_NS_DAV, "principal"))
                return E_WEBDAV_RESOURCE_KIND_PRINCIPAL;
 
-       if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:resourcetype/D:collection", xpath_prop_prefix))
+       if (e_xml_find_child (resourcetype, E_WEBDAV_NS_DAV, "collection"))
                return E_WEBDAV_RESOURCE_KIND_COLLECTION;
 
        return E_WEBDAV_RESOURCE_KIND_RESOURCE;
 }
 
 static guint32
-e_webdav_session_extract_supports (xmlXPathContextPtr xpath_ctx,
-                                  const gchar *xpath_prop_prefix)
+e_webdav_session_extract_supports (xmlNodePtr prop_node)
 {
+       xmlNodePtr calendar_components;
        guint32 supports = E_WEBDAV_RESOURCE_SUPPORTS_NONE;
 
-       g_return_val_if_fail (xpath_ctx != NULL, E_WEBDAV_RESOURCE_SUPPORTS_NONE);
-       g_return_val_if_fail (xpath_prop_prefix != NULL, E_WEBDAV_RESOURCE_SUPPORTS_NONE);
+       g_return_val_if_fail (prop_node != NULL, E_WEBDAV_RESOURCE_SUPPORTS_NONE);
 
-       if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:resourcetype/A:addressbook", xpath_prop_prefix))
+       if (e_xml_find_in_hierarchy (prop_node, E_WEBDAV_NS_DAV, "resourcetype", E_WEBDAV_NS_CARDDAV, 
"addressbook", NULL, NULL))
                supports = supports | E_WEBDAV_RESOURCE_SUPPORTS_CONTACTS;
 
-       if (e_xml_xpath_eval_exists (xpath_ctx, "%s/C:supported-calendar-component-set", xpath_prop_prefix)) {
-               xmlXPathObjectPtr xpath_obj;
+       calendar_components = e_xml_find_child (prop_node, E_WEBDAV_NS_CALDAV, 
"supported-calendar-component-set");
 
-               xpath_obj = e_xml_xpath_eval (xpath_ctx, "%s/C:supported-calendar-component-set/C:comp", 
xpath_prop_prefix);
-               if (xpath_obj) {
-                       gint ii, length;
+       if (calendar_components) {
+               xmlNodePtr node;
+               gint found_comps = 0;
 
-                       length = xmlXPathNodeSetGetLength (xpath_obj->nodesetval);
+               for (node = calendar_components->children; node; node = xmlNextElementSibling (node)) {
+                       if (e_xml_is_element_name (node, E_WEBDAV_NS_CALDAV, "comp")) {
+                               xmlChar *name;
 
-                       for (ii = 0; ii < length; ii++) {
-                               gchar *name;
+                               found_comps++;
 
-                               name = e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/C:supported-calendar-component-set/C:comp[%d]/@name",
-                                       xpath_prop_prefix, ii + 1);
+                               name = xmlGetProp (node, (const xmlChar *) "name");
 
                                if (!name)
                                        continue;
 
-                               if (g_ascii_strcasecmp (name, "VEVENT") == 0)
+                               if (g_ascii_strcasecmp ((const gchar *) name, "VEVENT") == 0)
                                        supports |= E_WEBDAV_RESOURCE_SUPPORTS_EVENTS;
-                               else if (g_ascii_strcasecmp (name, "VJOURNAL") == 0)
+                               else if (g_ascii_strcasecmp ((const gchar *) name, "VJOURNAL") == 0)
                                        supports |= E_WEBDAV_RESOURCE_SUPPORTS_MEMOS;
-                               else if (g_ascii_strcasecmp (name, "VTODO") == 0)
+                               else if (g_ascii_strcasecmp ((const gchar *) name, "VTODO") == 0)
                                        supports |= E_WEBDAV_RESOURCE_SUPPORTS_TASKS;
-                               else if (g_ascii_strcasecmp (name, "VFREEBUSY") == 0)
+                               else if (g_ascii_strcasecmp ((const gchar *) name, "VFREEBUSY") == 0)
                                        supports |= E_WEBDAV_RESOURCE_SUPPORTS_FREEBUSY;
-                               else if (g_ascii_strcasecmp (name, "VTIMEZONE") == 0)
+                               else if (g_ascii_strcasecmp ((const gchar *) name, "VTIMEZONE") == 0)
                                        supports |= E_WEBDAV_RESOURCE_SUPPORTS_TIMEZONE;
 
-                               g_free (name);
+                               xmlFree (name);
                        }
+               }
 
-                       xmlXPathFreeObject (xpath_obj);
-               } else {
+               if (!found_comps) {
                        /* If the property is not present, assume all component
                         * types are supported.  (RFC 4791, Section 5.2.3) */
                        supports = supports |
@@ -3558,20 +3530,30 @@ e_webdav_session_extract_supports (xmlXPathContextPtr xpath_ctx,
 }
 
 static gchar *
-e_webdav_session_extract_nonempty (xmlXPathContextPtr xpath_ctx,
-                                  const gchar *xpath_prop_prefix,
-                                  const gchar *prop,
-                                  const gchar *alternative_prop)
+e_webdav_session_extract_nonempty (xmlNodePtr parent,
+                                  const gchar *prop_ns_href,
+                                  const gchar *prop_name,
+                                  const gchar *alternative_prop_ns_href,
+                                  const gchar *alternative_prop_name)
 {
-       gchar *value;
+       const xmlChar *x_value;
+       gchar *value = NULL;
 
-       g_return_val_if_fail (xpath_ctx != NULL, NULL);
-       g_return_val_if_fail (xpath_prop_prefix != NULL, NULL);
-       g_return_val_if_fail (prop != NULL, NULL);
+       g_return_val_if_fail (parent != NULL, NULL);
+       g_return_val_if_fail (prop_name != NULL, NULL);
+
+       x_value = e_xml_find_child_and_get_text (parent, prop_ns_href, prop_name);
+
+       if (x_value && *x_value)
+               value = g_strdup ((const gchar *) x_value);
+
+       if (!value && alternative_prop_name) {
+               x_value = e_xml_find_child_and_get_text (parent, alternative_prop_ns_href, 
alternative_prop_name);
+
+               if (x_value && *x_value)
+                       value = g_strdup ((const gchar *) x_value);
+       }
 
-       value = e_xml_xpath_eval_as_string (xpath_ctx, "%s/%s", xpath_prop_prefix, prop);
-       if (!value && alternative_prop)
-               value = e_xml_xpath_eval_as_string (xpath_ctx, "%s/%s", xpath_prop_prefix, alternative_prop);
        if (!value)
                return NULL;
 
@@ -3584,16 +3566,14 @@ e_webdav_session_extract_nonempty (xmlXPathContextPtr xpath_ctx,
 }
 
 static gsize
-e_webdav_session_extract_content_length (xmlXPathContextPtr xpath_ctx,
-                                        const gchar *xpath_prop_prefix)
+e_webdav_session_extract_content_length (xmlNodePtr parent)
 {
        gchar *value;
        gsize length;
 
-       g_return_val_if_fail (xpath_ctx != NULL, 0);
-       g_return_val_if_fail (xpath_prop_prefix != NULL, 0);
+       g_return_val_if_fail (parent != NULL, 0);
 
-       value = e_webdav_session_extract_nonempty (xpath_ctx, xpath_prop_prefix, "D:getcontentlength", NULL);
+       value = e_webdav_session_extract_nonempty (parent, E_WEBDAV_NS_DAV, "getcontentlength", NULL, NULL);
        if (!value)
                return 0;
 
@@ -3605,18 +3585,18 @@ e_webdav_session_extract_content_length (xmlXPathContextPtr xpath_ctx,
 }
 
 static glong
-e_webdav_session_extract_datetime (xmlXPathContextPtr xpath_ctx,
-                                  const gchar *xpath_prop_prefix,
+e_webdav_session_extract_datetime (xmlNodePtr parent,
+                                  const gchar *ns_href,
                                   const gchar *prop,
                                   gboolean is_iso_property)
 {
        gchar *value;
        GTimeVal tv;
 
-       g_return_val_if_fail (xpath_ctx != NULL, -1);
-       g_return_val_if_fail (xpath_prop_prefix != NULL, -1);
+       g_return_val_if_fail (parent != NULL, -1);
+       g_return_val_if_fail (prop != NULL, -1);
 
-       value = e_webdav_session_extract_nonempty (xpath_ctx, xpath_prop_prefix, prop, NULL);
+       value = e_webdav_session_extract_nonempty (parent, ns_href, prop, NULL, NULL);
        if (!value)
                return -1;
 
@@ -3633,8 +3613,7 @@ e_webdav_session_extract_datetime (xmlXPathContextPtr xpath_ctx,
 
 static gboolean
 e_webdav_session_list_cb (EWebDAVSession *webdav,
-                         xmlXPathContextPtr xpath_ctx,
-                         const gchar *xpath_prop_prefix,
+                         xmlNodePtr prop_node,
                          const SoupURI *request_uri,
                          const gchar *href,
                          guint status_code,
@@ -3645,17 +3624,6 @@ e_webdav_session_list_cb (EWebDAVSession *webdav,
        g_return_val_if_fail (out_resources != NULL, FALSE);
        g_return_val_if_fail (request_uri != NULL, FALSE);
 
-       if (!xpath_prop_prefix) {
-               e_xml_xpath_context_register_namespaces (xpath_ctx,
-                       "CS", E_WEBDAV_NS_CALENDARSERVER,
-                       "C", E_WEBDAV_NS_CALDAV,
-                       "A", E_WEBDAV_NS_CARDDAV,
-                       "IC", E_WEBDAV_NS_ICAL,
-                       NULL);
-
-               return TRUE;
-       }
-
        if (status_code == SOUP_STATUS_OK) {
                EWebDAVResource *resource;
                EWebDAVResourceKind kind;
@@ -3670,26 +3638,38 @@ e_webdav_session_list_cb (EWebDAVSession *webdav,
                gchar *color;
                gchar *source_href = NULL;
 
-               kind = e_webdav_session_extract_kind (xpath_ctx, xpath_prop_prefix);
+               kind = e_webdav_session_extract_kind (prop_node);
                if (kind == E_WEBDAV_RESOURCE_KIND_UNKNOWN)
                        return TRUE;
 
                if (kind == E_WEBDAV_RESOURCE_KIND_SUBSCRIBED_ICALENDAR) {
-                       source_href = e_webdav_session_extract_nonempty (xpath_ctx, xpath_prop_prefix, 
"CS:source/D:href", NULL);
+                       xmlNodePtr source_href_node;
+                       const xmlChar *x_source_href = NULL;
+
+
+                       source_href_node = e_xml_find_in_hierarchy (prop_node, E_WEBDAV_NS_CALENDARSERVER, 
"source", E_WEBDAV_NS_DAV, "href", NULL, NULL);
+
+                       if (!source_href_node)
+                               return TRUE;
+
+                       x_source_href = e_xml_get_node_text (source_href_node);
 
-                       if (!source_href)
+                       if (!x_source_href || !*x_source_href)
                                return TRUE;
+
+                       source_href = e_webdav_session_util_maybe_dequote (g_strdup ((const gchar *) 
x_source_href));
                }
 
-               supports = e_webdav_session_extract_supports (xpath_ctx, xpath_prop_prefix);
-               etag = e_webdav_session_extract_nonempty (xpath_ctx, xpath_prop_prefix, "D:getetag", 
"CS:getctag");
-               display_name = e_webdav_session_extract_nonempty (xpath_ctx, xpath_prop_prefix, 
"D:displayname", NULL);
-               content_type = e_webdav_session_extract_nonempty (xpath_ctx, xpath_prop_prefix, 
"D:getcontenttype", NULL);
-               content_length = e_webdav_session_extract_content_length (xpath_ctx, xpath_prop_prefix);
-               creation_date = e_webdav_session_extract_datetime (xpath_ctx, xpath_prop_prefix, 
"D:creationdate", TRUE);
-               last_modified = e_webdav_session_extract_datetime (xpath_ctx, xpath_prop_prefix, 
"D:getlastmodified", FALSE);
-               description = e_webdav_session_extract_nonempty (xpath_ctx, xpath_prop_prefix, 
"C:calendar-description", "A:addressbook-description");
-               color = e_webdav_session_extract_nonempty (xpath_ctx, xpath_prop_prefix, "IC:calendar-color", 
NULL);
+               supports = e_webdav_session_extract_supports (prop_node);
+               etag = e_webdav_session_extract_nonempty (prop_node, E_WEBDAV_NS_DAV, "getetag", 
E_WEBDAV_NS_CALENDARSERVER, "getctag");
+               display_name = e_webdav_session_extract_nonempty (prop_node, E_WEBDAV_NS_DAV, "displayname", 
NULL, NULL);
+               content_type = e_webdav_session_extract_nonempty (prop_node, E_WEBDAV_NS_DAV, 
"getcontenttype", NULL, NULL);
+               content_length = e_webdav_session_extract_content_length (prop_node);
+               creation_date = e_webdav_session_extract_datetime (prop_node, E_WEBDAV_NS_DAV, 
"creationdate", TRUE);
+               last_modified = e_webdav_session_extract_datetime (prop_node, E_WEBDAV_NS_DAV, 
"getlastmodified", FALSE);
+               description = e_webdav_session_extract_nonempty (prop_node, E_WEBDAV_NS_CALDAV, 
"calendar-description",
+                       E_WEBDAV_NS_CARDDAV, "addressbook-description");
+               color = e_webdav_session_extract_nonempty (prop_node, E_WEBDAV_NS_ICAL, "calendar-color", 
NULL, NULL);
 
                resource = e_webdav_resource_new (kind, supports,
                        source_href ? source_href : href,
@@ -4061,81 +4041,53 @@ e_webdav_session_lock_resource_sync (EWebDAVSession *webdav,
 }
 
 static void
-e_webdav_session_traverse_privilege_level (xmlXPathContextPtr xpath_ctx,
-                                          const gchar *xpath_prefix,
+e_webdav_session_traverse_privilege_level (xmlNodePtr parent_node,
                                           GNode *parent)
 {
-       xmlXPathObjectPtr xpath_obj;
+       xmlNodePtr node;
 
-       g_return_if_fail (xpath_ctx != NULL);
-       g_return_if_fail (xpath_prefix != NULL);
+       g_return_if_fail (parent_node != NULL);
        g_return_if_fail (parent != NULL);
 
-       xpath_obj = e_xml_xpath_eval (xpath_ctx, "%s/D:supported-privilege", xpath_prefix);
-
-       if (xpath_obj) {
-               gint ii, length;
-
-               length = xmlXPathNodeSetGetLength (xpath_obj->nodesetval);
-
-               for (ii = 0; ii < length; ii++) {
-                       xmlXPathObjectPtr xpath_obj_privilege;
-                       gchar *prefix;
-
-                       prefix = g_strdup_printf ("%s/D:supported-privilege[%d]", xpath_prefix, ii + 1);
-                       xpath_obj_privilege = e_xml_xpath_eval (xpath_ctx, "%s/D:privilege", prefix);
-
-                       if (xpath_obj_privilege &&
-                           xpath_obj_privilege->type == XPATH_NODESET &&
-                           xpath_obj_privilege->nodesetval &&
-                           xpath_obj_privilege->nodesetval->nodeNr == 1 &&
-                           xpath_obj_privilege->nodesetval->nodeTab &&
-                           xpath_obj_privilege->nodesetval->nodeTab[0] &&
-                           xpath_obj_privilege->nodesetval->nodeTab[0]->children) {
-                               xmlNodePtr node;
-
-                               for (node = xpath_obj_privilege->nodesetval->nodeTab[0]->children; node; node 
= node->next) {
-                                       if (node->type == XML_ELEMENT_NODE &&
-                                           node->name && *(node->name) &&
-                                           node->ns && node->ns->href && *(node->ns->href)) {
-                                               GNode *child;
-                                               gchar *description;
-                                               EWebDAVPrivilegeKind kind = E_WEBDAV_PRIVILEGE_KIND_COMMON;
-                                               EWebDAVPrivilegeHint hint = E_WEBDAV_PRIVILEGE_HINT_UNKNOWN;
-                                               EWebDAVPrivilege *privilege;
-
-                                               if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:abstract", 
prefix))
-                                                       kind = E_WEBDAV_PRIVILEGE_KIND_ABSTRACT;
-                                               else if (e_xml_xpath_eval_exists (xpath_ctx, 
"%s/D:aggregate", prefix))
-                                                       kind = E_WEBDAV_PRIVILEGE_KIND_AGGREGATE;
-
-                                               description = e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/D:description", prefix);
-                                               privilege = e_webdav_privilege_new ((const gchar *) 
node->ns->href, (const gchar *) node->name, description, kind, hint);
-                                               child = g_node_new (privilege);
-                                               g_node_append (parent, child);
-
-                                               g_free (description);
-
-                                               if (e_xml_xpath_eval_exists (xpath_ctx, 
"%s/D:supported-privilege", prefix))
-                                                       e_webdav_session_traverse_privilege_level (xpath_ctx, 
prefix, child);
-                                       }
-                               }
-                       }
-
-                       if (xpath_obj_privilege)
-                               xmlXPathFreeObject (xpath_obj_privilege);
-
-                       g_free (prefix);
+       for (node = e_xml_find_child (parent_node, E_WEBDAV_NS_DAV, "supported-privilege");
+            node;
+            node = e_xml_find_next_sibling (node, E_WEBDAV_NS_DAV, "supported-privilege")) {
+               xmlNodePtr privilege_node;
+
+               privilege_node = e_xml_find_child (node, E_WEBDAV_NS_DAV, "privilege");
+
+               if (privilege_node) {
+                       GNode *child;
+                       const xmlChar *description;
+                       EWebDAVPrivilegeKind kind = E_WEBDAV_PRIVILEGE_KIND_COMMON;
+                       EWebDAVPrivilegeHint hint = E_WEBDAV_PRIVILEGE_HINT_UNKNOWN;
+                       EWebDAVPrivilege *privilege;
+
+                       if (e_xml_find_child (privilege_node, E_WEBDAV_NS_DAV, "abstract"))
+                               kind = E_WEBDAV_PRIVILEGE_KIND_ABSTRACT;
+                       else if (e_xml_find_child (privilege_node, E_WEBDAV_NS_DAV, "aggregate"))
+                               kind = E_WEBDAV_PRIVILEGE_KIND_AGGREGATE;
+
+                       description = e_xml_find_child_and_get_text (privilege_node, E_WEBDAV_NS_DAV, 
"description");
+                       privilege = e_webdav_privilege_new ((const gchar *) ((privilege_node->ns && 
privilege_node->ns->href) ? privilege_node->ns->href : NULL),
+                               (const gchar *) privilege_node->name,
+                               (const gchar *) description,
+                               kind,
+                               hint);
+                       child = g_node_new (privilege);
+                       g_node_append (parent, child);
+
+                       privilege_node = e_xml_find_child (privilege_node, E_WEBDAV_NS_DAV, 
"supported-privilege");
+
+                       if (privilege_node)
+                               e_webdav_session_traverse_privilege_level (privilege_node, child);
                }
-
-               xmlXPathFreeObject (xpath_obj);
        }
 }
 
 static gboolean
 e_webdav_session_supported_privilege_set_cb (EWebDAVSession *webdav,
-                                            xmlXPathContextPtr xpath_ctx,
-                                            const gchar *xpath_prop_prefix,
+                                            xmlNodePtr prop_node,
                                             const SoupURI *request_uri,
                                             const gchar *href,
                                             guint status_code,
@@ -4145,23 +4097,15 @@ e_webdav_session_supported_privilege_set_cb (EWebDAVSession *webdav,
 
        g_return_val_if_fail (out_privileges != NULL, FALSE);
 
-       if (!xpath_prop_prefix) {
-               e_xml_xpath_context_register_namespaces (xpath_ctx,
-                       "C", E_WEBDAV_NS_CALDAV,
-                       NULL);
-       } else if (status_code == SOUP_STATUS_OK &&
-                  e_xml_xpath_eval_exists (xpath_ctx, "%s/D:supported-privilege-set/D:supported-privilege", 
xpath_prop_prefix)) {
+       if (status_code == SOUP_STATUS_OK &&
+           e_xml_find_in_hierarchy (prop_node, E_WEBDAV_NS_DAV, "supported-privilege-set", E_WEBDAV_NS_DAV, 
"supported-privilege", NULL, NULL)) {
                GNode *root;
-               gchar *prefix;
 
-               prefix = g_strconcat (xpath_prop_prefix, "/D:supported-privilege-set", NULL);
                root = g_node_new (NULL);
 
-               e_webdav_session_traverse_privilege_level (xpath_ctx, prefix, root);
+               e_webdav_session_traverse_privilege_level (prop_node, root);
 
                *out_privileges = root;
-
-               g_free (prefix);
        }
 
        return TRUE;
@@ -4290,19 +4234,13 @@ e_webdav_session_get_supported_privilege_set_sync (EWebDAVSession *webdav,
 }
 
 static void
-e_webdav_session_extract_privilege_simple (xmlXPathObjectPtr xpath_obj_privilege,
+e_webdav_session_extract_privilege_simple (xmlNodePtr privilege_node,
                                           GSList **out_privileges)
 {
-       if (xpath_obj_privilege &&
-           xpath_obj_privilege->type == XPATH_NODESET &&
-           xpath_obj_privilege->nodesetval &&
-           xpath_obj_privilege->nodesetval->nodeNr == 1 &&
-           xpath_obj_privilege->nodesetval->nodeTab &&
-           xpath_obj_privilege->nodesetval->nodeTab[0] &&
-           xpath_obj_privilege->nodesetval->nodeTab[0]->children) {
+       if (privilege_node) {
                xmlNodePtr node;
 
-               for (node = xpath_obj_privilege->nodesetval->nodeTab[0]->children; node; node = node->next) {
+               for (node = privilege_node->children; node; node = node->next) {
                        if (node->type == XML_ELEMENT_NODE &&
                            node->name && *(node->name) &&
                            node->ns && node->ns->href && *(node->ns->href)) {
@@ -4325,8 +4263,7 @@ typedef struct _PrivilegeSetData {
 
 static gboolean
 e_webdav_session_current_user_privilege_set_cb (EWebDAVSession *webdav,
-                                               xmlXPathContextPtr xpath_ctx,
-                                               const gchar *xpath_prop_prefix,
+                                               xmlNodePtr prop_node,
                                                const SoupURI *request_uri,
                                                const gchar *href,
                                                guint status_code,
@@ -4334,43 +4271,25 @@ e_webdav_session_current_user_privilege_set_cb (EWebDAVSession *webdav,
 {
        PrivilegeSetData *psd = user_data;
 
-       g_return_val_if_fail (xpath_ctx != NULL, FALSE);
+       g_return_val_if_fail (prop_node != NULL, FALSE);
        g_return_val_if_fail (psd != NULL, FALSE);
 
-       if (!xpath_prop_prefix) {
-               e_xml_xpath_context_register_namespaces (xpath_ctx,
-                       "C", E_WEBDAV_NS_CALDAV,
-                       NULL);
-       } else if (status_code == SOUP_STATUS_OK &&
-                  e_xml_xpath_eval_exists (xpath_ctx, "%s/D:current-user-privilege-set/D:privilege", 
xpath_prop_prefix)) {
-               xmlXPathObjectPtr xpath_obj;
-
-               psd->any_found = TRUE;
-
-               xpath_obj = e_xml_xpath_eval (xpath_ctx, "%s/D:current-user-privilege-set/D:privilege", 
xpath_prop_prefix);
-
-               if (xpath_obj) {
-                       gint ii, length;
-
-                       length = xmlXPathNodeSetGetLength (xpath_obj->nodesetval);
+       if (status_code == SOUP_STATUS_OK) {
+               xmlNodePtr privilege_set_node;
 
-                       for (ii = 0; ii < length; ii++) {
-                               xmlXPathObjectPtr xpath_obj_privilege;
+               privilege_set_node = e_xml_find_child (prop_node, E_WEBDAV_NS_DAV, 
"current-user-privilege-set");
 
-                               xpath_obj_privilege = e_xml_xpath_eval (xpath_ctx, 
"%s/D:current-user-privilege-set/D:privilege[%d]", xpath_prop_prefix, ii + 1);
+               if (privilege_set_node) {
+                       xmlNodePtr privilege_node;
 
-                               if (xpath_obj_privilege) {
-                                       e_webdav_session_extract_privilege_simple (xpath_obj_privilege, 
psd->out_privileges);
+                       psd->any_found = TRUE;
 
-                                       xmlXPathFreeObject (xpath_obj_privilege);
-                               }
+                       for (privilege_node = e_xml_find_child (privilege_set_node, E_WEBDAV_NS_DAV, 
"privilege");
+                            privilege_node;
+                            privilege_node = e_xml_find_next_sibling (privilege_node, E_WEBDAV_NS_DAV, 
"privilege")) {
+                               e_webdav_session_extract_privilege_simple (privilege_node, 
psd->out_privileges);
                        }
-
-                       xmlXPathFreeObject (xpath_obj);
                }
-       } else if (status_code == SOUP_STATUS_OK &&
-                  e_xml_xpath_eval_exists (xpath_ctx, "%s/D:current-user-privilege-set", xpath_prop_prefix)) 
{
-               psd->any_found = TRUE;
        }
 
        return TRUE;
@@ -4436,14 +4355,33 @@ e_webdav_session_get_current_user_privilege_set_sync (EWebDAVSession *webdav,
        return success;
 }
 
+static gboolean
+e_webdav_session_has_one_children (xmlNodePtr parent_node)
+{
+       xmlNodePtr node;
+       gint subelements = 0;
+
+       if (!parent_node)
+               return FALSE;
+
+       for (node = parent_node->children; node && subelements <= 1; node = node->next) {
+               if (node->type == XML_ELEMENT_NODE)
+                       subelements++;
+       }
+
+       return subelements == 1;
+}
+
 static EWebDAVACEPrincipalKind
-e_webdav_session_extract_acl_principal (xmlXPathContextPtr xpath_ctx,
-                                       const gchar *principal_prefix,
+e_webdav_session_extract_acl_principal (xmlNodePtr principal_node,
                                        gchar **out_principal_href,
                                        GSList **out_principal_hrefs)
 {
-       g_return_val_if_fail (xpath_ctx != NULL, E_WEBDAV_ACE_PRINCIPAL_UNKNOWN);
-       g_return_val_if_fail (principal_prefix != NULL, E_WEBDAV_ACE_PRINCIPAL_UNKNOWN);
+       xmlNodePtr node;
+
+       if (!principal_node)
+               return E_WEBDAV_ACE_PRINCIPAL_UNKNOWN;
+
        g_return_val_if_fail (out_principal_href != NULL || out_principal_hrefs != NULL, 
E_WEBDAV_ACE_PRINCIPAL_UNKNOWN);
 
        if (out_principal_href)
@@ -4452,33 +4390,22 @@ e_webdav_session_extract_acl_principal (xmlXPathContextPtr xpath_ctx,
        if (out_principal_hrefs)
                *out_principal_hrefs = NULL;
 
-       if (!e_xml_xpath_eval_exists (xpath_ctx, "%s", principal_prefix))
-               return E_WEBDAV_ACE_PRINCIPAL_UNKNOWN;
-
-       if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:href", principal_prefix)) {
-               if (out_principal_href) {
-                       *out_principal_href = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:href", 
principal_prefix);
-               } else {
-                       xmlXPathObjectPtr xpath_obj;
-
-                       *out_principal_hrefs = NULL;
+       node = e_xml_find_child (principal_node, E_WEBDAV_NS_DAV, "href");
 
-                       xpath_obj = e_xml_xpath_eval (xpath_ctx, "%s/D:href", principal_prefix);
-
-                       if (xpath_obj) {
-                               gint ii, length;
+       if (node) {
+               const xmlChar *href;
 
-                               length = xmlXPathNodeSetGetLength (xpath_obj->nodesetval);
+               href = e_xml_get_node_text (node);
 
-                               for (ii = 0; ii < length; ii++) {
-                                       gchar *href;
+               if (out_principal_href) {
+                       *out_principal_href = (href && *href) ? g_strdup ((const gchar *) href) : NULL;
+               } else {
+                       for (; node; node = e_xml_find_next_sibling (node, E_WEBDAV_NS_DAV, "href")) {
 
-                                       href = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:href[%d]", 
principal_prefix, ii + 1);
-                                       if (href)
-                                               *out_principal_hrefs = g_slist_prepend (*out_principal_hrefs, 
href);
-                               }
+                               href = e_xml_get_node_text (node);
 
-                               xmlXPathFreeObject (xpath_obj);
+                               if (href && *href)
+                                       *out_principal_hrefs = g_slist_prepend (*out_principal_hrefs, 
g_strdup ((const gchar *) href));
                        }
 
                        *out_principal_hrefs = g_slist_reverse (*out_principal_hrefs);
@@ -4487,51 +4414,31 @@ e_webdav_session_extract_acl_principal (xmlXPathContextPtr xpath_ctx,
                return E_WEBDAV_ACE_PRINCIPAL_HREF;
        }
 
-       if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:all", principal_prefix))
+       if (e_xml_find_child (principal_node, E_WEBDAV_NS_DAV, "all"))
                return E_WEBDAV_ACE_PRINCIPAL_ALL;
 
-       if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:authenticated", principal_prefix))
+       if (e_xml_find_child (principal_node, E_WEBDAV_NS_DAV, "authenticated"))
                return E_WEBDAV_ACE_PRINCIPAL_AUTHENTICATED;
 
-       if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:unauthenticated", principal_prefix))
+       if (e_xml_find_child (principal_node, E_WEBDAV_NS_DAV, "unauthenticated"))
                return E_WEBDAV_ACE_PRINCIPAL_UNAUTHENTICATED;
 
-       if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:self", principal_prefix))
+       if (e_xml_find_child (principal_node, E_WEBDAV_NS_DAV, "self"))
                return E_WEBDAV_ACE_PRINCIPAL_SELF;
 
-       if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:property", principal_prefix)) {
+       node = e_xml_find_child (principal_node, E_WEBDAV_NS_DAV, "property");
+
+       if (node) {
                /* No details read about what properties */
                EWebDAVACEPrincipalKind kind = E_WEBDAV_ACE_PRINCIPAL_PROPERTY;
 
                /* Special-case owner */
-               if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:property/D:owner", principal_prefix)) {
-                       xmlXPathObjectPtr xpath_obj_property;
-
-                       xpath_obj_property = e_xml_xpath_eval (xpath_ctx, "%s/D:property", principal_prefix);
-
+               if (e_xml_find_child (node, E_WEBDAV_NS_DAV, "owner")) {
                        /* DAV:owner is the only child and there is only one DAV:property child of the 
DAV:principal */
-                       if (xpath_obj_property &&
-                           xpath_obj_property->type == XPATH_NODESET &&
-                           xmlXPathNodeSetGetLength (xpath_obj_property->nodesetval) == 1 &&
-                           xpath_obj_property->nodesetval &&
-                           xpath_obj_property->nodesetval->nodeNr == 1 &&
-                           xpath_obj_property->nodesetval->nodeTab &&
-                           xpath_obj_property->nodesetval->nodeTab[0] &&
-                           xpath_obj_property->nodesetval->nodeTab[0]->children) {
-                               xmlNodePtr node;
-                               gint subelements = 0;
-
-                               for (node = xpath_obj_property->nodesetval->nodeTab[0]->children; node && 
subelements <= 1; node = node->next) {
-                                       if (node->type == XML_ELEMENT_NODE)
-                                               subelements++;
-                               }
-
-                               if (subelements == 1)
-                                       kind = E_WEBDAV_ACE_PRINCIPAL_OWNER;
+                       if (e_webdav_session_has_one_children (node) &&
+                           e_webdav_session_has_one_children (principal_node)) {
+                               kind = E_WEBDAV_ACE_PRINCIPAL_OWNER;
                        }
-
-                       if (xpath_obj_property)
-                               xmlXPathFreeObject (xpath_obj_property);
                }
 
                return kind;
@@ -4542,116 +4449,88 @@ e_webdav_session_extract_acl_principal (xmlXPathContextPtr xpath_ctx,
 
 static gboolean
 e_webdav_session_acl_cb (EWebDAVSession *webdav,
-                        xmlXPathContextPtr xpath_ctx,
-                        const gchar *xpath_prop_prefix,
+                        xmlNodePtr prop_node,
                         const SoupURI *request_uri,
                         const gchar *href,
                         guint status_code,
                         gpointer user_data)
 {
        GSList **out_entries = user_data;
+       xmlNodePtr acl_node, ace_node;
 
-       g_return_val_if_fail (xpath_ctx != NULL, FALSE);
+       g_return_val_if_fail (prop_node != NULL, FALSE);
        g_return_val_if_fail (out_entries != NULL, FALSE);
 
-       if (!xpath_prop_prefix) {
-       } else if (status_code == SOUP_STATUS_OK &&
-                  e_xml_xpath_eval_exists (xpath_ctx, "%s/D:acl/D:ace", xpath_prop_prefix)) {
-               xmlXPathObjectPtr xpath_obj_ace;
+       if (status_code != SOUP_STATUS_OK)
+               return TRUE;
 
-               xpath_obj_ace = e_xml_xpath_eval (xpath_ctx, "%s/D:acl/D:ace", xpath_prop_prefix);
 
-               if (xpath_obj_ace) {
-                       gint ii, length;
+       acl_node = e_xml_find_child (prop_node, E_WEBDAV_NS_DAV, "acl");
 
-                       length = xmlXPathNodeSetGetLength (xpath_obj_ace->nodesetval);
+       if (acl_node) {
+               for (ace_node = e_xml_find_child (acl_node, E_WEBDAV_NS_DAV, "ace");
+                    ace_node;
+                    ace_node = e_xml_find_next_sibling (ace_node, E_WEBDAV_NS_DAV, "ace")) {
+                       EWebDAVACEPrincipalKind principal_kind = E_WEBDAV_ACE_PRINCIPAL_UNKNOWN;
+                       xmlNodePtr node;
+                       gchar *principal_href = NULL;
+                       guint32 flags = E_WEBDAV_ACE_FLAG_UNKNOWN;
+                       gchar *inherited_href = NULL;
 
-                       for (ii = 0; ii < length; ii++) {
-                               EWebDAVACEPrincipalKind principal_kind = E_WEBDAV_ACE_PRINCIPAL_UNKNOWN;
-                               xmlXPathObjectPtr xpath_obj = NULL;
-                               gchar *principal_href = NULL;
-                               guint32 flags = E_WEBDAV_ACE_FLAG_UNKNOWN;
-                               gchar *inherited_href = NULL;
-                               gchar *privilege_prefix = NULL;
-                               gchar *ace_prefix;
+                       node = e_xml_find_child (ace_node, E_WEBDAV_NS_DAV, "invert");
 
-                               ace_prefix = g_strdup_printf ("%s/D:acl/D:ace[%d]", xpath_prop_prefix, ii + 
1);
+                       if (node) {
+                               flags |= E_WEBDAV_ACE_FLAG_INVERT;
 
-                               if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:invert", ace_prefix)) {
-                                       gchar *prefix;
+                               principal_kind = e_webdav_session_extract_acl_principal (e_xml_find_child 
(node, E_WEBDAV_NS_DAV, "principal"), &principal_href, NULL);
+                       } else {
+                               principal_kind = e_webdav_session_extract_acl_principal (e_xml_find_child 
(ace_node, E_WEBDAV_NS_DAV, "principal"), &principal_href, NULL);
+                       }
 
-                                       flags |= E_WEBDAV_ACE_FLAG_INVERT;
+                       if (principal_kind == E_WEBDAV_ACE_PRINCIPAL_UNKNOWN)
+                               continue;
 
-                                       prefix = g_strdup_printf ("%s/D:invert/D:principal", ace_prefix);
-                                       principal_kind = e_webdav_session_extract_acl_principal (xpath_ctx, 
prefix, &principal_href, NULL);
-                                       g_free (prefix);
-                               } else {
-                                       gchar *prefix;
+                       if (e_xml_find_child (ace_node, E_WEBDAV_NS_DAV, "protected"))
+                               flags |= E_WEBDAV_ACE_FLAG_PROTECTED;
 
-                                       prefix = g_strdup_printf ("%s/D:principal", ace_prefix);
-                                       principal_kind = e_webdav_session_extract_acl_principal (xpath_ctx, 
prefix, &principal_href, NULL);
-                                       g_free (prefix);
-                               }
+                       node = e_xml_find_in_hierarchy (ace_node, E_WEBDAV_NS_DAV, "inherited", 
E_WEBDAV_NS_DAV, "href", NULL, NULL);
 
-                               if (principal_kind == E_WEBDAV_ACE_PRINCIPAL_UNKNOWN) {
-                                       g_free (ace_prefix);
-                                       continue;
-                               }
+                       if (node) {
+                               flags |= E_WEBDAV_ACE_FLAG_INHERITED;
+                               inherited_href = g_strdup ((const gchar *) e_xml_get_node_text (node));
+                       }
 
-                               if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:protected", ace_prefix))
-                                       flags |= E_WEBDAV_ACE_FLAG_PROTECTED;
+                       node = e_xml_find_child (ace_node, E_WEBDAV_NS_DAV, "grant");
 
-                               if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:inherited/D:href", ace_prefix)) 
{
-                                       flags |= E_WEBDAV_ACE_FLAG_INHERITED;
-                                       inherited_href = e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/D:inherited/D:href", ace_prefix);
-                               }
+                       if (node) {
+                               flags |= E_WEBDAV_ACE_FLAG_GRANT;
+                       } else {
+                               node = e_xml_find_child (ace_node, E_WEBDAV_NS_DAV, "deny");
 
-                               if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:grant", ace_prefix)) {
-                                       privilege_prefix = g_strdup_printf ("%s/D:grant/D:privilege", 
ace_prefix);
-                                       flags |= E_WEBDAV_ACE_FLAG_GRANT;
-                               } else if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:deny", ace_prefix)) {
-                                       privilege_prefix = g_strdup_printf ("%s/D:deny/D:privilege", 
ace_prefix);
+                               if (node)
                                        flags |= E_WEBDAV_ACE_FLAG_DENY;
-                               }
-
-                               if (privilege_prefix)
-                                       xpath_obj = e_xml_xpath_eval (xpath_ctx, "%s", privilege_prefix);
-
-                               if (xpath_obj) {
-                                       EWebDAVAccessControlEntry *ace;
-                                       gint ii, length;
-
-                                       ace = e_webdav_access_control_entry_new (principal_kind, 
principal_href, flags, inherited_href);
-                                       if (ace) {
-                                               length = xmlXPathNodeSetGetLength (xpath_obj->nodesetval);
-
-                                               for (ii = 0; ii < length; ii++) {
-                                                       xmlXPathObjectPtr xpath_obj_privilege;
-
-                                                       xpath_obj_privilege = e_xml_xpath_eval (xpath_ctx, 
"%s[%d]", privilege_prefix, ii + 1);
-
-                                                       if (xpath_obj_privilege) {
-                                                               e_webdav_session_extract_privilege_simple 
(xpath_obj_privilege, &ace->privileges);
+                       }
 
-                                                               xmlXPathFreeObject (xpath_obj_privilege);
-                                                       }
-                                               }
+                       if (node) {
+                               EWebDAVAccessControlEntry *ace;
 
-                                               ace->privileges = g_slist_reverse (ace->privileges);
+                               ace = e_webdav_access_control_entry_new (principal_kind, principal_href, 
flags, inherited_href);
 
-                                               *out_entries = g_slist_prepend (*out_entries, ace);
+                               if (ace) {
+                                       for (node = e_xml_find_child (node, E_WEBDAV_NS_DAV, "privilege");
+                                            node;
+                                            node = e_xml_find_next_sibling (node, E_WEBDAV_NS_DAV, 
"privilege")) {
+                                               e_webdav_session_extract_privilege_simple (node, 
&ace->privileges);
                                        }
 
-                                       xmlXPathFreeObject (xpath_obj);
-                               }
+                                       ace->privileges = g_slist_reverse (ace->privileges);
 
-                               g_free (principal_href);
-                               g_free (inherited_href);
-                               g_free (privilege_prefix);
-                               g_free (ace_prefix);
+                                       *out_entries = g_slist_prepend (*out_entries, ace);
+                               }
                        }
 
-                       xmlXPathFreeObject (xpath_obj_ace);
+                       g_free (principal_href);
+                       g_free (inherited_href);
                }
        }
 
@@ -4720,38 +4599,40 @@ typedef struct _ACLRestrictionsData {
 
 static gboolean
 e_webdav_session_acl_restrictions_cb (EWebDAVSession *webdav,
-                                     xmlXPathContextPtr xpath_ctx,
-                                     const gchar *xpath_prop_prefix,
+                                     xmlNodePtr prop_node,
                                      const SoupURI *request_uri,
                                      const gchar *href,
                                      guint status_code,
                                      gpointer user_data)
 {
        ACLRestrictionsData *ard = user_data;
+       xmlNodePtr acl_restrictions_node;
 
-       g_return_val_if_fail (xpath_ctx != NULL, FALSE);
+       g_return_val_if_fail (prop_node != NULL, FALSE);
        g_return_val_if_fail (ard != NULL, FALSE);
 
-       if (!xpath_prop_prefix) {
-       } else if (status_code == SOUP_STATUS_OK &&
-                  e_xml_xpath_eval_exists (xpath_ctx, "%s/D:acl-restrictions", xpath_prop_prefix)) {
-               if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:acl-restrictions/D:grant-only", 
xpath_prop_prefix))
+       if (status_code != SOUP_STATUS_OK)
+               return TRUE;
+
+       acl_restrictions_node = e_xml_find_child (prop_node, E_WEBDAV_NS_DAV, "acl-restrictions");
+
+       if (acl_restrictions_node) {
+               xmlNodePtr required_principal;
+
+               if (e_xml_find_child (acl_restrictions_node, E_WEBDAV_NS_DAV, "grant-only"))
                        *ard->out_restrictions |= E_WEBDAV_ACL_RESTRICTION_GRANT_ONLY;
 
-               if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:acl-restrictions/D:no-invert", 
xpath_prop_prefix))
+               if (e_xml_find_child (acl_restrictions_node, E_WEBDAV_NS_DAV, "no-invert"))
                        *ard->out_restrictions |= E_WEBDAV_ACL_RESTRICTION_NO_INVERT;
 
-               if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:acl-restrictions/D:deny-before-grant", 
xpath_prop_prefix))
+               if (e_xml_find_child (acl_restrictions_node, E_WEBDAV_NS_DAV, "deny-before-grant"))
                        *ard->out_restrictions |= E_WEBDAV_ACL_RESTRICTION_DENY_BEFORE_GRANT;
 
-               if (e_xml_xpath_eval_exists (xpath_ctx, "%s/D:acl-restrictions/D:required-principal", 
xpath_prop_prefix)) {
-                       gchar *prefix;
+               required_principal = e_xml_find_child (acl_restrictions_node, E_WEBDAV_NS_DAV, 
"required-principal");
 
+               if (required_principal) {
                        *ard->out_restrictions |= E_WEBDAV_ACL_RESTRICTION_REQUIRED_PRINCIPAL;
-
-                       prefix = g_strdup_printf ("%s/D:acl-restrictions/D:required-principal", 
xpath_prop_prefix);
-                       *ard->out_principal_kind = e_webdav_session_extract_acl_principal (xpath_ctx, prefix, 
NULL, ard->out_principal_hrefs);
-                       g_free (prefix);
+                       *ard->out_principal_kind = e_webdav_session_extract_acl_principal 
(required_principal, NULL, ard->out_principal_hrefs);
                }
        }
 
@@ -4825,39 +4706,35 @@ e_webdav_session_get_acl_restrictions_sync (EWebDAVSession *webdav,
 
 static gboolean
 e_webdav_session_principal_collection_set_cb (EWebDAVSession *webdav,
-                                             xmlXPathContextPtr xpath_ctx,
-                                             const gchar *xpath_prop_prefix,
+                                             xmlNodePtr prop_node,
                                              const SoupURI *request_uri,
                                              const gchar *href,
                                              guint status_code,
                                              gpointer user_data)
 {
        GSList **out_principal_hrefs = user_data;
+       xmlNodePtr principal_collection_set;
 
-       g_return_val_if_fail (xpath_ctx != NULL, FALSE);
+       g_return_val_if_fail (prop_node != NULL, FALSE);
        g_return_val_if_fail (out_principal_hrefs != NULL, FALSE);
 
-       if (!xpath_prop_prefix) {
-       } else if (status_code == SOUP_STATUS_OK &&
-                  e_xml_xpath_eval_exists (xpath_ctx, "%s/D:principal-collection-set", xpath_prop_prefix)) {
-               xmlXPathObjectPtr xpath_obj;
-
-               xpath_obj = e_xml_xpath_eval (xpath_ctx, "%s/D:principal-collection-set/D:href", 
xpath_prop_prefix);
+       if (status_code != SOUP_STATUS_OK)
+               return TRUE;
 
-               if (xpath_obj) {
-                       gint ii, length;
+       principal_collection_set = e_xml_find_child (prop_node, E_WEBDAV_NS_DAV, "principal-collection-set");
 
-                       length = xmlXPathNodeSetGetLength (xpath_obj->nodesetval);
+       if (principal_collection_set) {
+               xmlNodePtr node;
 
-                       for (ii = 0; ii < length; ii++) {
-                               gchar *got_href;
+               for (node = e_xml_find_child (principal_collection_set, E_WEBDAV_NS_DAV, "href");
+                    node;
+                    node = e_xml_find_next_sibling (node, E_WEBDAV_NS_DAV, "href")) {
+                       const xmlChar *got_href;
 
-                               got_href = e_xml_xpath_eval_as_string (xpath_ctx, 
"%s/D:principal-collection-set/D:href[%d]", xpath_prop_prefix, ii + 1);
-                               if (got_href)
-                                       *out_principal_hrefs = g_slist_prepend (*out_principal_hrefs, 
got_href);
-                       }
+                       got_href = e_xml_get_node_text (node);
 
-                       xmlXPathFreeObject (xpath_obj);
+                       if (got_href && *got_href)
+                               *out_principal_hrefs = g_slist_prepend (*out_principal_hrefs, g_strdup 
((const gchar *) got_href));
                }
        }
 
@@ -5091,40 +4968,38 @@ e_webdav_session_set_acl_sync (EWebDAVSession *webdav,
 
 static gboolean
 e_webdav_session_principal_property_search_cb (EWebDAVSession *webdav,
-                                              xmlXPathContextPtr xpath_ctx,
-                                              const gchar *xpath_prop_prefix,
+                                              xmlNodePtr prop_node,
                                               const SoupURI *request_uri,
                                               const gchar *href,
                                               guint status_code,
                                               gpointer user_data)
 {
        GSList **out_principals = user_data;
+       EWebDAVResource *resource;
+       gchar *display_name;
 
        g_return_val_if_fail (out_principals != NULL, FALSE);
 
-       if (!xpath_prop_prefix) {
-       } else if (status_code == SOUP_STATUS_OK) {
-               EWebDAVResource *resource;
-               gchar *display_name;
-
-               display_name = e_webdav_session_extract_nonempty (xpath_ctx, xpath_prop_prefix, 
"D:displayname", NULL);
-
-               resource = e_webdav_resource_new (
-                       E_WEBDAV_RESOURCE_KIND_PRINCIPAL,
-                       0, /* supports */
-                       href,
-                       NULL, /* etag */
-                       NULL, /* display_name */
-                       NULL, /* content_type */
-                       0, /* content_length */
-                       0, /* creation_date */
-                       0, /* last_modified */
-                       NULL, /* description */
-                       NULL); /* color */
-               resource->display_name = display_name;
+       if (status_code != SOUP_STATUS_OK)
+               return TRUE;
 
-               *out_principals = g_slist_prepend (*out_principals, resource);
-       }
+       display_name = e_webdav_session_extract_nonempty (prop_node, E_WEBDAV_NS_DAV, "displayname", NULL, 
NULL);
+
+       resource = e_webdav_resource_new (
+               E_WEBDAV_RESOURCE_KIND_PRINCIPAL,
+               0, /* supports */
+               href,
+               NULL, /* etag */
+               NULL, /* display_name */
+               NULL, /* content_type */
+               0, /* content_length */
+               0, /* creation_date */
+               0, /* last_modified */
+               NULL, /* description */
+               NULL); /* color */
+       resource->display_name = display_name;
+
+       *out_principals = g_slist_prepend (*out_principals, resource);
 
        return TRUE;
 }
diff --git a/src/libedataserver/e-webdav-session.h b/src/libedataserver/e-webdav-session.h
index 418d30c0c..c45d3c70c 100644
--- a/src/libedataserver/e-webdav-session.h
+++ b/src/libedataserver/e-webdav-session.h
@@ -154,8 +154,7 @@ typedef enum {
 /**
  * EWebDAVPropstatTraverseFunc:
  * @webdav: an #EWebDAVSession
- * @xpath_ctx: an #xmlXPathContextPtr
- * @xpath_prop_prefix: (nullable): an XPath prefix for the current prop element, without trailing forward 
slash
+ * @prop_node: an #xmlNodePtr
  * @request_uri: a #SoupURI, containing the request URI, maybe redirected by the server
  * @href: (nullable): a full URI to which the property belongs, or %NULL, when not found
  * @status_code: an HTTP status code for this property
@@ -165,18 +164,15 @@ typedef enum {
  * e_webdav_session_report_sync() and other XML response with DAV:propstat
  * elements traversal functions.
  *
- * The @xpath_prop_prefix can be %NULL only once, for the first time,
- * which is meant to let the caller setup the @xpath_ctx, like to register
- * its own namespaces to it with e_xml_xpath_context_register_namespaces().
- * All other invocations of the function will have @xpath_prop_prefix non-%NULL.
+ * The @prop_node points to the actual property (prop) node and it can be examined
+ * with e_xml_find_child(), e_xml_find_children_nodes() and other provided XML helper functions.
  *
  * Returns: %TRUE to continue traversal of the returned response, %FALSE otherwise.
  *
  * Since: 3.26
  **/
 typedef gboolean (* EWebDAVPropstatTraverseFunc)       (EWebDAVSession *webdav,
-                                                        xmlXPathContext *xpath_ctx,
-                                                        const gchar *xpath_prop_prefix,
+                                                        xmlNodePtr prop_node,
                                                         const SoupURI *request_uri,
                                                         const gchar *href,
                                                         guint status_code,
diff --git a/src/libedataserver/e-xml-utils.c b/src/libedataserver/e-xml-utils.c
index 4d58142dd..0c50144f9 100644
--- a/src/libedataserver/e-xml-utils.c
+++ b/src/libedataserver/e-xml-utils.c
@@ -470,3 +470,369 @@ e_xml_xpath_eval_exists (xmlXPathContext *xpath_ctx,
 
        return TRUE;
 }
+
+/**
+ * e_xml_is_element_name: (skip)
+ * @node: (nullable): an #xmlNodePtr
+ * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
+ * @name: an element name to search for
+ *
+ * Returns: Whether the @node is an element node of name @name and with a namespace href set to @ns_href
+ *
+ * Since: 3.38
+ **/
+gboolean
+e_xml_is_element_name (xmlNodePtr node,
+                      const gchar *ns_href,
+                      const gchar *name)
+{
+       if (!node || node->type != XML_ELEMENT_NODE)
+               return FALSE;
+
+       if (g_strcmp0 ((const gchar *) node->name, name) == 0) {
+               if (!ns_href) {
+                       if (!node->ns)
+                               return TRUE;
+               } else if (node->ns) {
+                       xmlNsPtr nsPtr = xmlSearchNsByHref (node->doc, node, (const xmlChar *) ns_href);
+
+                       if (nsPtr && node->ns == nsPtr)
+                               return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+/**
+ * e_xml_find_sibling: (skip)
+ * @sibling: (nullable): an #xmlNodePtr, where to start searching
+ * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
+ * @name: an element name to search for
+ *
+ * Searches the sibling nodes of the @sibling for an element named @name in namespace @ns_href.
+ * It checks the @sibling itself too, but it doesn't check the previous siblings of the @sibling.
+ *
+ * Returns: (transfer none) (nullable): an #xmlNodePtr of the given name, or %NULL, if not found
+ *    It also returns %NULL, when the @sibling is %NULL.
+ *
+ * See: e_xml_find_next_sibling(), e_xml_find_child()
+ *
+ * Since: 3.38
+ **/
+xmlNodePtr
+e_xml_find_sibling (xmlNodePtr sibling,
+                   const gchar *ns_href,
+                   const gchar *name)
+{
+       xmlNodePtr node;
+
+       for (node = sibling; node; node = xmlNextElementSibling (node)) {
+               if (e_xml_is_element_name (node, ns_href, name))
+                       break;
+       }
+
+       return node;
+}
+
+/**
+ * e_xml_find_next_sibling: (skip)
+ * @sibling: (nullable): an #xmlNodePtr, where to search from
+ * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
+ * @name: an element name to search for
+ *
+ * Searches for the next sibling node of the @sibling for an element named @name in namespace @ns_href.
+ * Unlike e_xml_find_sibling(), it skips the @sibling itself.
+ *
+ * Returns: (transfer none) (nullable): an #xmlNodePtr of the given name, or %NULL, if not found
+ *    It also returns %NULL, when the @sibling is %NULL.
+ *
+ * See: e_xml_find_sibling(), e_xml_find_child()
+ *
+ * Since: 3.38
+ **/
+xmlNodePtr
+e_xml_find_next_sibling (xmlNodePtr sibling,
+                        const gchar *ns_href,
+                        const gchar *name)
+{
+       if (!sibling)
+               return NULL;
+
+       return e_xml_find_sibling (sibling->next, ns_href, name);
+}
+
+/**
+ * e_xml_find_child: (skip)
+ * @parent: (nullable): an #xmlNodePtr, parent of which immediate children to search
+ * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
+ * @name: an element name to search for
+ *
+ * Searches the children nodes of the @parent for an element named @name in namespace @ns_href.
+ *
+ * Returns: (transfer none) (nullable): an #xmlNodePtr of the given name, or %NULL, if not found.
+ *    It also returns %NULL, when the @parent is %NULL.
+ *
+ * See: e_xml_find_sibling(), e_xml_find_children_nodes()
+ *
+ * Since: 3.38
+ **/
+xmlNodePtr
+e_xml_find_child (xmlNodePtr parent,
+                 const gchar *ns_href,
+                 const gchar *name)
+{
+       if (!parent)
+               return NULL;
+
+       return e_xml_find_sibling (parent->children, ns_href, name);
+}
+
+/**
+ * e_xml_dup_node_content: (skip)
+ * @node: (nullable): an #xmlNodePtr
+ *
+ * Duplicates content of the @node. If the @node is %NULL, then the
+ * function does nothing and returns also %NULL.
+ *
+ * Unlike e_xml_get_node_text(), this includes also any element sub-structure
+ * of the @node, if any such exists.
+ *
+ * Returns: (transfer full) (nullable): the @node content as #xmlChar string,
+ *    or %NULL, when the content could not be read or was not set. Free
+ *    the non-%NULL value with xmlFree(), when no longer needed.
+ *
+ * See: e_xml_find_child_and_dup_content(), e_xml_get_node_text()
+ *
+ * Since: 3.38
+ **/
+xmlChar *
+e_xml_dup_node_content (const xmlNodePtr node)
+{
+       if (!node)
+               return NULL;
+
+       return xmlNodeGetContent (node);
+}
+
+/**
+ * e_xml_find_child_and_dup_content: (skip)
+ * @parent: (nullable): an #xmlNodePtr, parent of which immediate children to search
+ * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
+ * @name: an element name to search for
+ *
+ * Searches the children nodes of the @parent for an element named @name in namespace @ns_href
+ * and returns its content. This combines e_xml_find_child() and e_xml_dup_node_content() calls.
+ *
+ * Returns: (transfer full) (nullable): the found node content as #xmlChar string,
+ *    or %NULL, when the node could not be found or the content could not be read
+ *    or was not set. Free the non-%NULL value with xmlFree(), when no longer needed.
+ *
+ * See: e_xml_find_child_and_get_text()
+ *
+ * Since: 3.38
+ **/
+xmlChar *
+e_xml_find_child_and_dup_content (xmlNodePtr parent,
+                                 const gchar *ns_href,
+                                 const gchar *name)
+{
+       xmlNodePtr tmp;
+
+       tmp = e_xml_find_child (parent, ns_href, name);
+
+       if (!tmp)
+               return NULL;
+
+       return e_xml_dup_node_content (tmp);
+}
+
+/**
+ * e_xml_get_node_text: (skip)
+ * @node: (nullable): an #xmlNodePtr
+ *
+ * Retrieves content of the @node. If the @node is %NULL, then the
+ * function does nothing and returns also %NULL.
+ *
+ * This is similar to e_xml_dup_node_content(), except it does not
+ * allocate new memory for the string. It also doesn't traverse
+ * the element structure, is returns the first text node's value
+ * only. It can be used to avoid unnecessary allocations, when
+ * reading element values with a single text node as a child.
+ *
+ * Returns: (transfer none) (nullable): The @node content, or %NULL.
+ *
+ * See: e_xml_dup_node_content()
+ *
+ * Since: 3.38
+ **/
+const xmlChar *
+e_xml_get_node_text (const xmlNodePtr node)
+{
+       xmlNodePtr child;
+
+       if (!node)
+               return NULL;
+
+       if (node->type == XML_TEXT_NODE)
+               return node->content;
+
+       for (child = node->children; child; child = child->next) {
+               if (child->type == XML_TEXT_NODE)
+                       return child->content;
+       }
+
+       return NULL;
+}
+
+/**
+ * e_xml_find_child_and_get_text: (skip)
+ * @parent: (nullable): an #xmlNodePtr, parent of which immediate children to search
+ * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
+ * @name: an element name to search for
+ *
+ * Searches the children nodes of the @parent for an element named @name in namespace @ns_href
+ * and returns its text content.
+ *
+ * It combines e_xml_find_child() and e_xml_get_node_text() calls.
+ *
+ * Returns: (transfer none) (nullable): the found node text as #xmlChar string,
+ *    or %NULL, when the node could not be found or the content could not be read
+ *    or was not set.
+ *
+ * See: e_xml_find_child_and_dup_content(), e_xml_find_children_nodes()
+ *
+ * Since: 3.38
+ **/
+const xmlChar *
+e_xml_find_child_and_get_text (xmlNodePtr parent,
+                              const gchar *ns_href,
+                              const gchar *name)
+{
+       xmlNodePtr tmp;
+
+       tmp = e_xml_find_child (parent, ns_href, name);
+
+       if (!tmp)
+               return NULL;
+
+       return e_xml_get_node_text (tmp);
+}
+
+/**
+ * e_xml_find_children_nodes: (skip)
+ * @parent: an #xmlNodePtr, whose children to search
+ * @count: how many nodes will be read
+ * @...: triple of arguments describing the nodes and their out variable
+ *
+ * Retrieve multiple nodes in one go, in an efficient way. It can be
+ * quicker than traversing the children of the @parent @count times
+ * in certain circumstances.
+ *
+ * The variable parameters expect triple of:
+ *   const gchar *ns_href;
+ *   const gchar *name;
+ *   xmlNodePtr *out_node;
+ * where the ns_href is a namespace href the node should have set,
+ * or %NULL for none namespace; the name is an element name to search for.
+ * The names should not be included more than once.
+ *
+ * Since: 3.38
+ **/
+void
+e_xml_find_children_nodes (xmlNodePtr parent,
+                          guint count,
+                          ...)
+{
+       struct _data {
+               const gchar *ns_href;
+               const gchar *name;
+               xmlNodePtr *out_node;
+       } *data;
+       va_list args;
+       xmlNodePtr node;
+       guint ii;
+
+       g_return_if_fail (count > 0);
+
+       data = g_alloca (sizeof (struct _data) * count);
+
+       va_start (args, count);
+
+       for (ii = 0; ii < count; ii++) {
+               data[ii].ns_href = va_arg (args, const gchar *);
+               data[ii].name = va_arg (args, const gchar *);
+               data[ii].out_node = va_arg (args, xmlNodePtr *);
+
+               *(data[ii].out_node) = NULL;
+       }
+
+       va_end (args);
+
+       for (node = parent->children; node; node = count ? xmlNextElementSibling (node) : NULL) {
+               for (ii = 0; ii < count; ii++) {
+                       if (e_xml_is_element_name (node, data[ii].ns_href, data[ii].name)) {
+                               *(data[ii].out_node) = node;
+                               count--;
+
+                               if (ii < count)
+                                       data[ii] = data[count];
+
+                               break;
+                       }
+               }
+       }
+}
+
+/**
+ * e_xml_find_in_hierarchy: (skip)
+ * @parent: (nullable): an #xmlNodePtr, or %NULL, in which case function does nothing and just returns %NULL
+ * @child_ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
+ * @child_name: an element name to search for
+ * @...: a two-%NULL-terminated pair of hierarchy children
+ *
+ * Checks whether the @parent has a hierarchy of children described by pair
+ * of 'ns_href' and 'name'.
+ *
+ * Note: It requires two %NULL-s at the end of the arguments, because the `ns_href' can
+ *    be %NULL, thus it could not distinguish between no namespace href and the end of
+ *    the hierarchy children, thus it stops only on the 'name' being %NULL.
+ *
+ * Returns: (transfer none) (nullable): an #xmlNodePtr referencing the node in the hierarchy
+ *    of the children of the @parent, or %NULL, when no such found.
+ *
+ * Since: 3.38
+ **/
+xmlNodePtr
+e_xml_find_in_hierarchy (xmlNodePtr parent,
+                        const gchar *child_ns_href,
+                        const gchar *child_name,
+                        ...)
+{
+       xmlNodePtr node;
+       va_list va;
+
+       if (!parent)
+               return NULL;
+
+       node = e_xml_find_child (parent, child_ns_href, child_name);
+
+       if (!node)
+               return NULL;
+
+       va_start (va, child_name);
+
+       while (node) {
+               child_ns_href = va_arg (va, const gchar *);
+               child_name = va_arg (va, const gchar *);
+
+               if (!child_name)
+                       break;
+
+               node = e_xml_find_child (node, child_ns_href, child_name);
+       }
+
+       va_end (va);
+
+       return child_name ? NULL : node;
+}
diff --git a/src/libedataserver/e-xml-utils.h b/src/libedataserver/e-xml-utils.h
index 41bb70abc..d063ac0c3 100644
--- a/src/libedataserver/e-xml-utils.h
+++ b/src/libedataserver/e-xml-utils.h
@@ -59,6 +59,34 @@ gboolean     e_xml_xpath_eval_exists         (xmlXPathContext *xpath_ctx,
                                                 const gchar *format,
                                                 ...) G_GNUC_PRINTF (2, 3);
 
+gboolean       e_xml_is_element_name           (xmlNodePtr node,
+                                                const gchar *ns_href,
+                                                const gchar *name);
+xmlNodePtr     e_xml_find_sibling              (xmlNodePtr sibling,
+                                                const gchar *ns_href,
+                                                const gchar *name);
+xmlNodePtr     e_xml_find_next_sibling         (xmlNodePtr sibling,
+                                                const gchar *ns_href,
+                                                const gchar *name);
+xmlNodePtr     e_xml_find_child                (xmlNodePtr parent,
+                                                const gchar *ns_href,
+                                                const gchar *name);
+xmlChar *      e_xml_dup_node_content          (const xmlNodePtr node);
+xmlChar *      e_xml_find_child_and_dup_content(xmlNodePtr parent,
+                                                const gchar *ns_href,
+                                                const gchar *name);
+const xmlChar *        e_xml_get_node_text             (const xmlNodePtr node);
+const xmlChar *        e_xml_find_child_and_get_text   (xmlNodePtr parent,
+                                                const gchar *ns_href,
+                                                const gchar *name);
+void           e_xml_find_children_nodes       (xmlNodePtr parent,
+                                                guint count,
+                                                ...);
+xmlNodePtr     e_xml_find_in_hierarchy         (xmlNodePtr parent,
+                                                const gchar *child_ns_href,
+                                                const gchar *child_name,
+                                                ...) G_GNUC_NULL_TERMINATED; /* requires two NULL-s at the 
end of the arguments */
+
 G_END_DECLS
 
 #endif /* E_XML_UTILS_H */


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