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 | |
40 | static gboolean |
41 | get_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 | |
78 | out: |
79 | g_clear_object (&connection); |
80 | return *documents != NULL; |
81 | } |
82 | |
83 | /* Flags accepted by org.freedesktop.portal.Documents.AddFull */ |
84 | enum { |
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 | |
91 | GList * |
92 | g_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 * = 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 | |
206 | out: |
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 | |