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 GXdpOpenURI
*openuri
= GXDP_OPEN_URI (source
);
201 GDBusConnection
*connection
;
202 GTask
*task
= user_data
;
203 GError
*error
= NULL
;
210 connection
= g_dbus_proxy_get_connection (G_DBUS_PROXY (openuri
));
211 open_file
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (task
), "open-file"));
214 res
= gxdp_open_uri_call_open_file_finish (openuri
, &path
, NULL
, result
, &error
);
216 res
= gxdp_open_uri_call_open_uri_finish (openuri
, &path
, result
, &error
);
220 g_task_return_error (task
, error
);
221 g_object_unref (task
);
226 handle
= (const char *)g_object_get_data (G_OBJECT (task
), "handle");
227 if (g_strcmp0 (handle
, path
) != 0)
229 signal_id
= GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (task
), "signal-id"));
230 g_dbus_connection_signal_unsubscribe (connection
, signal_id
);
232 signal_id
= g_dbus_connection_signal_subscribe (connection
,
233 "org.freedesktop.portal.Desktop",
234 "org.freedesktop.portal.Request",
238 G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE
,
242 g_object_set_data (G_OBJECT (task
), "signal-id", GINT_TO_POINTER (signal_id
));
247 g_openuri_portal_open_uri_async (const char *uri
,
248 const char *parent_window
,
249 GCancellable
*cancellable
,
250 GAsyncReadyCallback callback
,
253 GDBusConnection
*connection
;
256 GVariant
*opts
= NULL
;
260 if (!init_openuri_portal ())
262 g_task_report_new_error (NULL
, callback
, user_data
, NULL
,
263 G_IO_ERROR
, G_IO_ERROR_NOT_INITIALIZED
,
264 "OpenURI portal is not available");
268 connection
= g_dbus_proxy_get_connection (G_DBUS_PROXY (openuri
));
272 GVariantBuilder opt_builder
;
277 task
= g_task_new (NULL
, cancellable
, callback
, user_data
);
279 token
= g_strdup_printf ("gio%d", g_random_int_range (0, G_MAXINT
));
280 sender
= g_strdup (g_dbus_connection_get_unique_name (connection
) + 1);
281 for (i
= 0; sender
[i
]; i
++)
282 if (sender
[i
] == '.')
285 handle
= g_strdup_printf ("/org/fredesktop/portal/desktop/request/%s/%s", sender
, token
);
286 g_object_set_data_full (G_OBJECT (task
), "handle", handle
, g_free
);
289 signal_id
= g_dbus_connection_signal_subscribe (connection
,
290 "org.freedesktop.portal.Desktop",
291 "org.freedesktop.portal.Request",
295 G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE
,
299 g_object_set_data (G_OBJECT (task
), "signal-id", GINT_TO_POINTER (signal_id
));
301 g_variant_builder_init (&opt_builder
, G_VARIANT_TYPE_VARDICT
);
302 g_variant_builder_add (&opt_builder
, "{sv}", "handle_token", g_variant_new_string (token
));
305 opts
= g_variant_builder_end (&opt_builder
);
310 file
= g_file_new_for_uri (uri
);
311 if (g_file_is_native (file
))
314 GUnixFDList
*fd_list
= NULL
;
315 int fd
, fd_id
, errsv
;
318 g_object_set_data (G_OBJECT (task
), "open-file", GINT_TO_POINTER (TRUE
));
320 path
= g_file_get_path (file
);
321 fd
= g_open (path
, O_PATH
| O_CLOEXEC
);
325 g_task_report_new_error (NULL
, callback
, user_data
, NULL
,
326 G_IO_ERROR
, g_io_error_from_errno (errsv
),
327 "OpenURI portal is not available");
331 #ifndef HAVE_O_CLOEXEC
332 fcntl (fd
, F_SETFD
, FD_CLOEXEC
);
334 fd_list
= g_unix_fd_list_new_from_array (&fd
, 1);
338 gxdp_open_uri_call_open_file (openuri
,
339 parent_window
? parent_window
: "",
340 g_variant_new ("h", fd_id
),
344 task
? open_call_done
: NULL
,
346 g_object_unref (fd_list
);
351 gxdp_open_uri_call_open_uri (openuri
,
352 parent_window
? parent_window
: "",
356 task
? open_call_done
: NULL
,
360 g_object_unref (file
);
364 g_openuri_portal_open_uri_finish (GAsyncResult
*result
,
367 return g_task_propagate_boolean (G_TASK (result
), error
);