From bb6fba636f870c4a820abc6614582e045d6e9195 Mon Sep 17 00:00:00 2001 From: Jonathan Matthew Date: Tue, 23 Mar 2021 23:02:36 +1000 Subject: [PATCH] lib: add rb_uri_is_descendant This checks if one canonical URI is a descendant of another, that is, if removing some number of path components from the first URI would result in the second. --- lib/rb-file-helpers.c | 42 +++++++++++++++++++++++++++++++++++++++ lib/rb-file-helpers.h | 1 + tests/test-file-helpers.c | 17 ++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/lib/rb-file-helpers.c b/lib/rb-file-helpers.c index bc8e49ddf..4e5b2a53b 100644 --- a/lib/rb-file-helpers.c +++ b/lib/rb-file-helpers.c @@ -1483,6 +1483,48 @@ rb_file_find_extant_parent (GFile *file) return file; } +/** + * rb_uri_is_descendant: + * @uri: URI to check + * @ancestor: a URI to check against + * + * Checks if @uri refers to a path beneath @ancestor, such that removing some number + * of path segments of @uri would result in @ancestor. + * It doesn't do any filesystem operations, it just looks at the URIs as strings. + * The URI strings should be built by looking at a filesystem rather than user input, + * and must not have path segments that are empty (multiple slashes) or '.' or '..'. + * + * Given this input, checking if one URI is a descendant of another is pretty simple. + * A descendant URI must have the ancestor as a prefix, and if the ancestor ends with + * a slash, there must be at least one character after that, otherwise the following + * character must be a slash with at least one character after it. + * + * Return value: %TRUE if @uri is a descendant of @ancestor + */ +gboolean +rb_uri_is_descendant (const char *uri, const char *ancestor) +{ + int len; + + if (g_str_has_prefix (uri, ancestor) == FALSE) + return FALSE; + + len = strlen(ancestor); + if (ancestor[len - 1] == '/') { + /* + * following character in uri must be a new path segment, not + * the end of the uri. not considering multiple slashes here. + */ + return (uri[len] != '\0'); + } else { + /* + * following character in uri must be a separator, with something after it. + * not considering multiple slashes here. + */ + return ((uri[len] == '/') && strlen(uri) > (len + 1)); + } +} + /** * rb_uri_get_filesystem_type: * @uri: URI to get filesystem type for diff --git a/lib/rb-file-helpers.h b/lib/rb-file-helpers.h index 606eeb31e..8196f63fb 100644 --- a/lib/rb-file-helpers.h +++ b/lib/rb-file-helpers.h @@ -60,6 +60,7 @@ gboolean rb_uri_is_writable (const char *uri); gboolean rb_uri_is_local (const char *uri); gboolean rb_uri_is_hidden (const char *uri); gboolean rb_uri_could_be_podcast (const char *uri, gboolean *is_opml); +gboolean rb_uri_is_descendant (const char *uri, const char *ancestor); char * rb_uri_make_hidden (const char *uri); char * rb_uri_get_dir_name (const char *uri); char * rb_uri_get_short_path_name (const char *uri); diff --git a/tests/test-file-helpers.c b/tests/test-file-helpers.c index 855b15fc0..f20d85e99 100644 --- a/tests/test-file-helpers.c +++ b/tests/test-file-helpers.c @@ -100,6 +100,22 @@ START_TEST (test_rb_check_dir_has_space) } END_TEST +START_TEST (test_rb_uri_is_descendant) +{ + ck_assert (rb_uri_is_descendant ("file:///tmp", "file:///")); + ck_assert (rb_uri_is_descendant ("file:///tmp/2", "file:///")); + ck_assert (rb_uri_is_descendant ("file:///tmp/2", "file:///tmp")); + ck_assert (rb_uri_is_descendant ("file:///tmp/2", "file:///tmp/")); + ck_assert (rb_uri_is_descendant ("file:///tmp/", "file:///tmp") == FALSE); + ck_assert (rb_uri_is_descendant ("file:///tmp", "file:///tmp/") == FALSE); + ck_assert (rb_uri_is_descendant ("file:///tmp/", "file:///tmp/") == FALSE); + ck_assert (rb_uri_is_descendant ("file:///tmp", "file:///tmp") == FALSE); + ck_assert (rb_uri_is_descendant ("file:///tmp2", "file:///tmp") == FALSE); + ck_assert (rb_uri_is_descendant ("file:///tmp/2", "file:///tmp2") == FALSE); + ck_assert (rb_uri_is_descendant ("file:///tmp/22", "file:///tmp/2") == FALSE); +} +END_TEST + static Suite * rb_file_helpers_suite () { @@ -110,6 +126,7 @@ rb_file_helpers_suite () tcase_add_test (tc_chain, test_rb_uri_get_short_path_name); tcase_add_test (tc_chain, test_rb_check_dir_has_space); + tcase_add_test (tc_chain, test_rb_uri_is_descendant); return s; }