From bb6fba636f870c4a820abc6614582e045d6e9195 Mon Sep 17 00:00:00 2001 From: Jonathan Matthew Date: Tue, 23 Mar 2021 23:02:36 +1000 Subject: [PATCH 1/2] 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; } From 8b7309cdef0091f67bea3678544aa5d58d7f2e4d Mon Sep 17 00:00:00 2001 From: Jonathan Matthew Date: Tue, 23 Mar 2021 23:04:25 +1000 Subject: [PATCH 2/2] use rb_uri_is_descendant in a few places This is better than the string prefix checks we had before as it ensures that the end of the ancestor URI is also the end of a path component of the descendant. --- plugins/generic-player/rb-generic-player-source.c | 4 ++-- rhythmdb/rhythmdb-monitor.c | 2 +- widgets/rb-import-dialog.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/generic-player/rb-generic-player-source.c b/plugins/generic-player/rb-generic-player-source.c index 03e59916c..b8b0ff68e 100644 --- a/plugins/generic-player/rb-generic-player-source.c +++ b/plugins/generic-player/rb-generic-player-source.c @@ -683,7 +683,7 @@ default_uri_from_playlist_uri (RBGenericPlayerSource *source, const char *uri) char *full_uri; mount_uri = rb_generic_player_source_get_mount_path (source); - if (g_str_has_prefix (uri, mount_uri)) { + if (rb_uri_is_descendant (uri, mount_uri)) { return g_strdup (uri); } @@ -704,7 +704,7 @@ default_uri_to_playlist_uri (RBGenericPlayerSource *source, const char *uri, Tot case TOTEM_PL_PARSER_IRIVER_PLA: /* we need absolute paths within the device filesystem for this format */ mount_uri = rb_generic_player_source_get_mount_path (source); - if (g_str_has_prefix (uri, mount_uri) == FALSE) { + if (rb_uri_is_descendant (uri, mount_uri) == FALSE) { rb_debug ("uri %s is not under device mount uri %s", uri, mount_uri); return NULL; } diff --git a/rhythmdb/rhythmdb-monitor.c b/rhythmdb/rhythmdb-monitor.c index a59600288..dbb0f1bfe 100644 --- a/rhythmdb/rhythmdb-monitor.c +++ b/rhythmdb/rhythmdb-monitor.c @@ -280,7 +280,7 @@ rhythmdb_directory_change_cb (GFileMonitor *monitor, /* ignore new files outside of the library locations */ for (i = 0; db->priv->library_locations[i] != NULL; i++) { - if (g_str_has_prefix (canon_uri, db->priv->library_locations[i])) { + if (rb_uri_is_descendant (canon_uri, db->priv->library_locations[i])) { in_library = TRUE; break; } diff --git a/widgets/rb-import-dialog.c b/widgets/rb-import-dialog.c index 86c36823f..dfa7b2995 100644 --- a/widgets/rb-import-dialog.c +++ b/widgets/rb-import-dialog.c @@ -431,7 +431,7 @@ current_folder_changed_cb (GtkFileChooser *chooser, RBImportDialog *dialog) locations = g_settings_get_strv (settings, "locations"); gtk_widget_set_sensitive (dialog->priv->copy_check, TRUE); for (i = 0; locations[i] != NULL; i++) { - if (g_str_has_prefix (uri, locations[i])) { + if ((g_strcmp0 (uri, locations[i]) == 0) || rb_uri_is_descendant (uri, locations[i])) { gtk_widget_set_sensitive (dialog->priv->copy_check, FALSE); break; }