Branch data Line data Source code
1 : : /* GIO - GLib Input, Output and Streaming Library
2 : : *
3 : : * Copyright 2017 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General Public
18 : : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : */
20 : :
21 : : #include "config.h"
22 : :
23 : : #include <sys/stat.h>
24 : : #include <fcntl.h>
25 : : #include <errno.h>
26 : : #include <string.h>
27 : :
28 : : #include "gopenuriportal.h"
29 : : #include "xdp-dbus.h"
30 : : #include "gstdio.h"
31 : :
32 : : #ifdef G_OS_UNIX
33 : : #include "gunixfdlist.h"
34 : : #endif
35 : :
36 : : #ifndef O_CLOEXEC
37 : : #define O_CLOEXEC 0
38 : : #else
39 : : #define HAVE_O_CLOEXEC 1
40 : : #endif
41 : :
42 : : gboolean
43 : 2 : g_openuri_portal_open_file (GFile *file,
44 : : const char *parent_window,
45 : : const char *startup_id,
46 : : GError **error)
47 : : {
48 : : GXdpOpenURI *openuri;
49 : : GVariantBuilder opt_builder;
50 : : gboolean res;
51 : :
52 : 2 : openuri = gxdp_open_uri_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
53 : : G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
54 : : G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
55 : : G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION,
56 : : "org.freedesktop.portal.Desktop",
57 : : "/org/freedesktop/portal/desktop",
58 : : NULL,
59 : : error);
60 : :
61 : 2 : if (openuri == NULL)
62 : : {
63 : 0 : g_prefix_error (error, "Failed to create OpenURI proxy: ");
64 : 0 : return FALSE;
65 : : }
66 : :
67 : 2 : g_variant_builder_init_static (&opt_builder, G_VARIANT_TYPE_VARDICT);
68 : :
69 : 2 : if (startup_id)
70 : 2 : g_variant_builder_add (&opt_builder, "{sv}",
71 : : "activation_token",
72 : : g_variant_new_string (startup_id));
73 : :
74 : 2 : if (g_file_is_native (file))
75 : : {
76 : 1 : char *path = NULL;
77 : 1 : GUnixFDList *fd_list = NULL;
78 : : int fd, fd_id, errsv;
79 : :
80 : 1 : path = g_file_get_path (file);
81 : :
82 : 1 : fd = g_open (path, O_RDONLY | O_CLOEXEC);
83 : 1 : errsv = errno;
84 : 1 : if (fd == -1)
85 : : {
86 : 0 : g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
87 : : "Failed to open ā%sā: %s", path, g_strerror (errsv));
88 : 0 : g_free (path);
89 : 0 : g_variant_builder_clear (&opt_builder);
90 : 0 : return FALSE;
91 : : }
92 : :
93 : : #ifndef HAVE_O_CLOEXEC
94 : : fcntl (fd, F_SETFD, FD_CLOEXEC);
95 : : #endif
96 : 1 : fd_list = g_unix_fd_list_new_from_array (&fd, 1);
97 : 1 : fd = -1;
98 : 1 : fd_id = 0;
99 : :
100 : 1 : res = gxdp_open_uri_call_open_file_sync (openuri,
101 : : parent_window ? parent_window : "",
102 : : g_variant_new ("h", fd_id),
103 : : g_variant_builder_end (&opt_builder),
104 : : fd_list,
105 : : NULL,
106 : : NULL,
107 : : NULL,
108 : : error);
109 : 1 : g_free (path);
110 : 1 : g_object_unref (fd_list);
111 : : }
112 : : else
113 : : {
114 : 1 : char *uri = NULL;
115 : :
116 : 1 : uri = g_file_get_uri (file);
117 : :
118 : 1 : res = gxdp_open_uri_call_open_uri_sync (openuri,
119 : : parent_window ? parent_window : "",
120 : : uri,
121 : : g_variant_builder_end (&opt_builder),
122 : : NULL,
123 : : NULL,
124 : : error);
125 : 1 : g_free (uri);
126 : : }
127 : :
128 : 2 : g_prefix_error (error, "Failed to call OpenURI portal: ");
129 : :
130 : 2 : g_clear_object (&openuri);
131 : :
132 : 2 : return res;
133 : : }
134 : :
135 : : enum {
136 : : XDG_DESKTOP_PORTAL_SUCCESS = 0,
137 : : XDG_DESKTOP_PORTAL_CANCELLED = 1,
138 : : XDG_DESKTOP_PORTAL_FAILED = 2
139 : : };
140 : :
141 : : typedef struct
142 : : {
143 : : GXdpOpenURI *proxy;
144 : : char *response_handle;
145 : : unsigned int response_signal_id;
146 : : gboolean open_file;
147 : : } CallData;
148 : :
149 : : static CallData *
150 : 2 : call_data_new (void)
151 : : {
152 : 2 : return g_new0 (CallData, 1);
153 : : }
154 : :
155 : : static void
156 : 2 : call_data_free (gpointer data)
157 : : {
158 : 2 : CallData *call = data;
159 : :
160 : 2 : g_assert (call->response_signal_id == 0);
161 : 2 : g_clear_object (&call->proxy);
162 : 2 : g_clear_pointer (&call->response_handle, g_free);
163 : 2 : g_free_sized (data, sizeof (CallData));
164 : 2 : }
165 : :
166 : : static void
167 : 2 : response_received (GDBusConnection *connection,
168 : : const char *sender_name,
169 : : const char *object_path,
170 : : const char *interface_name,
171 : : const char *signal_name,
172 : : GVariant *parameters,
173 : : gpointer user_data)
174 : : {
175 : 2 : GTask *task = user_data;
176 : : CallData *call_data;
177 : : guint32 response;
178 : :
179 : 2 : call_data = g_task_get_task_data (task);
180 : 2 : g_dbus_connection_signal_unsubscribe (connection, g_steal_handle_id (&call_data->response_signal_id));
181 : :
182 : 2 : g_variant_get (parameters, "(u@a{sv})", &response, NULL);
183 : :
184 : 2 : switch (response)
185 : : {
186 : 2 : case XDG_DESKTOP_PORTAL_SUCCESS:
187 : 2 : g_task_return_boolean (task, TRUE);
188 : 2 : break;
189 : 0 : case XDG_DESKTOP_PORTAL_CANCELLED:
190 : 0 : g_task_return_new_error_literal (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Launch cancelled");
191 : 0 : break;
192 : 0 : case XDG_DESKTOP_PORTAL_FAILED:
193 : : default:
194 : 0 : g_task_return_new_error_literal (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Launch failed");
195 : 0 : break;
196 : : }
197 : :
198 : 2 : g_object_unref (task);
199 : 2 : }
200 : :
201 : : static void
202 : 2 : open_call_done (GObject *source,
203 : : GAsyncResult *result,
204 : : gpointer user_data)
205 : : {
206 : 2 : GXdpOpenURI *openuri = GXDP_OPEN_URI (source);
207 : : GDBusConnection *connection;
208 : 2 : GTask *task = user_data;
209 : : CallData *call_data;
210 : 2 : GError *error = NULL;
211 : : gboolean res;
212 : 2 : char *path = NULL;
213 : :
214 : 2 : call_data = g_task_get_task_data (task);
215 : 2 : connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (openuri));
216 : :
217 : 2 : if (call_data->open_file)
218 : 1 : res = gxdp_open_uri_call_open_file_finish (openuri, &path, NULL, result, &error);
219 : : else
220 : 1 : res = gxdp_open_uri_call_open_uri_finish (openuri, &path, result, &error);
221 : :
222 : 2 : if (!res)
223 : : {
224 : 0 : g_task_return_error (task, error);
225 : 0 : g_object_unref (task);
226 : 0 : g_free (path);
227 : 0 : return;
228 : : }
229 : :
230 : 2 : if (g_strcmp0 (call_data->response_handle, path) != 0)
231 : : {
232 : : guint signal_id;
233 : :
234 : 0 : g_dbus_connection_signal_unsubscribe (connection, g_steal_handle_id (&call_data->response_signal_id));
235 : :
236 : 0 : signal_id = g_dbus_connection_signal_subscribe (connection,
237 : : "org.freedesktop.portal.Desktop",
238 : : "org.freedesktop.portal.Request",
239 : : "Response",
240 : : path,
241 : : NULL,
242 : : G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
243 : : response_received,
244 : : task,
245 : : NULL);
246 : 0 : g_clear_pointer (&call_data->response_handle, g_free);
247 : 0 : call_data->response_signal_id = g_steal_handle_id (&signal_id);
248 : 0 : call_data->response_handle = g_steal_pointer (&path);
249 : : }
250 : :
251 : 2 : g_free (path);
252 : : }
253 : :
254 : : void
255 : 2 : g_openuri_portal_open_file_async (GFile *file,
256 : : const char *parent_window,
257 : : const char *startup_id,
258 : : GCancellable *cancellable,
259 : : GAsyncReadyCallback callback,
260 : : gpointer user_data)
261 : : {
262 : : CallData *call_data;
263 : 2 : GError *error = NULL;
264 : : GDBusConnection *connection;
265 : : GXdpOpenURI *openuri;
266 : : GTask *task;
267 : 2 : GVariant *opts = NULL;
268 : : int i;
269 : : guint signal_id;
270 : :
271 : 2 : openuri = gxdp_open_uri_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
272 : : G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
273 : : G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
274 : : G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION,
275 : : "org.freedesktop.portal.Desktop",
276 : : "/org/freedesktop/portal/desktop",
277 : : NULL,
278 : : &error);
279 : :
280 : 2 : if (openuri == NULL)
281 : : {
282 : 0 : g_prefix_error (&error, "Failed to create OpenURI proxy: ");
283 : 0 : g_task_report_error (NULL, callback, user_data, NULL, error);
284 : 0 : return;
285 : : }
286 : :
287 : 2 : connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (openuri));
288 : :
289 : 2 : if (callback)
290 : : {
291 : : GVariantBuilder opt_builder;
292 : : char *token;
293 : : char *sender;
294 : : char *handle;
295 : :
296 : 2 : task = g_task_new (NULL, cancellable, callback, user_data);
297 : :
298 : 2 : token = g_strdup_printf ("gio%d", g_random_int_range (0, G_MAXINT));
299 : 2 : sender = g_strdup (g_dbus_connection_get_unique_name (connection) + 1);
300 : 9 : for (i = 0; sender[i]; i++)
301 : 7 : if (sender[i] == '.')
302 : 2 : sender[i] = '_';
303 : :
304 : 2 : handle = g_strdup_printf ("/org/freedesktop/portal/desktop/request/%s/%s", sender, token);
305 : 2 : g_free (sender);
306 : :
307 : 2 : signal_id = g_dbus_connection_signal_subscribe (connection,
308 : : "org.freedesktop.portal.Desktop",
309 : : "org.freedesktop.portal.Request",
310 : : "Response",
311 : : handle,
312 : : NULL,
313 : : G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
314 : : response_received,
315 : : task,
316 : : NULL);
317 : :
318 : 2 : g_variant_builder_init_static (&opt_builder, G_VARIANT_TYPE_VARDICT);
319 : 2 : g_variant_builder_add (&opt_builder, "{sv}", "handle_token", g_variant_new_string (token));
320 : 2 : g_free (token);
321 : :
322 : 2 : if (startup_id)
323 : 2 : g_variant_builder_add (&opt_builder, "{sv}",
324 : : "activation_token",
325 : : g_variant_new_string (startup_id));
326 : :
327 : 2 : opts = g_variant_builder_end (&opt_builder);
328 : :
329 : 2 : call_data = call_data_new ();
330 : 2 : call_data->proxy = g_object_ref (openuri);
331 : 2 : call_data->response_handle = g_steal_pointer (&handle);
332 : 2 : call_data->response_signal_id = g_steal_handle_id (&signal_id);
333 : 2 : g_task_set_task_data (task, call_data, call_data_free);
334 : : }
335 : : else
336 : : {
337 : 0 : call_data = NULL;
338 : 0 : task = NULL;
339 : : }
340 : :
341 : 2 : if (g_file_is_native (file))
342 : : {
343 : 1 : char *path = NULL;
344 : 1 : GUnixFDList *fd_list = NULL;
345 : : int fd, fd_id, errsv;
346 : :
347 : 1 : if (call_data)
348 : 1 : call_data->open_file = TRUE;
349 : :
350 : 1 : path = g_file_get_path (file);
351 : 1 : fd = g_open (path, O_RDONLY | O_CLOEXEC);
352 : 1 : errsv = errno;
353 : 1 : if (fd == -1)
354 : : {
355 : 0 : g_clear_object (&task);
356 : 0 : g_task_report_new_error (NULL, callback, user_data, NULL,
357 : 0 : G_IO_ERROR, g_io_error_from_errno (errsv),
358 : : "Failed to open ā%sā: %s", path, g_strerror (errsv));
359 : 0 : g_clear_object (&openuri);
360 : 0 : return;
361 : : }
362 : :
363 : : #ifndef HAVE_O_CLOEXEC
364 : : fcntl (fd, F_SETFD, FD_CLOEXEC);
365 : : #endif
366 : 1 : fd_list = g_unix_fd_list_new_from_array (&fd, 1);
367 : 1 : fd = -1;
368 : 1 : fd_id = 0;
369 : :
370 : 1 : gxdp_open_uri_call_open_file (openuri,
371 : : parent_window ? parent_window : "",
372 : : g_variant_new ("h", fd_id),
373 : : opts,
374 : : fd_list,
375 : : cancellable,
376 : 1 : task ? open_call_done : NULL,
377 : : task);
378 : 1 : g_object_unref (fd_list);
379 : 1 : g_free (path);
380 : : }
381 : : else
382 : : {
383 : 1 : char *uri = NULL;
384 : :
385 : 1 : uri = g_file_get_uri (file);
386 : :
387 : 1 : gxdp_open_uri_call_open_uri (openuri,
388 : : parent_window ? parent_window : "",
389 : : uri,
390 : : opts,
391 : : cancellable,
392 : 1 : task ? open_call_done : NULL,
393 : : task);
394 : 1 : g_free (uri);
395 : : }
396 : :
397 : 2 : g_clear_object (&openuri);
398 : : }
399 : :
400 : : gboolean
401 : 2 : g_openuri_portal_open_file_finish (GAsyncResult *result,
402 : : GError **error)
403 : : {
404 : 2 : return g_task_propagate_boolean (G_TASK (result), error);
405 : : }
|