From 2b14a3d092eb52cf0b42c6c26ffa283349a3730b Mon Sep 17 00:00:00 2001 From: Jan Grulich Date: Mon, 20 May 2024 10:40:54 +0200 Subject: [PATCH] document-portal: Implement GetHostPaths This method allows apps to get path as exists on the host filesystem for documents exported through the document portal. This method takes a list of document IDs as a string array and returns a dictionary mapping document IDs to the paths in the host filesystem. It is expected an app making this request to have access to given list of documents. This is based on initial work made by @JakobDev Fixes #475 --- data/org.freedesktop.portal.Documents.xml | 16 +++++ document-portal/document-portal.c | 87 ++++++++++++++++++++++- tests/test-doc-portal.c | 57 ++++++++++++++- 3 files changed, 158 insertions(+), 2 deletions(-) diff --git a/data/org.freedesktop.portal.Documents.xml b/data/org.freedesktop.portal.Documents.xml index d5a76372a..09009b238 100644 --- a/data/org.freedesktop.portal.Documents.xml +++ b/data/org.freedesktop.portal.Documents.xml @@ -273,5 +273,21 @@ + + + + + + diff --git a/document-portal/document-portal.c b/document-portal/document-portal.c index ccbb514bc..d393bdcd5 100644 --- a/document-portal/document-portal.c +++ b/document-portal/document-portal.c @@ -1416,6 +1416,90 @@ portal_list (GDBusMethodInvocation *invocation, return TRUE; } +const char * +get_host_path_internal (GDBusMethodInvocation *invocation, + XdpAppInfo *app_info, + const char *id) +{ + g_autoptr(PermissionDbEntry) entry = NULL; + + XDP_AUTOLOCK (db); + + entry = permission_db_lookup (db, id); + + if (!entry) + { + g_dbus_method_invocation_return_error (invocation, + XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, + g_strdup_printf("Invalid ID passed (%s)", id)); + return NULL; + } + + if (!xdp_app_info_is_host (app_info)) + { + g_autofree const char **apps = NULL; + const char *app_id = NULL; + gboolean app_found = FALSE; + int i; + + app_id = xdp_app_info_get_id(app_info); + + apps = permission_db_entry_list_apps (entry); + for (i = 0; apps[i] != NULL; i++) + { + if (g_strcmp0 (app_id, apps[i]) == 0) + { + app_found = TRUE; + break; + } + } + + if (!app_found) + { + g_dbus_method_invocation_return_error (invocation, + XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_NOT_ALLOWED, + "Not enough permissions"); + return NULL; + } + } + + g_autoptr (GVariant) data = permission_db_entry_get_data (entry); + const char *path; + + g_variant_get (data, "(^&ayttu)", &path, NULL, NULL, NULL); + + return path; +} + +static gboolean +portal_get_host_paths (GDBusMethodInvocation *invocation, + GVariant *parameters, + XdpAppInfo *app_info) +{ + g_autofree const char **id_list = NULL; + const char *path; + + g_variant_get (parameters, "(^a&s)", &id_list); + + GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{say}")); + + for (size_t i = 0; id_list[i] != NULL; i++) + { + path = get_host_path_internal (invocation, app_info, id_list[i]); + if (path == NULL) + { + g_variant_builder_clear (&builder); + return FALSE; + } + + g_variant_builder_add (&builder, "{s@ay}", id_list[i], g_variant_new_bytestring (path)); + } + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@a{say})", g_variant_builder_end (&builder)));; + + return TRUE; +} + static void peer_died_cb (const char *name) { @@ -1432,7 +1516,7 @@ on_bus_acquired (GDBusConnection *connection, dbus_api = xdp_dbus_documents_skeleton_new (); - xdp_dbus_documents_set_version (XDP_DBUS_DOCUMENTS (dbus_api), 4); + xdp_dbus_documents_set_version (XDP_DBUS_DOCUMENTS (dbus_api), 5); g_signal_connect_swapped (dbus_api, "handle-get-mount-point", G_CALLBACK (handle_get_mount_point), NULL); g_signal_connect_swapped (dbus_api, "handle-add", G_CALLBACK (handle_method), portal_add); @@ -1445,6 +1529,7 @@ on_bus_acquired (GDBusConnection *connection, g_signal_connect_swapped (dbus_api, "handle-lookup", G_CALLBACK (handle_method), portal_lookup); g_signal_connect_swapped (dbus_api, "handle-info", G_CALLBACK (handle_method), portal_info); g_signal_connect_swapped (dbus_api, "handle-list", G_CALLBACK (handle_method), portal_list); + g_signal_connect_swapped (dbus_api, "handle-get-host-paths", G_CALLBACK (handle_method), portal_get_host_paths); file_transfer = file_transfer_create (); g_dbus_interface_skeleton_set_flags (file_transfer, diff --git a/tests/test-doc-portal.c b/tests/test-doc-portal.c index 8298943a2..ad3114bc5 100644 --- a/tests/test-doc-portal.c +++ b/tests/test-doc-portal.c @@ -687,6 +687,60 @@ test_add_named (void) assert_doc_not_exist (id1, basename1, "com.test.App2"); } +static void +test_get_host_paths (void) +{ + g_autofree char *doc_id = NULL; + g_autofree char *expected_real_path = NULL; + g_autofree char *real_path = NULL; + const char *basename = "host-path"; + g_autoptr(GVariant) path= NULL; + g_autoptr(GVariant) reply = NULL; + g_autoptr(GVariant) result = NULL; + g_autoptr(GVariantIter) iter = NULL; + GVariant *args = NULL; + GError *error = NULL; + const gchar* key = NULL; + + if (!check_fuse_or_skip_test ()) + return; + + doc_id = export_new_file (basename, "content", FALSE); + + GVariantBuilder builder; + g_variant_builder_init (&builder, G_VARIANT_TYPE ("(as)")); + GVariantBuilder arrayBuilder; + g_variant_builder_init (&arrayBuilder, G_VARIANT_TYPE ("as")); + g_variant_builder_add (&arrayBuilder, "s", doc_id); + g_variant_builder_add_value (&builder, g_variant_builder_end (&arrayBuilder)); + + args = g_variant_builder_end (&builder); + + reply = g_dbus_connection_call_sync (session_bus, + "org.freedesktop.portal.Documents", + "/org/freedesktop/portal/documents", + "org.freedesktop.portal.Documents", + "GetHostPaths", args, + G_VARIANT_TYPE ("(a{say})"), + 0, -1, + NULL, + &error); + + g_assert_no_error (error); + result = g_variant_get_child_value (reply, 0); + + g_assert (g_variant_is_of_type (result, G_VARIANT_TYPE ("a{say}"))); + + expected_real_path = g_build_filename (outdir, basename, NULL); + iter = g_variant_iter_new (result); + while (g_variant_iter_loop (iter, "{&s@ay}", &key, &path)) { + if (g_strcmp0 (key, doc_id) == 0) { + g_assert_cmpstr (g_variant_get_bytestring (path), ==, expected_real_path); + return; + } + } +} + static void global_setup (void) { @@ -853,7 +907,7 @@ test_version (void) if (!check_fuse_or_skip_test ()) return; - g_assert_cmpint (xdp_dbus_documents_get_version (documents), ==, 4); + g_assert_cmpint (xdp_dbus_documents_get_version (documents), ==, 5); } int @@ -871,6 +925,7 @@ main (int argc, char **argv) g_test_add_func ("/db/recursive_doc", test_recursive_doc); g_test_add_func ("/db/create_docs", test_create_docs); g_test_add_func ("/db/add_named", test_add_named); + g_test_add_func ("/db/get_host_paths", test_get_host_paths); global_setup ();