1/* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright 2016 Endless Mobile, Inc.
4 *
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.
9 *
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.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "config.h"
20
21#include <sys/stat.h>
22#include <fcntl.h>
23#include <errno.h>
24#include <string.h>
25
26#include "gdocumentportal.h"
27#include "xdp-dbus.h"
28#include "gstdio.h"
29
30#ifdef G_OS_UNIX
31#include "gunixfdlist.h"
32#endif
33
34#ifndef O_CLOEXEC
35#define O_CLOEXEC 0
36#else
37#define HAVE_O_CLOEXEC 1
38#endif
39
40static gboolean
41get_document_portal (GXdpDocuments **documents,
42 char **documents_mountpoint,
43 GError **error)
44{
45 GDBusConnection *connection = NULL;
46
47 *documents = NULL;
48 *documents_mountpoint = NULL;
49
50 connection = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, error);
51 if (connection == NULL)
52 {
53 g_prefix_error (err: error, format: "Cannot connect to session bus when initializing document portal: ");
54 goto out;
55 }
56
57 *documents = gxdp_documents_proxy_new_sync (connection,
58 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
59 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
60 "org.freedesktop.portal.Documents",
61 "/org/freedesktop/portal/documents",
62 NULL, error);
63 if (*documents == NULL)
64 {
65 g_prefix_error (err: error, format: "Cannot create document portal proxy: ");
66 goto out;
67 }
68
69 if (!gxdp_documents_call_get_mount_point_sync (*documents,
70 documents_mountpoint,
71 NULL, error))
72 {
73 g_clear_object (documents);
74 g_prefix_error (err: error, format: "Cannot get document portal mount point: ");
75 goto out;
76 }
77
78out:
79 g_clear_object (&connection);
80 return *documents != NULL;
81}
82
83/* Flags accepted by org.freedesktop.portal.Documents.AddFull */
84enum {
85 XDP_ADD_FLAGS_REUSE_EXISTING = (1 << 0),
86 XDP_ADD_FLAGS_PERSISTENT = (1 << 1),
87 XDP_ADD_FLAGS_AS_NEEDED_BY_APP = (1 << 2),
88 XDP_ADD_FLAGS_FLAGS_ALL = ((1 << 3) - 1)
89};
90
91GList *
92g_document_portal_add_documents (GList *uris,
93 const char *app_id,
94 GError **error)
95{
96 GXdpDocuments *documents = NULL;
97 char *documents_mountpoint = NULL;
98 int length;
99 GList *ruris = NULL;
100 gboolean *as_is;
101 GVariantBuilder builder;
102 GUnixFDList *fd_list = NULL;
103 GList *l;
104 gsize i, j;
105 const char *permissions[] = { "read", "write", NULL };
106 char **doc_ids = NULL;
107 GVariant *extra_out = NULL;
108
109 if (!get_document_portal (&documents, &documents_mountpoint, error))
110 {
111 return NULL;
112 }
113
114 length = g_list_length (list: uris);
115 as_is = g_new0 (gboolean, length);
116
117 g_variant_builder_init (builder: &builder, G_VARIANT_TYPE ("ah"));
118
119 fd_list = g_unix_fd_list_new ();
120 for (l = uris, i = 0; l; l = l->next, i++)
121 {
122 const char *uri = l->data;
123 int idx = -1;
124 char *path = NULL;
125
126 path = g_filename_from_uri (uri, NULL, NULL);
127 if (path != NULL)
128 {
129 int fd;
130
131 fd = g_open (file: path, O_CLOEXEC | O_RDWR);
132 if (fd == -1 && (errno == EACCES || errno == EISDIR))
133 {
134 /* If we don't have write access, fall back to read-only,
135 * and stop requesting the write permission */
136 fd = g_open (file: path, O_CLOEXEC | O_RDONLY);
137 permissions[1] = NULL;
138 }
139 if (fd >= 0)
140 {
141#ifndef HAVE_O_CLOEXEC
142 fcntl (fd, F_SETFD, FD_CLOEXEC);
143#endif
144 idx = g_unix_fd_list_append (list: fd_list, fd, NULL);
145 close (fd: fd);
146 }
147 }
148
149 g_free (mem: path);
150
151 if (idx != -1)
152 g_variant_builder_add (builder: &builder, format_string: "h", idx);
153 else
154 as_is[i] = TRUE;
155 }
156
157 if (g_unix_fd_list_get_length (list: fd_list) > 0)
158 {
159 if (!gxdp_documents_call_add_full_sync (documents,
160 g_variant_builder_end (builder: &builder),
161 XDP_ADD_FLAGS_AS_NEEDED_BY_APP,
162 app_id,
163 permissions,
164 fd_list,
165 &doc_ids,
166 &extra_out,
167 NULL,
168 NULL,
169 error))
170 goto out;
171
172 for (l = uris, i = 0, j = 0; l; l = l->next, i++)
173 {
174 const char *uri = l->data;
175 char *ruri;
176
177 if (as_is[i]) /* use as-is, not a file uri */
178 {
179 ruri = g_strdup (str: uri);
180 }
181 else if (strcmp (s1: doc_ids[j], s2: "") == 0) /* not rewritten */
182 {
183 ruri = g_strdup (str: uri);
184 j++;
185 }
186 else
187 {
188 char *basename = g_path_get_basename (file_name: uri + strlen (s: "file:"));
189 char *doc_path = g_build_filename (first_element: documents_mountpoint, doc_ids[j], basename, NULL);
190 ruri = g_strconcat (string1: "file:", doc_path, NULL);
191 g_free (mem: basename);
192 g_free (mem: doc_path);
193 j++;
194 }
195
196 ruris = g_list_prepend (list: ruris, data: ruri);
197 }
198
199 ruris = g_list_reverse (list: ruris);
200 }
201 else
202 {
203 ruris = g_list_copy_deep (list: uris, func: (GCopyFunc)g_strdup, NULL);
204 }
205
206out:
207 g_clear_object (&documents);
208 g_clear_pointer (&documents_mountpoint, g_free);
209 g_clear_object (&fd_list);
210 g_clear_pointer (&extra_out, g_variant_unref);
211 g_clear_pointer (&doc_ids, g_strfreev);
212 g_free (mem: as_is);
213
214 return ruris;
215}
216

source code of gtk/subprojects/glib/gio/gdocumentportal.c