Coverage Report

Created: 2025-07-23 08:13

/src/pango/subprojects/glib/gio/gopenuriportal.c
Line
Count
Source (jump to first uncovered line)
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
g_openuri_portal_open_file (GFile       *file,
44
                            const char  *parent_window,
45
                            const char  *startup_id,
46
                            GError     **error)
47
0
{
48
0
  GXdpOpenURI *openuri;
49
0
  GVariantBuilder opt_builder;
50
0
  gboolean res;
51
52
0
  openuri = gxdp_open_uri_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
53
0
                                                  G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
54
0
                                                  G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
55
0
                                                  G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION,
56
0
                                                  "org.freedesktop.portal.Desktop",
57
0
                                                  "/org/freedesktop/portal/desktop",
58
0
                                                  NULL,
59
0
                                                  error);
60
61
0
  if (openuri == NULL)
62
0
    {
63
0
      g_prefix_error (error, "Failed to create OpenURI proxy: ");
64
0
      return FALSE;
65
0
    }
66
67
0
  g_variant_builder_init_static (&opt_builder, G_VARIANT_TYPE_VARDICT);
68
69
0
  if (startup_id)
70
0
    g_variant_builder_add (&opt_builder, "{sv}",
71
0
                           "activation_token",
72
0
                           g_variant_new_string (startup_id));
73
74
0
  if (g_file_is_native (file))
75
0
    {
76
0
      char *path = NULL;
77
0
      GUnixFDList *fd_list = NULL;
78
0
      int fd, fd_id, errsv;
79
80
0
      path = g_file_get_path (file);
81
82
0
      fd = g_open (path, O_RDONLY | O_CLOEXEC);
83
0
      errsv = errno;
84
0
      if (fd == -1)
85
0
        {
86
0
          g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
87
0
                       "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
0
        }
92
93
#ifndef HAVE_O_CLOEXEC
94
      fcntl (fd, F_SETFD, FD_CLOEXEC);
95
#endif
96
0
      fd_list = g_unix_fd_list_new_from_array (&fd, 1);
97
0
      fd = -1;
98
0
      fd_id = 0;
99
100
0
      res = gxdp_open_uri_call_open_file_sync (openuri,
101
0
                                               parent_window ? parent_window : "",
102
0
                                               g_variant_new ("h", fd_id),
103
0
                                               g_variant_builder_end (&opt_builder),
104
0
                                               fd_list,
105
0
                                               NULL,
106
0
                                               NULL,
107
0
                                               NULL,
108
0
                                               error);
109
0
      g_free (path);
110
0
      g_object_unref (fd_list);
111
0
    }
112
0
  else
113
0
    {
114
0
      char *uri = NULL;
115
116
0
      uri = g_file_get_uri (file);
117
118
0
      res = gxdp_open_uri_call_open_uri_sync (openuri,
119
0
                                              parent_window ? parent_window : "",
120
0
                                              uri,
121
0
                                              g_variant_builder_end (&opt_builder),
122
0
                                              NULL,
123
0
                                              NULL,
124
0
                                              error);
125
0
      g_free (uri);
126
0
    }
127
128
0
  g_prefix_error (error, "Failed to call OpenURI portal: ");
129
130
0
  g_clear_object (&openuri);
131
132
0
  return res;
133
0
}
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
call_data_new (void)
151
0
{
152
0
  return g_new0 (CallData, 1);
153
0
}
154
155
static void
156
call_data_free (gpointer data)
157
0
{
158
0
  CallData *call = data;
159
160
0
  g_assert (call->response_signal_id == 0);
161
0
  g_clear_object (&call->proxy);
162
0
  g_clear_pointer (&call->response_handle, g_free);
163
0
  g_free_sized (data, sizeof (CallData));
164
0
}
165
166
static void
167
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
0
{
175
0
  GTask *task = user_data;
176
0
  CallData *call_data;
177
0
  guint32 response;
178
179
0
  call_data = g_task_get_task_data (task);
180
0
  g_dbus_connection_signal_unsubscribe (connection, g_steal_handle_id (&call_data->response_signal_id));
181
182
0
  g_variant_get (parameters, "(u@a{sv})", &response, NULL);
183
184
0
  switch (response)
185
0
    {
186
0
    case XDG_DESKTOP_PORTAL_SUCCESS:
187
0
      g_task_return_boolean (task, TRUE);
188
0
      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
0
    default:
194
0
      g_task_return_new_error_literal (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Launch failed");
195
0
      break;
196
0
    }
197
198
0
  g_object_unref (task);
199
0
}
200
201
static void
202
open_call_done (GObject      *source,
203
                GAsyncResult *result,
204
                gpointer      user_data)
205
0
{
206
0
  GXdpOpenURI *openuri = GXDP_OPEN_URI (source);
207
0
  GDBusConnection *connection;
208
0
  GTask *task = user_data;
209
0
  CallData *call_data;
210
0
  GError *error = NULL;
211
0
  gboolean res;
212
0
  char *path = NULL;
213
214
0
  call_data = g_task_get_task_data (task);
215
0
  connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (openuri));
216
217
0
  if (call_data->open_file)
218
0
    res = gxdp_open_uri_call_open_file_finish (openuri, &path, NULL, result, &error);
219
0
  else
220
0
    res = gxdp_open_uri_call_open_uri_finish (openuri, &path, result, &error);
221
222
0
  if (!res)
223
0
    {
224
0
      g_task_return_error (task, error);
225
0
      g_object_unref (task);
226
0
      g_free (path);
227
0
      return;
228
0
    }
229
230
0
  if (g_strcmp0 (call_data->response_handle, path) != 0)
231
0
    {
232
0
      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
0
                                                      "org.freedesktop.portal.Desktop",
238
0
                                                      "org.freedesktop.portal.Request",
239
0
                                                      "Response",
240
0
                                                      path,
241
0
                                                      NULL,
242
0
                                                      G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
243
0
                                                      response_received,
244
0
                                                      task,
245
0
                                                      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
0
    }
250
251
0
  g_free (path);
252
0
}
253
254
void
255
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
0
{
262
0
  CallData *call_data;
263
0
  GError *error = NULL;
264
0
  GDBusConnection *connection;
265
0
  GXdpOpenURI *openuri;
266
0
  GTask *task;
267
0
  GVariant *opts = NULL;
268
0
  int i;
269
0
  guint signal_id;
270
271
0
  openuri = gxdp_open_uri_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
272
0
                                                  G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
273
0
                                                  G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
274
0
                                                  G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION,
275
0
                                                  "org.freedesktop.portal.Desktop",
276
0
                                                  "/org/freedesktop/portal/desktop",
277
0
                                                  NULL,
278
0
                                                  &error);
279
280
0
  if (openuri == NULL)
281
0
    {
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
0
    }
286
287
0
  connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (openuri));
288
289
0
  if (callback)
290
0
    {
291
0
      GVariantBuilder opt_builder;
292
0
      char *token;
293
0
      char *sender;
294
0
      char *handle;
295
296
0
      task = g_task_new (NULL, cancellable, callback, user_data);
297
298
0
      token = g_strdup_printf ("gio%d", g_random_int_range (0, G_MAXINT));
299
0
      sender = g_strdup (g_dbus_connection_get_unique_name (connection) + 1);
300
0
      for (i = 0; sender[i]; i++)
301
0
        if (sender[i] == '.')
302
0
          sender[i] = '_';
303
304
0
      handle = g_strdup_printf ("/org/freedesktop/portal/desktop/request/%s/%s", sender, token);
305
0
      g_free (sender);
306
307
0
      signal_id = g_dbus_connection_signal_subscribe (connection,
308
0
                                                      "org.freedesktop.portal.Desktop",
309
0
                                                      "org.freedesktop.portal.Request",
310
0
                                                      "Response",
311
0
                                                      handle,
312
0
                                                      NULL,
313
0
                                                      G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
314
0
                                                      response_received,
315
0
                                                      task,
316
0
                                                      NULL);
317
318
0
      g_variant_builder_init_static (&opt_builder, G_VARIANT_TYPE_VARDICT);
319
0
      g_variant_builder_add (&opt_builder, "{sv}", "handle_token", g_variant_new_string (token));
320
0
      g_free (token);
321
322
0
      if (startup_id)
323
0
        g_variant_builder_add (&opt_builder, "{sv}",
324
0
                               "activation_token",
325
0
                               g_variant_new_string (startup_id));
326
327
0
      opts = g_variant_builder_end (&opt_builder);
328
329
0
      call_data = call_data_new ();
330
0
      call_data->proxy = g_object_ref (openuri);
331
0
      call_data->response_handle = g_steal_pointer (&handle);
332
0
      call_data->response_signal_id = g_steal_handle_id (&signal_id);
333
0
      g_task_set_task_data (task, call_data, call_data_free);
334
0
    }
335
0
  else
336
0
    {
337
0
      call_data = NULL;
338
0
      task = NULL;
339
0
    }
340
341
0
  if (g_file_is_native (file))
342
0
    {
343
0
      char *path = NULL;
344
0
      GUnixFDList *fd_list = NULL;
345
0
      int fd, fd_id, errsv;
346
347
0
      if (call_data)
348
0
        call_data->open_file = TRUE;
349
350
0
      path = g_file_get_path (file);
351
0
      fd = g_open (path, O_RDONLY | O_CLOEXEC);
352
0
      errsv = errno;
353
0
      if (fd == -1)
354
0
        {
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
0
                                   "Failed to open ā€˜%s’: %s", path, g_strerror (errsv));
359
0
          g_clear_object (&openuri);
360
0
          return;
361
0
        }
362
363
#ifndef HAVE_O_CLOEXEC
364
      fcntl (fd, F_SETFD, FD_CLOEXEC);
365
#endif
366
0
      fd_list = g_unix_fd_list_new_from_array (&fd, 1);
367
0
      fd = -1;
368
0
      fd_id = 0;
369
370
0
      gxdp_open_uri_call_open_file (openuri,
371
0
                                    parent_window ? parent_window : "",
372
0
                                    g_variant_new ("h", fd_id),
373
0
                                    opts,
374
0
                                    fd_list,
375
0
                                    cancellable,
376
0
                                    task ? open_call_done : NULL,
377
0
                                    task);
378
0
      g_object_unref (fd_list);
379
0
      g_free (path);
380
0
    }
381
0
  else
382
0
    {
383
0
      char *uri = NULL;
384
385
0
      uri = g_file_get_uri (file);
386
387
0
      gxdp_open_uri_call_open_uri (openuri,
388
0
                                   parent_window ? parent_window : "",
389
0
                                   uri,
390
0
                                   opts,
391
0
                                   cancellable,
392
0
                                   task ? open_call_done : NULL,
393
0
                                   task);
394
0
      g_free (uri);
395
0
    }
396
397
0
  g_clear_object (&openuri);
398
0
}
399
400
gboolean
401
g_openuri_portal_open_file_finish (GAsyncResult  *result,
402
                                   GError       **error)
403
0
{
404
0
  return g_task_propagate_boolean (G_TASK (result), error);
405
0
}