1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright 2018, Red Hat, 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 "gtrashportal.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 #ifndef O_PATH
41 #define O_PATH 0
42 #endif
43 
44 static GXdpTrash *
ensure_trash_portal(void)45 ensure_trash_portal (void)
46 {
47   static GXdpTrash *trash = NULL;
48 
49   if (g_once_init_enter (&trash))
50     {
51       GDBusConnection *connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
52       GXdpTrash *proxy = NULL;
53 
54       if (connection != NULL)
55         {
56           proxy = gxdp_trash_proxy_new_sync (connection, 0,
57                                              "org.freedesktop.portal.Desktop",
58                                              "/org/freedesktop/portal/desktop",
59                                              NULL, NULL);
60           g_object_unref (connection);
61         }
62 
63       g_once_init_leave (&trash, proxy);
64     }
65 
66   return trash;
67 }
68 
69 gboolean
g_trash_portal_trash_file(GFile * file,GError ** error)70 g_trash_portal_trash_file (GFile   *file,
71                            GError **error)
72 {
73   char *path = NULL;
74   GUnixFDList *fd_list = NULL;
75   int fd, fd_in, errsv;
76   gboolean ret = FALSE;
77   guint portal_result = 0;
78   GXdpTrash *proxy;
79 
80   proxy = ensure_trash_portal ();
81   if (proxy == NULL)
82     {
83       g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
84                    "Trash portal is not available");
85       goto out;
86     }
87 
88   path = g_file_get_path (file);
89 
90   fd = g_open (path, O_RDWR | O_CLOEXEC | O_NOFOLLOW);
91   if (fd == -1 && errno == EISDIR)
92     /* If it is a directory, fall back to O_PATH */
93     fd = g_open (path, O_PATH | O_CLOEXEC | O_RDONLY | O_NOFOLLOW);
94 
95   errsv = errno;
96 
97   if (fd == -1)
98     {
99       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
100                    "Failed to open %s", path);
101       goto out;
102     }
103 
104 #ifndef HAVE_O_CLOEXEC
105   fcntl (fd, F_SETFD, FD_CLOEXEC);
106 #endif
107 
108   fd_list = g_unix_fd_list_new ();
109   fd_in = g_unix_fd_list_append (fd_list, fd, error);
110   g_close (fd, NULL);
111 
112   if (fd_in == -1)
113     goto out;
114 
115   ret = gxdp_trash_call_trash_file_sync (proxy,
116                                          g_variant_new_handle (fd_in),
117                                          fd_list,
118                                          &portal_result,
119                                          NULL,
120                                          NULL,
121                                          error);
122 
123   if (ret && portal_result != 1)
124     {
125       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Trash portal failed on %s", path);
126       ret = FALSE;
127     }
128 
129  out:
130   g_clear_object (&fd_list);
131   g_free (path);
132 
133   return ret;
134 }
135