From bc35a1496d5ceda035aca2ac7a85ddcbd80da885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Sat, 20 Sep 2025 05:55:53 +0200 Subject: [PATCH] gio/gdesktopappinfo: Also use document portal for desktop file snaps When a snap is launched from its non-dbus-activable desktop file we may still need to use the portal to pass the URIs to the program, so that it can access to them even if confined. In fact most snaps are not dbus-activable, but they rely on the classic desktop file activation. --- gio/gdesktopappinfo.c | 26 ++++++++ gio/tests/desktop-app-info.c | 74 ++++++++++++++++++++++ gio/tests/meson.build | 17 +++-- gio/tests/snap-app_appinfo-test.desktop.in | 12 ++++ gio/tests/snapinfo-test.c | 51 +++++++++++++++ 5 files changed, 173 insertions(+), 7 deletions(-) create mode 100644 gio/tests/snap-app_appinfo-test.desktop.in create mode 100644 gio/tests/snapinfo-test.c diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c index 73c2a979b..da408be41 100644 --- a/gio/gdesktopappinfo.c +++ b/gio/gdesktopappinfo.c @@ -2951,6 +2951,7 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info, gboolean completed = FALSE; GList *old_uris; GList *dup_uris; + GList *ruris = NULL; char **argv, **envp; int argc; @@ -2964,6 +2965,30 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info, else envp = g_get_environ (); +#ifdef G_OS_UNIX + if (uris && info->keyfile) + { + char *snap_instance; + char *app_id = NULL; + + snap_instance = g_desktop_app_info_get_string (info, "X-SnapInstanceName"); + + if (snap_instance && *snap_instance) + app_id = g_strconcat ("snap.", snap_instance, NULL); + + g_free (snap_instance); + + if (app_id) + { + ruris = g_document_portal_add_documents (uris, app_id, NULL); + if (ruris != NULL) + uris = ruris; + } + + g_clear_pointer (&app_id, g_free); + } +#endif + /* The GList* passed to expand_application_parameters() will be modified * internally by expand_macro(), so we need to pass a copy of it instead, * and also use that copy to control the exit condition of the loop below. @@ -3153,6 +3178,7 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info, out: g_strfreev (argv); g_strfreev (envp); + g_list_free_full (ruris, g_free); return completed; } diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c index 6a2577739..f880a3dc8 100644 --- a/gio/tests/desktop-app-info.c +++ b/gio/tests/desktop-app-info.c @@ -33,6 +33,9 @@ #include #include +#include "fake-document-portal.h" +#include "gdbus-sessionbus.h" + G_DECLARE_FINAL_TYPE (TestLaunchContext, test_launch_context, TEST, LAUNCH_CONTEXT, GAppLaunchContext); @@ -1432,6 +1435,76 @@ test_default_uri_handler_async (void) g_free (file_path); } +static void +launch_snap_uris_with_portal (void) +{ + GDesktopAppInfo *appinfo; + GError *error = NULL; + gboolean retval; + const gchar *path; + const gchar *path2; + gboolean invoked = FALSE; + gboolean launched = FALSE; + gboolean failed = FALSE; + gboolean child_waited = FALSE; + GAppLaunchContext *context; + GList *uris = NULL; + GFakeDocumentPortalThread *thread = NULL; + + /* Run a fake-document-portal */ + session_bus_up (); + thread = g_fake_document_portal_thread_new (session_bus_get_address (), + "snap.snap-app"); + g_fake_document_portal_thread_run (thread); + + path = g_test_get_filename (G_TEST_BUILT, "snap-app_appinfo-test.desktop", NULL); + appinfo = g_desktop_app_info_new_from_filename (path); + g_assert_true (G_IS_APP_INFO (appinfo)); + + path2 = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); + g_assert_true (g_file_test (path2, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)); + + context = g_object_new (test_launch_context_get_type (), NULL); + g_signal_connect (context, "launch-started", + G_CALLBACK (on_launch_started), + &invoked); + g_signal_connect (context, "launched", + G_CALLBACK (on_launched), + &launched); + g_signal_connect (context, "launch-failed", + G_CALLBACK (on_launch_failed), + &failed); + g_app_launch_context_setenv (context, "DOCUMENT_PORTAL_MOUNT_POINT", + g_fake_document_portal_thread_get_mount_point (thread)); + + uris = g_list_append (uris, g_strconcat ("file://", path, NULL)); + uris = g_list_append (uris, g_strconcat ("file://", path2, NULL)); + + retval = g_desktop_app_info_launch_uris_as_manager (appinfo, uris, + context, + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, + NULL, + wait_child_completed, + &child_waited, + &error); + + g_assert_no_error (error); + g_assert_true (retval); + g_assert_true (invoked); + g_assert_true (launched); + g_assert_true (child_waited); + g_assert_false (failed); + + g_clear_list (&uris, g_free); + g_object_unref (appinfo); + g_assert_finalize_object (context); + + g_fake_document_portal_thread_stop (thread); + g_clear_object (&thread); + session_bus_down (); +} + /* Test if Desktop-File Id is correctly formed */ static void test_id (void) @@ -2062,6 +2135,7 @@ main (int argc, g_test_add_func ("/desktop-app-info/launch-as-manager/fail", test_launch_as_manager_fail); g_test_add_func ("/desktop-app-info/launch-default-uri-handler", test_default_uri_handler); g_test_add_func ("/desktop-app-info/launch-default-uri-handler-async", test_default_uri_handler_async); + g_test_add_func ("/desktop-app-info/launch-snap-uri-with-portal", launch_snap_uris_with_portal); g_test_add_func ("/desktop-app-info/id", test_id); for (i = 0; i < G_N_ELEMENTS (supported_terminals); i++) diff --git a/gio/tests/meson.build b/gio/tests/meson.build index 0b5e6c912..580a6af86 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -370,13 +370,6 @@ if host_machine.system() != 'windows' 'install' : false, 'extra_programs' : ['appinfo-test'], }, - 'desktop-app-info' : { - 'install' : false, - 'depends' : gio_launch_desktop, - 'extra_programs' : ['apps', 'appinfo-test'], - # FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/3148 - 'can_fail' : host_system == 'gnu', - }, } endif @@ -384,6 +377,7 @@ if host_machine.system() != 'windows' 'basic-application' : {}, 'dbus-launch' : {}, 'appinfo-test' : {}, + 'snapinfo-test' : {}, } if not glib_have_cocoa @@ -622,6 +616,14 @@ if host_machine.system() != 'windows' 'dbus-appinfo' : { 'extra_sources' : [extra_sources, fake_document_portal_sources], }, + 'desktop-app-info' : { + 'install' : false, + 'depends' : gio_launch_desktop, + 'extra_programs' : ['apps', 'appinfo-test', 'snapinfo-test'], + # FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/3148 + 'can_fail' : host_system == 'gnu', + 'extra_sources' : [extra_sources, fake_document_portal_sources], + }, } endif @@ -750,6 +752,7 @@ appinfo_test_desktop_files = [ 'appinfo-test-path', 'appinfo-test', 'appinfo-test2', + 'snap-app_appinfo-test', ] foreach appinfo_test_desktop_file : appinfo_test_desktop_files diff --git a/gio/tests/snap-app_appinfo-test.desktop.in b/gio/tests/snap-app_appinfo-test.desktop.in new file mode 100644 index 000000000..d79f6db69 --- /dev/null +++ b/gio/tests/snap-app_appinfo-test.desktop.in @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2025 Marco Trevisan (Treviño) +[Desktop Entry] +Type=Application +Version=1.0 +Name=Snap Test +X-SnapInstanceName=snap-app +X-SnapAppName=appinfo-test +X-SnapCommonID=org.gio.snap-app.appinfo-test +DBusActivatable=false +Exec=@installed_tests_dir@/snapinfo-test %U +StartupNotify=true diff --git a/gio/tests/snapinfo-test.c b/gio/tests/snapinfo-test.c new file mode 100644 index 000000000..0dd51a40f --- /dev/null +++ b/gio/tests/snapinfo-test.c @@ -0,0 +1,51 @@ +/** + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 Marco Trevisan (Treviño) + */ + +#include +#include + +int +main (int argc, char *argv[]) +{ + const char *envvar; + const char *document_portal_mount; + char *expected; + char *expected_files[3] = {0}; + gint pid_from_env; + + g_test_init (&argc, &argv, NULL); + + envvar = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE"); + g_assert_nonnull (envvar); + + expected = g_test_build_filename (G_TEST_BUILT, "snap-app_appinfo-test.desktop", NULL); + g_assert_cmpstr (envvar, ==, expected); + g_free (expected); + + envvar = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE_PID"); + g_assert (envvar != NULL); + pid_from_env = atoi (envvar); + g_assert_cmpint (pid_from_env, ==, getpid ()); + + document_portal_mount = g_getenv ("DOCUMENT_PORTAL_MOUNT_POINT"); + g_assert_nonnull (document_portal_mount); + + expected_files[0] = g_build_filename (document_portal_mount, + "document-id-0", "snap-app_appinfo-test.desktop", + NULL); + expected_files[1] = g_build_filename (document_portal_mount, + "document-id-1", "appinfo-test.desktop", + NULL); + + g_assert_cmpint (argc, ==, 3); + + for (size_t i = 0; i < G_N_ELEMENTS (expected_files); ++i) + { + g_assert_cmpstr (argv[i+1], ==, expected_files[i]); + g_clear_pointer (&expected_files[i], g_free); + } + + return 0; +}