| 
									
										
										
										
											2024-09-24 14:12:15 +02:00
										 |  |  |  | /*
 | 
					
						
							|  |  |  |  |  * Copyright © 2024 GNOME Foundation Inc. | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * SPDX-License-Identifier: LGPL-2.1-or-later | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * This library 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; either | 
					
						
							|  |  |  |  |  * version 2.1 of the License, or (at your option) any later version. | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * This library 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 library; if not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * Authors: Julian Sparber <jsparber@gnome.org> | 
					
						
							|  |  |  |  |  *          Philip Withnall <philip@tecnocode.co.uk> | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /* A stub implementation of xdg-desktop-portal */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 13:29:01 +00:00
										 |  |  |  | #ifdef __FreeBSD__
 | 
					
						
							|  |  |  |  | #include <fcntl.h>
 | 
					
						
							|  |  |  |  | #include <sys/types.h>
 | 
					
						
							|  |  |  |  | #include <sys/user.h>
 | 
					
						
							|  |  |  |  | #endif  /* __FreeBSD__ */
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-24 14:12:15 +02:00
										 |  |  |  | #include <glib.h>
 | 
					
						
							|  |  |  |  | #include <gio/gio.h>
 | 
					
						
							|  |  |  |  | #include <gio/gunixfdlist.h>
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #include "fake-desktop-portal.h"
 | 
					
						
							|  |  |  |  | #include "fake-openuri-portal-generated.h"
 | 
					
						
							|  |  |  |  | #include "fake-request-portal-generated.h"
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | struct _GFakeDesktopPortalThread | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GObject parent_instance; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   char *address;  /* (not nullable) */ | 
					
						
							|  |  |  |  |   GCancellable *cancellable;  /* (not nullable) (owned) */ | 
					
						
							|  |  |  |  |   GThread *thread;  /* (not nullable) (owned) */ | 
					
						
							|  |  |  |  |   GCond cond;  /* (mutex mutex) */ | 
					
						
							|  |  |  |  |   GMutex mutex; | 
					
						
							|  |  |  |  |   gboolean ready;  /* (mutex mutex) */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   char *request_activation_token;  /* (mutex mutex) */ | 
					
						
							|  |  |  |  |   char *request_uri;  /* (mutex mutex) */ | 
					
						
							|  |  |  |  | } FakeDesktopPortalThread; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | G_DEFINE_FINAL_TYPE (GFakeDesktopPortalThread, g_fake_desktop_portal_thread, G_TYPE_OBJECT) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void g_fake_desktop_portal_thread_finalize (GObject *object); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_fake_desktop_portal_thread_class_init (GFakeDesktopPortalThreadClass *klass) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GObjectClass *gobject_class = G_OBJECT_CLASS (klass); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   gobject_class->finalize = g_fake_desktop_portal_thread_finalize; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_fake_desktop_portal_thread_init (GFakeDesktopPortalThread *self) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   self->cancellable = g_cancellable_new (); | 
					
						
							|  |  |  |  |   g_cond_init (&self->cond); | 
					
						
							|  |  |  |  |   g_mutex_init (&self->mutex); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_fake_desktop_portal_thread_finalize (GObject *object) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GFakeDesktopPortalThread *self = G_FAKE_DESKTOP_PORTAL_THREAD (object); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_assert (self->thread == NULL);  /* should already have been joined */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_mutex_clear (&self->mutex); | 
					
						
							|  |  |  |  |   g_cond_clear (&self->cond); | 
					
						
							|  |  |  |  |   g_clear_object (&self->cancellable); | 
					
						
							|  |  |  |  |   g_clear_pointer (&self->address, g_free); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_clear_pointer (&self->request_activation_token, g_free); | 
					
						
							|  |  |  |  |   g_clear_pointer (&self->request_uri, g_free); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   G_OBJECT_CLASS (g_fake_desktop_portal_thread_parent_class)->finalize (object); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gboolean | 
					
						
							|  |  |  |  | on_handle_close (FakeRequest           *object, | 
					
						
							|  |  |  |  |                  GDBusMethodInvocation *invocation, | 
					
						
							|  |  |  |  |                  gpointer               user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   g_test_message ("Got close request"); | 
					
						
							|  |  |  |  |   fake_request_complete_close (object, invocation); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return G_DBUS_METHOD_INVOCATION_HANDLED; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static char* | 
					
						
							|  |  |  |  | get_request_path (GDBusMethodInvocation *invocation, | 
					
						
							|  |  |  |  |                   const char            *token) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   char *request_obj_path; | 
					
						
							|  |  |  |  |   char *sender; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   sender = g_strdup (g_dbus_method_invocation_get_sender (invocation) + 1); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* The object path needs to be the specific format.
 | 
					
						
							|  |  |  |  |    * See: https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Request.html#org-freedesktop-portal-request */
 | 
					
						
							|  |  |  |  |   for (size_t i = 0; sender[i]; i++) | 
					
						
							|  |  |  |  |     if (sender[i] == '.') | 
					
						
							|  |  |  |  |       sender[i] = '_'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   request_obj_path = g_strdup_printf ("/org/freedesktop/portal/desktop/request/%s/%s", sender, token); | 
					
						
							|  |  |  |  |   g_free (sender); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return request_obj_path; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gboolean | 
					
						
							|  |  |  |  | handle_request (GFakeDesktopPortalThread *self, | 
					
						
							|  |  |  |  |                 FakeOpenURI             *object, | 
					
						
							|  |  |  |  |                 GDBusMethodInvocation   *invocation, | 
					
						
							|  |  |  |  |                 const gchar             *arg_parent_window, | 
					
						
							|  |  |  |  |                 const gchar             *arg_uri, | 
					
						
							|  |  |  |  |                 gboolean                 open_file, | 
					
						
							|  |  |  |  |                 GVariant                *arg_options) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   const char *activation_token = NULL; | 
					
						
							|  |  |  |  |   GError *error = NULL; | 
					
						
							|  |  |  |  |   FakeRequest *interface_request; | 
					
						
							|  |  |  |  |   GVariantBuilder opt_builder; | 
					
						
							|  |  |  |  |   char *request_obj_path; | 
					
						
							|  |  |  |  |   const char *token = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (arg_options) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       g_variant_lookup (arg_options, "activation_token", "&s", &activation_token); | 
					
						
							|  |  |  |  |       g_variant_lookup (arg_options, "handle_token", "&s", &token); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_set_str (&self->request_activation_token, activation_token); | 
					
						
							|  |  |  |  |   g_set_str (&self->request_uri, arg_uri); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   request_obj_path = get_request_path (invocation, token ? token : "t"); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (open_file) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       g_test_message ("Got open file request for %s", arg_uri); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fake_open_uri_complete_open_file (object, | 
					
						
							|  |  |  |  |                                         invocation, | 
					
						
							|  |  |  |  |                                         NULL, | 
					
						
							|  |  |  |  |                                         request_obj_path); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   else | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       g_test_message ("Got open URI request for %s", arg_uri); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fake_open_uri_complete_open_uri (object, | 
					
						
							|  |  |  |  |                                        invocation, | 
					
						
							|  |  |  |  |                                        request_obj_path); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   interface_request = fake_request_skeleton_new (); | 
					
						
							|  |  |  |  |   g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_signal_connect (interface_request, | 
					
						
							|  |  |  |  |                     "handle-close", | 
					
						
							|  |  |  |  |                     G_CALLBACK (on_handle_close), | 
					
						
							|  |  |  |  |                     NULL); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (interface_request), | 
					
						
							|  |  |  |  |                                     g_dbus_method_invocation_get_connection (invocation), | 
					
						
							|  |  |  |  |                                     request_obj_path, | 
					
						
							|  |  |  |  |                                     &error); | 
					
						
							|  |  |  |  |   g_assert_no_error (error); | 
					
						
							|  |  |  |  |   g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (interface_request), | 
					
						
							|  |  |  |  |                                        G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); | 
					
						
							|  |  |  |  |   g_test_message ("Request skeleton exported at %s", request_obj_path); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* We can't use `fake_request_emit_response()` because it doesn't set the sender */ | 
					
						
							|  |  |  |  |   g_dbus_connection_emit_signal (g_dbus_method_invocation_get_connection (invocation), | 
					
						
							|  |  |  |  |                                  g_dbus_method_invocation_get_sender (invocation), | 
					
						
							|  |  |  |  |                                  request_obj_path, | 
					
						
							|  |  |  |  |                                  "org.freedesktop.portal.Request", | 
					
						
							|  |  |  |  |                                  "Response", | 
					
						
							|  |  |  |  |                                  g_variant_new ("(u@a{sv})", | 
					
						
							|  |  |  |  |                                                 0, /* Success */ | 
					
						
							|  |  |  |  |                                                 g_variant_builder_end (&opt_builder)), | 
					
						
							|  |  |  |  |                                  NULL); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_test_message ("Response emitted"); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (interface_request)); | 
					
						
							|  |  |  |  |   g_free (request_obj_path); | 
					
						
							|  |  |  |  |   g_object_unref (interface_request); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return G_DBUS_METHOD_INVOCATION_HANDLED; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 13:21:44 +00:00
										 |  |  |  | /* This is currently private as there’s only one user of it, but it could become
 | 
					
						
							|  |  |  |  |  * a public API in future. */ | 
					
						
							|  |  |  |  | static char * | 
					
						
							|  |  |  |  | _g_fd_query_path (int      fd, | 
					
						
							|  |  |  |  |                   GError **error) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | #if defined(__FreeBSD__)
 | 
					
						
							|  |  |  |  |   struct kinfo_file kf; | 
					
						
							|  |  |  |  |   kf.kf_structsize = sizeof (kf); | 
					
						
							|  |  |  |  |   if (fcntl (fd, F_KINFO, &kf) < 0) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       int saved_errno = errno; | 
					
						
							|  |  |  |  |       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, | 
					
						
							|  |  |  |  |                    "Error querying file information for FD %d: %s", | 
					
						
							|  |  |  |  |                    fd, g_strerror (saved_errno)); | 
					
						
							|  |  |  |  |       return NULL; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return g_strdup (kf.kf_path); | 
					
						
							|  |  |  |  | #elif defined(G_OS_UNIX)
 | 
					
						
							|  |  |  |  |   char *path = NULL; | 
					
						
							|  |  |  |  |   char *proc_path = g_strdup_printf ("/proc/self/fd/%d", fd); | 
					
						
							|  |  |  |  |   path = g_file_read_link (proc_path, error); | 
					
						
							|  |  |  |  |   g_free (proc_path); | 
					
						
							|  |  |  |  |   return g_steal_pointer (&path); | 
					
						
							|  |  |  |  | #else
 | 
					
						
							|  |  |  |  |   /*  - A NetBSD implementation would probably use `fcntl()` with `F_GETPATH`:
 | 
					
						
							|  |  |  |  |    *    https://man.netbsd.org/fcntl.2
 | 
					
						
							|  |  |  |  |    *  - A Windows implementation would probably use `GetFinalPathNameByHandleW()`: | 
					
						
							|  |  |  |  |    *    https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlea
 | 
					
						
							|  |  |  |  |    *  - A Hurd implementation could open("/dev/fd/%u"): | 
					
						
							|  |  |  |  |    *    https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4396#note_2279923
 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   #error "_g_fd_query_path() not supported on this platform"
 | 
					
						
							|  |  |  |  | #endif
 | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-24 14:12:15 +02:00
										 |  |  |  | static char * | 
					
						
							|  |  |  |  | handle_to_uri (GVariant    *handle, | 
					
						
							|  |  |  |  |                GUnixFDList *fd_list) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   int fd = -1; | 
					
						
							|  |  |  |  |   int fd_id; | 
					
						
							|  |  |  |  |   char *path; | 
					
						
							|  |  |  |  |   char *uri; | 
					
						
							| 
									
										
										
										
											2024-11-13 13:21:44 +00:00
										 |  |  |  |   GError *local_error = NULL; | 
					
						
							| 
									
										
										
										
											2024-09-24 14:12:15 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   fd_id = g_variant_get_handle (handle); | 
					
						
							|  |  |  |  |   fd = g_unix_fd_list_get (fd_list, fd_id, NULL); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (fd == -1) | 
					
						
							|  |  |  |  |     return NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 13:21:44 +00:00
										 |  |  |  |   path = _g_fd_query_path (fd, &local_error); | 
					
						
							|  |  |  |  |   g_assert_no_error (local_error); | 
					
						
							| 
									
										
										
										
											2024-09-24 14:12:15 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   uri = g_filename_to_uri (path, NULL, NULL); | 
					
						
							|  |  |  |  |   g_free (path); | 
					
						
							|  |  |  |  |   close (fd); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return uri; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gboolean | 
					
						
							|  |  |  |  | on_handle_open_file (FakeOpenURI           *object, | 
					
						
							|  |  |  |  |                      GDBusMethodInvocation *invocation, | 
					
						
							|  |  |  |  |                      GUnixFDList           *fd_list, | 
					
						
							|  |  |  |  |                      const gchar           *arg_parent_window, | 
					
						
							|  |  |  |  |                      GVariant              *arg_fd, | 
					
						
							|  |  |  |  |                      GVariant              *arg_options, | 
					
						
							|  |  |  |  |                      gpointer               user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GFakeDesktopPortalThread *self = G_FAKE_DESKTOP_PORTAL_THREAD (user_data); | 
					
						
							|  |  |  |  |   char *uri = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   uri = handle_to_uri (arg_fd, fd_list); | 
					
						
							|  |  |  |  |   handle_request (self, | 
					
						
							|  |  |  |  |                   object, | 
					
						
							|  |  |  |  |                   invocation, | 
					
						
							|  |  |  |  |                   arg_parent_window, | 
					
						
							|  |  |  |  |                   uri, | 
					
						
							|  |  |  |  |                   TRUE, | 
					
						
							|  |  |  |  |                   arg_options); | 
					
						
							|  |  |  |  |   g_free (uri); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return G_DBUS_METHOD_INVOCATION_HANDLED; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gboolean | 
					
						
							|  |  |  |  | on_handle_open_uri (FakeOpenURI           *object, | 
					
						
							|  |  |  |  |                     GDBusMethodInvocation *invocation, | 
					
						
							|  |  |  |  |                     const gchar           *arg_parent_window, | 
					
						
							|  |  |  |  |                     const gchar           *arg_uri, | 
					
						
							|  |  |  |  |                     GVariant              *arg_options, | 
					
						
							|  |  |  |  |                     gpointer               user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GFakeDesktopPortalThread *self = G_FAKE_DESKTOP_PORTAL_THREAD (user_data); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   handle_request (self, | 
					
						
							|  |  |  |  |                   object, | 
					
						
							|  |  |  |  |                   invocation, | 
					
						
							|  |  |  |  |                   arg_parent_window, | 
					
						
							|  |  |  |  |                   arg_uri, | 
					
						
							|  |  |  |  |                   TRUE, | 
					
						
							|  |  |  |  |                   arg_options); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return G_DBUS_METHOD_INVOCATION_HANDLED; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | on_name_acquired (GDBusConnection *connection, | 
					
						
							|  |  |  |  |                   const gchar     *name, | 
					
						
							|  |  |  |  |                   gpointer         user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GFakeDesktopPortalThread *self = G_FAKE_DESKTOP_PORTAL_THREAD (user_data); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_test_message ("Acquired the name %s", name); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_mutex_lock (&self->mutex); | 
					
						
							|  |  |  |  |   self->ready = TRUE; | 
					
						
							|  |  |  |  |   g_cond_signal (&self->cond); | 
					
						
							|  |  |  |  |   g_mutex_unlock (&self->mutex); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | on_name_lost (GDBusConnection *connection, | 
					
						
							|  |  |  |  |               const gchar     *name, | 
					
						
							|  |  |  |  |               gpointer         user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   g_test_message ("Lost the name %s", name); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gboolean | 
					
						
							|  |  |  |  | cancelled_cb (GCancellable *cancellable, | 
					
						
							|  |  |  |  |               gpointer      user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   g_test_message ("fake-desktop-portal cancelled"); | 
					
						
							|  |  |  |  |   return G_SOURCE_CONTINUE; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gpointer | 
					
						
							|  |  |  |  | fake_desktop_portal_thread_cb (gpointer user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GFakeDesktopPortalThread *self = G_FAKE_DESKTOP_PORTAL_THREAD (user_data); | 
					
						
							|  |  |  |  |   GMainContext *context = NULL; | 
					
						
							|  |  |  |  |   GDBusConnection *connection = NULL; | 
					
						
							|  |  |  |  |   GSource *source = NULL; | 
					
						
							|  |  |  |  |   guint id; | 
					
						
							|  |  |  |  |   FakeOpenURI *interface_open_uri; | 
					
						
							|  |  |  |  |   GError *local_error = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   context = g_main_context_new (); | 
					
						
							|  |  |  |  |   g_main_context_push_thread_default (context); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   connection = g_dbus_connection_new_for_address_sync (self->address, | 
					
						
							|  |  |  |  |                                                        G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | | 
					
						
							|  |  |  |  |                                                        G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, | 
					
						
							|  |  |  |  |                                                        NULL, | 
					
						
							|  |  |  |  |                                                        self->cancellable, | 
					
						
							|  |  |  |  |                                                        &local_error); | 
					
						
							|  |  |  |  |   g_assert_no_error (local_error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* Listen for cancellation. The source will wake up the context iteration
 | 
					
						
							|  |  |  |  |    * which can then re-check its exit condition below. */ | 
					
						
							|  |  |  |  |   source = g_cancellable_source_new (self->cancellable); | 
					
						
							|  |  |  |  |   g_source_set_callback (source, G_SOURCE_FUNC (cancelled_cb), NULL, NULL); | 
					
						
							|  |  |  |  |   g_source_attach (source, context); | 
					
						
							|  |  |  |  |   g_source_unref (source); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* Set up the interface */ | 
					
						
							|  |  |  |  |   g_test_message ("Acquired a message bus connection"); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   interface_open_uri = fake_open_uri_skeleton_new (); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_signal_connect (interface_open_uri, | 
					
						
							|  |  |  |  |                     "handle-open-file", | 
					
						
							|  |  |  |  |                     G_CALLBACK (on_handle_open_file), | 
					
						
							|  |  |  |  |                     self); | 
					
						
							|  |  |  |  |   g_signal_connect (interface_open_uri, | 
					
						
							|  |  |  |  |                     "handle-open-uri", | 
					
						
							|  |  |  |  |                     G_CALLBACK (on_handle_open_uri), | 
					
						
							|  |  |  |  |                     self); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (interface_open_uri), | 
					
						
							|  |  |  |  |                                     connection, | 
					
						
							|  |  |  |  |                                     "/org/freedesktop/portal/desktop", | 
					
						
							|  |  |  |  |                                     &local_error); | 
					
						
							|  |  |  |  |   g_assert_no_error (local_error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* Own the portal name */ | 
					
						
							|  |  |  |  |   id = g_bus_own_name_on_connection (connection, | 
					
						
							|  |  |  |  |                                      "org.freedesktop.portal.Desktop", | 
					
						
							|  |  |  |  |                                      G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | | 
					
						
							|  |  |  |  |                                      G_BUS_NAME_OWNER_FLAGS_REPLACE, | 
					
						
							|  |  |  |  |                                      on_name_acquired, | 
					
						
							|  |  |  |  |                                      on_name_lost, | 
					
						
							|  |  |  |  |                                      self, | 
					
						
							|  |  |  |  |                                      NULL); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   while (!g_cancellable_is_cancelled (self->cancellable)) | 
					
						
							|  |  |  |  |     g_main_context_iteration (context, TRUE); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_bus_unown_name (id); | 
					
						
							|  |  |  |  |   g_clear_object (&connection); | 
					
						
							|  |  |  |  |   g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (interface_open_uri)); | 
					
						
							|  |  |  |  |   g_object_unref (interface_open_uri); | 
					
						
							|  |  |  |  |   g_main_context_pop_thread_default (context); | 
					
						
							|  |  |  |  |   g_clear_pointer (&context, g_main_context_unref); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return NULL; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /* Get the activation token given to the most recent OpenURI request
 | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * Returns: (transfer none) (nullable: an activation token | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | const gchar * | 
					
						
							|  |  |  |  | g_fake_desktop_portal_thread_get_last_request_activation_token (GFakeDesktopPortalThread *self) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   g_return_val_if_fail (G_IS_FAKE_DESKTOP_PORTAL_THREAD (self), NULL); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return self->request_activation_token; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /* Get the file or URI given to the most recent OpenURI request
 | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * Returns: (transfer none) (nullable): an URI | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | const gchar * | 
					
						
							|  |  |  |  | g_fake_desktop_portal_thread_get_last_request_uri (GFakeDesktopPortalThread *self) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   g_return_val_if_fail (G_IS_FAKE_DESKTOP_PORTAL_THREAD (self), NULL); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return self->request_uri; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /*
 | 
					
						
							|  |  |  |  |  * Create a new #GFakeDesktopPortalThread. The thread isn’t started until | 
					
						
							|  |  |  |  |  * g_fake_desktop_portal_thread_run() is called on it. | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * Returns: (transfer full): the new fake desktop portal wrapper | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | GFakeDesktopPortalThread * | 
					
						
							|  |  |  |  | g_fake_desktop_portal_thread_new (const char *address) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GFakeDesktopPortalThread *self = g_object_new (G_TYPE_FAKE_DESKTOP_PORTAL_THREAD, NULL); | 
					
						
							|  |  |  |  |   self->address = g_strdup (address); | 
					
						
							|  |  |  |  |   return g_steal_pointer (&self); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /*
 | 
					
						
							|  |  |  |  |  * Start a worker thread which will run a fake | 
					
						
							|  |  |  |  |  * `org.freedesktop.portal.Desktops` portal on the bus at @address. This is | 
					
						
							|  |  |  |  |  * intended to be used with #GTestDBus to mock up a portal from within a unit | 
					
						
							|  |  |  |  |  * test process, rather than relying on D-Bus activation of a mock portal | 
					
						
							|  |  |  |  |  * subprocess. | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * It blocks until the thread has owned its D-Bus name and is ready to handle | 
					
						
							|  |  |  |  |  * requests. | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | void | 
					
						
							|  |  |  |  | g_fake_desktop_portal_thread_run (GFakeDesktopPortalThread *self) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   g_return_if_fail (G_IS_FAKE_DESKTOP_PORTAL_THREAD (self)); | 
					
						
							|  |  |  |  |   g_return_if_fail (self->thread == NULL); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   self->thread = g_thread_new ("fake-desktop-portal", fake_desktop_portal_thread_cb, self); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* Block until the thread is ready. */ | 
					
						
							|  |  |  |  |   g_mutex_lock (&self->mutex); | 
					
						
							|  |  |  |  |   while (!self->ready) | 
					
						
							|  |  |  |  |     g_cond_wait (&self->cond, &self->mutex); | 
					
						
							|  |  |  |  |   g_mutex_unlock (&self->mutex); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /* Stop and join a worker thread started with fake_desktop_portal_thread_run().
 | 
					
						
							|  |  |  |  |  * Blocks until the thread has stopped and joined. | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * Once this has been called, it’s safe to drop the final reference on @self. */ | 
					
						
							|  |  |  |  | void | 
					
						
							|  |  |  |  | g_fake_desktop_portal_thread_stop (GFakeDesktopPortalThread *self) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   g_return_if_fail (G_IS_FAKE_DESKTOP_PORTAL_THREAD (self)); | 
					
						
							|  |  |  |  |   g_return_if_fail (self->thread != NULL); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_cancellable_cancel (self->cancellable); | 
					
						
							|  |  |  |  |   g_thread_join (g_steal_pointer (&self->thread)); | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-12-10 11:47:43 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | /* Whether fake-desktop-portal is supported on this platform. This basically
 | 
					
						
							|  |  |  |  |  * means whether _g_fd_query_path() will work at runtime. */ | 
					
						
							|  |  |  |  | gboolean | 
					
						
							|  |  |  |  | g_fake_desktop_portal_is_supported (void) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | #ifdef __GNU__
 | 
					
						
							|  |  |  |  |   return FALSE; | 
					
						
							|  |  |  |  | #else
 | 
					
						
							|  |  |  |  |   return TRUE; | 
					
						
							|  |  |  |  | #endif
 | 
					
						
							|  |  |  |  | } |