From 90fadea23ab7712259762ea2874a794aca482e03 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Wed, 11 Nov 2020 13:54:10 +0100 Subject: I#260 - DAV: Relax collection's item href comparison Closes https://gitlab.gnome.org/GNOME/evolution-data-server/-/issues/260 --- .../backends/carddav/e-book-backend-carddav.c | 7 +- .../backends/caldav/e-cal-backend-caldav.c | 7 +- src/libedataserver/e-webdav-session.c | 81 ++++++++++++++++++++++ src/libedataserver/e-webdav-session.h | 2 + tests/libedataserver/CMakeLists.txt | 1 + tests/libedataserver/libedataserver-test.c | 81 ++++++++++++++++++++++ 6 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 tests/libedataserver/libedataserver-test.c diff --git a/src/addressbook/backends/carddav/e-book-backend-carddav.c b/src/addressbook/backends/carddav/e-book-backend-carddav.c index e8867f881..393276246 100644 --- a/src/addressbook/backends/carddav/e-book-backend-carddav.c +++ b/src/addressbook/backends/carddav/e-book-backend-carddav.c @@ -484,7 +484,7 @@ ebb_carddav_multiget_response_cb (EWebDAVSession *webdav, if (!nfo) continue; - if (g_strcmp0 (nfo->extra, href) == 0) { + if (e_webdav_session_util_item_href_equal (nfo->extra, href)) { /* If the server returns data in the same order as it had been requested, then this speeds up lookup for the matching object. */ if (link == *from_link) @@ -496,6 +496,9 @@ ebb_carddav_multiget_response_cb (EWebDAVSession *webdav, } } + if (!link && e_soup_session_get_log_level (E_SOUP_SESSION (webdav)) != SOUP_LOGGER_LOG_NONE) + e_util_debug_print ("CardDAV", "Failed to find item with href '%s' in known server items\n", href); + g_free (dequoted_etag); } @@ -513,7 +516,7 @@ ebb_carddav_multiget_response_cb (EWebDAVSession *webdav, if (!nfo) continue; - if (g_strcmp0 (nfo->extra, href) == 0) { + if (e_webdav_session_util_item_href_equal (nfo->extra, href)) { /* If the server returns data in the same order as it had been requested, then this speeds up lookup for the matching object. */ if (link == *from_link) diff --git a/src/calendar/backends/caldav/e-cal-backend-caldav.c b/src/calendar/backends/caldav/e-cal-backend-caldav.c index 11af2d87d..e50bc0b57 100644 --- a/src/calendar/backends/caldav/e-cal-backend-caldav.c +++ b/src/calendar/backends/caldav/e-cal-backend-caldav.c @@ -460,7 +460,7 @@ ecb_caldav_multiget_response_cb (EWebDAVSession *webdav, if (!nfo) continue; - if (g_strcmp0 (nfo->extra, href) == 0) { + if (e_webdav_session_util_item_href_equal (nfo->extra, href)) { /* If the server returns data in the same order as it had been requested, then this speeds up lookup for the matching object. */ if (link == md->from_link) @@ -472,6 +472,9 @@ ecb_caldav_multiget_response_cb (EWebDAVSession *webdav, } } + if (!link && e_soup_session_get_log_level (E_SOUP_SESSION (webdav)) != SOUP_LOGGER_LOG_NONE) + e_util_debug_print ("CalDAV", "Failed to find item with href '%s' in known server items\n", href); + g_free (dequoted_etag); } @@ -489,7 +492,7 @@ ecb_caldav_multiget_response_cb (EWebDAVSession *webdav, if (!nfo) continue; - if (g_strcmp0 (nfo->extra, href) == 0) { + if (e_webdav_session_util_item_href_equal (nfo->extra, href)) { /* If the server returns data in the same order as it had been requested, then this speeds up lookup for the matching object. */ if (link == md->from_link) diff --git a/src/libedataserver/e-webdav-session.c b/src/libedataserver/e-webdav-session.c index 8d970a1cd..ca95ec437 100644 --- a/src/libedataserver/e-webdav-session.c +++ b/src/libedataserver/e-webdav-session.c @@ -29,6 +29,7 @@ #include "evolution-data-server-config.h" #include +#include #include #include "camel/camel.h" @@ -5137,3 +5138,83 @@ e_webdav_session_util_free_privileges (GNode *privileges) g_node_traverse (privileges, G_PRE_ORDER, G_TRAVERSE_ALL, -1, e_webdav_session_free_in_traverse_cb, NULL); g_node_destroy (privileges); } + +/** + * e_webdav_session_util_item_href_equal: + * @href1: the first href + * @href2: the second href + * + * Compares two hrefs and return whether they reference + * the same item on the server. The comparison is done in + * a relaxed way, not considering scheme part and comparing + * the host name case insensitively, while the path + * case sensitively. It also ignores the username/password + * information in the hostname part, if it's included. + * The function doesn't decode any URI-encoded characters. + * + * Returns: whether the two href-s reference the same item + * + * Since: 3.40 + **/ +gboolean +e_webdav_session_util_item_href_equal (const gchar *href1, + const gchar *href2) +{ + const gchar *ptr, *from1, *from2, *next1, *next2; + + if (!href1 || !href2) + return href1 == href2; + + if (g_strcmp0 (href1, href2) == 0) + return TRUE; + + /* skip the scheme part */ + ptr = strstr (href1, "://"); + if (ptr) + href1 = ptr + 3; + + ptr = strstr (href2, "://"); + if (ptr) + href2 = ptr + 3; + + for (from1 = href1, from2 = href2; from1 && from2; from1 = next1, from2 = next2) { + gint len; + + ptr = strchr (from1, '/'); + if (ptr) + ptr++; + next1 = ptr; + + ptr = strchr (from2, '/'); + if (ptr) + ptr++; + next2 = ptr; + + if ((!next1 && next2) || (next1 && !next2)) + break; + + len = next1 ? next1 - from1 : strlen (from1); + + if (!len) + len = next2 ? next2 - from2 : strlen (from2); + + /* it's the hostname part */ + if (from1 == href1) { + /* ignore the username/password part */ + ptr = strchr (from1, '@'); + if (ptr) + from1 = ptr + 1; + + ptr = strchr (from2, '@'); + if (ptr) + from2 = ptr + 1; + + if (g_ascii_strncasecmp (from1, from2, len) != 0) + return FALSE; + } else if (strncmp (from1, from2, len) != 0) { + return FALSE; + } + } + + return !from1 && !from2; +} diff --git a/src/libedataserver/e-webdav-session.h b/src/libedataserver/e-webdav-session.h index aba424f3e..a9f6f5b0a 100644 --- a/src/libedataserver/e-webdav-session.h +++ b/src/libedataserver/e-webdav-session.h @@ -591,6 +591,8 @@ gboolean e_webdav_session_principal_property_search_sync GError **error); gchar * e_webdav_session_util_maybe_dequote (gchar *text); void e_webdav_session_util_free_privileges (GNode *privileges); /* EWebDAVPrivilege * */ +gboolean e_webdav_session_util_item_href_equal (const gchar *href1, + const gchar *href2); G_END_DECLS diff --git a/tests/libedataserver/CMakeLists.txt b/tests/libedataserver/CMakeLists.txt index 13833069a..519745069 100644 --- a/tests/libedataserver/CMakeLists.txt +++ b/tests/libedataserver/CMakeLists.txt @@ -19,6 +19,7 @@ set(extra_ldflags # Should be kept ordered approximately from least to most difficult/complex set(TESTS e-source-registry-test + libedataserver-test ) foreach(_test ${TESTS}) diff --git a/tests/libedataserver/libedataserver-test.c b/tests/libedataserver/libedataserver-test.c new file mode 100644 index 000000000..8ff11b999 --- /dev/null +++ b/tests/libedataserver/libedataserver-test.c @@ -0,0 +1,81 @@ +/* + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "e-test-server-utils.h" + +#include + +static ETestServerClosure test_closure = { E_TEST_SERVER_NONE, NULL, 0, FALSE, NULL }; + +static void +test_webdav_href_compare (ETestServerFixture *fixture, + gconstpointer user_data) +{ + struct _hrefs { + const gchar *href1; + const gchar *href2; + gboolean same; + } hrefs[] = { + { "http://www.gnome.org/", "http://www.gnome.org/", TRUE }, + { "https://www.gnome.org/", "http://www.gnome.org/", TRUE }, + { "http://user@www.gnome.org/", "https://www.gnome.org/", TRUE }, + { "http://www.gnome.org/index", "http://www.gnome.org/", FALSE }, + { "http://www.GNOME.org/index", "http://www.gnome.org/index", TRUE }, + { "http://www.GNOME.org/Index", "http://www.gnome.org/index", FALSE }, + { "http://www.gnome.org/index/", "http://www.gnome.org/index", FALSE }, + { "http://www.gnome.org/path/collection/data.ext", "http://www.gnome.org/path/collection/data.ext", TRUE }, + { "https://www.gnome.org/path/collection/data.ext", "http://www.gnome.org/path/collection/data.ext", TRUE }, + { "http://user@www.gnome.org/path/collection/data.ext", "http://www.gnome.org/path/collection/data.ext", TRUE }, + { "http://www.gnome.org/Path/collection/data.ext", "http://www.gnome.org/path/collection/data.ext", FALSE }, + { "http://www.gnome.org/path/Collection/data.ext", "http://www.gnome.org/path/collection/data.ext", FALSE }, + { "http://www.gnome.org/path/collection/Data.ext", "http://www.gnome.org/path/collection/data.ext", FALSE }, + { "http://www.GNOME.org/path/collection/data.ext", "http://www.gnome.org/path/collection/data.ext", TRUE }, + { "http://www.gnome.org/path/collection/data.ext", "http://www.server.org/path/collection/data.ext", FALSE }, + { "https://www.gnome.org", "https://www.gnome.org/path/collection/data.ext", FALSE }, + { "https://www.gnome.org/", "https://www.gnome.org/path/collection/data.ext", FALSE }, + { "https://www.gnome.org/path", "https://www.gnome.org/path/collection/data.ext", FALSE }, + { "https://www.gnome.org/path/", "https://www.gnome.org/path/collection/data.ext", FALSE }, + { "https://www.gnome.org/path/collection", "https://www.gnome.org/path/collection/data.ext", FALSE }, + { "https://www.gnome.org/path/collection/", "https://www.gnome.org/path/collection/data.ext", FALSE } + }; + gint ii; + + for (ii = 0; ii < G_N_ELEMENTS (hrefs); ii++) { + if (hrefs[ii].same) { + g_assert_cmpint (ii, ==, e_webdav_session_util_item_href_equal (hrefs[ii].href1, hrefs[ii].href2) ? ii : -1); + g_assert_cmpint (ii, ==, e_webdav_session_util_item_href_equal (hrefs[ii].href2, hrefs[ii].href1) ? ii : -1); + } else { + g_assert_cmpint (ii, !=, e_webdav_session_util_item_href_equal (hrefs[ii].href1, hrefs[ii].href2) ? ii : -1); + g_assert_cmpint (ii, !=, e_webdav_session_util_item_href_equal (hrefs[ii].href2, hrefs[ii].href1) ? ii : -1); + } + } +} + +gint +main (gint argc, + gchar **argv) +{ + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("https://gitlab.gnome.org/GNOME/evolution-data-server/issues"); + + g_test_add ( + "/libedataserver-test/WebDAVhrefCompare", + ETestServerFixture, &test_closure, + e_test_server_utils_setup, + test_webdav_href_compare, + e_test_server_utils_teardown); + + return e_test_server_utils_run (); +} -- cgit v1.2.1