1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright 2017 Red Hat, Inc.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
26 #include "gopenuriportal.h"
31 #include "gunixfdlist.h"
40 #define HAVE_O_CLOEXEC 1
44 static GXdpOpenURI
*openuri
;
47 init_openuri_portal (void)
49 static gsize openuri_inited
= 0;
51 if (g_once_init_enter (&openuri_inited
))
54 GDBusConnection
*connection
= g_bus_get_sync (G_BUS_TYPE_SESSION
, NULL
, &error
);
56 if (connection
!= NULL
)
58 openuri
= gxdp_open_uri_proxy_new_sync (connection
, 0,
59 "org.freedesktop.portal.Desktop",
60 "/org/freedesktop/portal/desktop",
64 g_warning ("Cannot create document portal proxy: %s", error
->message
);
68 g_object_unref (connection
);
72 g_warning ("Cannot connect to session bus when initializing document portal: %s",
77 g_once_init_leave (&openuri_inited
, 1);
80 return openuri
!= NULL
;
84 g_openuri_portal_open_uri (const char *uri
,
85 const char *parent_window
,
89 GVariantBuilder opt_builder
;
92 if (!init_openuri_portal ())
94 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_NOT_INITIALIZED
,
95 "OpenURI portal is not available");
99 g_variant_builder_init (&opt_builder
, G_VARIANT_TYPE_VARDICT
);
101 file
= g_file_new_for_uri (uri
);
102 if (g_file_is_native (file
))
105 GUnixFDList
*fd_list
= NULL
;
106 int fd
, fd_id
, errsv
;
108 path
= g_file_get_path (file
);
110 fd
= g_open (path
, O_PATH
| O_CLOEXEC
);
114 g_set_error (error
, G_IO_ERROR
, g_io_error_from_errno (errsv
),
115 "Failed to open '%s'", path
);
119 #ifndef HAVE_O_CLOEXEC
120 fcntl (fd
, F_SETFD
, FD_CLOEXEC
);
122 fd_list
= g_unix_fd_list_new_from_array (&fd
, 1);
126 res
= gxdp_open_uri_call_open_file_sync (openuri
,
127 parent_window
? parent_window
: "",
128 g_variant_new ("h", fd_id
),
129 g_variant_builder_end (&opt_builder
),
136 g_object_unref (fd_list
);
140 res
= gxdp_open_uri_call_open_uri_sync (openuri
,
141 parent_window
? parent_window
: "",
143 g_variant_builder_end (&opt_builder
),
149 g_object_unref (file
);
155 XDG_DESKTOP_PORTAL_SUCCESS
= 0,
156 XDG_DESKTOP_PORTAL_CANCELLED
= 1,
157 XDG_DESKTOP_PORTAL_FAILED
= 2
161 response_received (GDBusConnection
*connection
,
162 const char *sender_name
,
163 const char *object_path
,
164 const char *interface_name
,
165 const char *signal_name
,
166 GVariant
*parameters
,
169 GTask
*task
= user_data
;
173 signal_id
= GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (task
), "signal-id"));
174 g_dbus_connection_signal_unsubscribe (connection
, signal_id
);
176 g_variant_get (parameters
, "(u@a{sv})", &response
, NULL
);
180 case XDG_DESKTOP_PORTAL_SUCCESS
:
181 g_task_return_boolean (task
, TRUE
);
183 case XDG_DESKTOP_PORTAL_CANCELLED
:
184 g_task_return_new_error (task
, G_IO_ERROR
, G_IO_ERROR_CANCELLED
, "Launch cancelled");
186 case XDG_DESKTOP_PORTAL_FAILED
:
188 g_task_return_new_error (task
, G_IO_ERROR
, G_IO_ERROR_FAILED
, "Launch failed");
192 g_object_unref (task
);
196 open_call_done (GObject
*source
,
197 GAsyncResult
*result
,
200 GDBusConnection
*connection
= G_DBUS_CONNECTION (source
);
201 GTask
*task
= user_data
;
202 GError
*error
= NULL
;
209 open_file
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (task
), "open-file"));
212 res
= gxdp_open_uri_call_open_file_finish (openuri
, &path
, NULL
, result
, &error
);
214 res
= gxdp_open_uri_call_open_uri_finish (openuri
, &path
, result
, &error
);
218 g_task_return_error (task
, error
);
219 g_object_unref (task
);
224 handle
= (const char *)g_object_get_data (G_OBJECT (task
), "handle");
225 if (strcmp (handle
, path
) != 0)
227 signal_id
= GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (task
), "signal-id"));
228 g_dbus_connection_signal_unsubscribe (connection
, signal_id
);
230 signal_id
= g_dbus_connection_signal_subscribe (connection
,
231 "org.freedesktop.portal.Desktop",
232 "org.freedesktop.portal.Request",
236 G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE
,
240 g_object_set_data (G_OBJECT (task
), "signal-id", GINT_TO_POINTER (signal_id
));
245 g_openuri_portal_open_uri_async (const char *uri
,
246 const char *parent_window
,
247 GCancellable
*cancellable
,
248 GAsyncReadyCallback callback
,
251 GDBusConnection
*connection
;
254 GVariant
*opts
= NULL
;
258 if (!init_openuri_portal ())
260 g_task_report_new_error (NULL
, callback
, user_data
, NULL
,
261 G_IO_ERROR
, G_IO_ERROR_NOT_INITIALIZED
,
262 "OpenURI portal is not available");
266 connection
= g_dbus_proxy_get_connection (G_DBUS_PROXY (openuri
));
270 GVariantBuilder opt_builder
;
275 task
= g_task_new (NULL
, cancellable
, callback
, user_data
);
277 token
= g_strdup_printf ("gio%d", g_random_int_range (0, G_MAXINT
));
278 sender
= g_strdup (g_dbus_connection_get_unique_name (connection
) + 1);
279 for (i
= 0; sender
[i
]; i
++)
280 if (sender
[i
] == '.')
283 handle
= g_strdup_printf ("/org/fredesktop/portal/desktop/request/%s/%s", sender
, token
);
284 g_object_set_data_full (G_OBJECT (task
), "handle", handle
, g_free
);
287 signal_id
= g_dbus_connection_signal_subscribe (connection
,
288 "org.freedesktop.portal.Desktop",
289 "org.freedesktop.portal.Request",
293 G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE
,
297 g_object_set_data (G_OBJECT (task
), "signal-id", GINT_TO_POINTER (signal_id
));
299 g_variant_builder_init (&opt_builder
, G_VARIANT_TYPE_VARDICT
);
300 g_variant_builder_add (&opt_builder
, "{sv}", "handle_token", g_variant_new_string (token
));
303 opts
= g_variant_builder_end (&opt_builder
);
308 file
= g_file_new_for_uri (uri
);
309 if (g_file_is_native (file
))
312 GUnixFDList
*fd_list
= NULL
;
313 int fd
, fd_id
, errsv
;
316 g_object_set_data (G_OBJECT (task
), "open-file", GINT_TO_POINTER (TRUE
));
318 path
= g_file_get_path (file
);
319 fd
= g_open (path
, O_PATH
| O_CLOEXEC
);
323 g_task_report_new_error (NULL
, callback
, user_data
, NULL
,
324 G_IO_ERROR
, g_io_error_from_errno (errsv
),
325 "OpenURI portal is not available");
329 #ifndef HAVE_O_CLOEXEC
330 fcntl (fd
, F_SETFD
, FD_CLOEXEC
);
332 fd_list
= g_unix_fd_list_new_from_array (&fd
, 1);
336 gxdp_open_uri_call_open_file (openuri
,
337 parent_window
? parent_window
: "",
338 g_variant_new ("h", fd_id
),
342 task
? open_call_done
: NULL
,
344 g_object_unref (fd_list
);
349 gxdp_open_uri_call_open_uri (openuri
,
350 parent_window
? parent_window
: "",
354 task
? open_call_done
: NULL
,
358 g_object_unref (file
);
362 g_openuri_portal_open_uri_finish (GAsyncResult
*result
,
365 return g_task_propagate_boolean (G_TASK (result
), error
);