1 /*
2 * Copyright (C) 2019, Matthias Clasen
3 *
4 * This file is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as
6 * published by the Free Software Foundation, version 3.0 of the
7 * License.
8 *
9 * This file is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * SPDX-License-Identifier: LGPL-3.0-only
18 */
19
20 #include "config.h"
21
22 #include "trash.h"
23
24 #define GNU_SOURCE 1
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29
30 #include <glib/gstdio.h>
31 #include <gio/gunixfdlist.h>
32
33 #include "portal-private.h"
34
35 typedef struct {
36 XdpPortal *portal;
37 char *path;
38 GTask *task;
39 } TrashCall;
40
41 static void
trash_call_free(TrashCall * call)42 trash_call_free (TrashCall *call)
43 {
44 g_object_unref (call->portal);
45 g_object_unref (call->task);
46 g_free (call->path);
47
48 g_free (call);
49 }
50
51 #ifndef O_PATH
52 #define O_PATH 0
53 #endif
54
55 static void
file_trashed(GObject * bus,GAsyncResult * result,gpointer data)56 file_trashed (GObject *bus,
57 GAsyncResult *result,
58 gpointer data)
59 {
60 GError *error = NULL;
61 g_autoptr(GVariant) ret = NULL;
62 TrashCall *call = data;
63
64 ret = g_dbus_connection_call_with_unix_fd_list_finish (G_DBUS_CONNECTION (bus),
65 NULL,
66 result,
67 &error);
68 if (error)
69 g_task_return_error (call->task, error);
70 else
71 {
72 guint retval;
73
74 g_variant_get (ret, "(u)", &retval);
75
76 if (retval == 1)
77 g_task_return_boolean (call->task, TRUE);
78 else
79 g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to trash");
80 }
81
82 trash_call_free (call);
83 }
84
85 static void
trash_file(TrashCall * call)86 trash_file (TrashCall *call)
87 {
88 g_autoptr(GUnixFDList) fd_list = NULL;
89 int fd, fd_in;
90 GCancellable *cancellable;
91
92 fd = g_open (call->path, O_PATH | O_CLOEXEC);
93 if (fd == -1)
94 {
95 g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to open '%s'", call->path);
96 trash_call_free (call);
97 return;
98 }
99
100 fd_list = g_unix_fd_list_new_from_array (&fd, 1);
101 fd = -1;
102 fd_in = 0;
103
104 cancellable = g_task_get_cancellable (call->task);
105
106 g_dbus_connection_call_with_unix_fd_list (call->portal->bus,
107 PORTAL_BUS_NAME,
108 PORTAL_OBJECT_PATH,
109 "org.freedesktop.portal.Trash",
110 "TrashFile",
111 g_variant_new ("(h)", fd_in),
112 NULL,
113 G_DBUS_CALL_FLAGS_NONE,
114 -1,
115 fd_list,
116 cancellable,
117 file_trashed,
118 call);
119
120 }
121
122 /**
123 * xdp_portal_trash_file:
124 * @portal: a [class@Portal]
125 * @path: the path for a local file
126 * @cancellable: (nullable): optional [class@Gio.Cancellable]
127 * @callback: (scope async): a callback to call when the request is done
128 * @data: (closure): data to pass to @callback
129 *
130 * Sends the file at @path to the trash can.
131 */
132 void
xdp_portal_trash_file(XdpPortal * portal,const char * path,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer data)133 xdp_portal_trash_file (XdpPortal *portal,
134 const char *path,
135 GCancellable *cancellable,
136 GAsyncReadyCallback callback,
137 gpointer data)
138
139 {
140 TrashCall *call;
141
142 g_return_if_fail (XDP_IS_PORTAL (portal));
143 g_return_if_fail (path != NULL);
144
145 call = g_new0 (TrashCall, 1);
146 call->portal = g_object_ref (portal);
147 call->path = g_strdup (path);
148 call->task = g_task_new (portal, cancellable, callback, data);
149 g_task_set_source_tag (call->task, xdp_portal_trash_file);
150
151 trash_file (call);
152 }
153
154 /**
155 * xdp_portal_trash_file_finish:
156 * @portal: a [class@Portal]
157 * @result: a [iface@Gio.AsyncResult]
158 * @error: return location for an error
159 *
160 * Finishes the trash-file request, and returns
161 * the result in the form of a boolean.
162 *
163 * Returns: `TRUE` if the call succeeded
164 */
165 gboolean
xdp_portal_trash_file_finish(XdpPortal * portal,GAsyncResult * result,GError ** error)166 xdp_portal_trash_file_finish (XdpPortal *portal,
167 GAsyncResult *result,
168 GError **error)
169 {
170 g_return_val_if_fail (XDP_IS_PORTAL (portal), FALSE);
171 g_return_val_if_fail (g_task_is_valid (result, portal), FALSE);
172 g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == xdp_portal_trash_file, FALSE);
173
174 return g_task_propagate_boolean (G_TASK (result), error);
175 }
176