💾 Archived View for gemini.rmf-dev.com › repo › Vaati › mz › files › d620cd9acbb98fff3d67864910c0424… captured on 2023-03-20 at 18:35:54. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
0 /*
1 * Copyright (c) 2023 RMF <rawmonk@firemail.cc>
2 *
3 * Permission to use, copy, modify, and distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 */
15 #ifdef __linux__
16 #define _GNU_SOURCE
17 #else
18 #define _BSD_SOURCE
19 #endif
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <pwd.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <fts.h>
30 #include "client.h"
31 #include "strlcpy.h"
32 #include "file.h"
33 #include "view.h"
34 #include "trash.h"
35 #include "util.h"
36
37 #define TRASH "/.trash"
38 #define ID_LENGTH 32
39
40 static int gethome(char *buf, size_t length) {
41
42 struct passwd *pw;
43 char *home;
44 int fd;
45
46 home = getenv("HOME");
47 if (home) {
48 fd = open(home, O_DIRECTORY);
49 if (fd > -1) {
50 close(fd);
51 return strlcpy(buf, home, length);
52 }
53 }
54
55 pw = getpwuid(geteuid());
56 if (!pw) return -1;
57 fd = open(pw->pw_dir, O_DIRECTORY);
58 if (fd < 0) {
59 close(fd);
60 return -1;
61 }
62 return strlcpy(buf, pw->pw_dir, length);
63 }
64
65 static int trash_path(char *path, size_t length) {
66
67 char buf[1024];
68
69 int len = gethome(buf, length);
70 if (len == -1 || len + sizeof(TRASH) >= length) goto clean;
71
72 len = strlcpy(path, buf, length);
73 strlcpy(&path[len], TRASH, sizeof(path) - length);
74
75 if (!strncmp(path, buf, length)) goto clean;
76
77 return 0;
78 clean: /* make sure we're not deleting a whole folder by accident */
79 memset(path, 0, length);
80 strlcpy(path, "/nonexistent/folder", sizeof(path));
81 return -1;
82 }
83
84 int trash_init() {
85 char path[PATH_MAX];
86 int home, trash;
87
88 if (gethome(V(path)) == -1) return -1;
89
90 home = open(path, O_DIRECTORY);
91 if (home < 0) goto fail;
92
93 trash = openat(home, ".trash", O_DIRECTORY);
94 if (trash > -1) {
95 close(home);
96 return trash;
97 }
98
99 if (mkdirat(home, ".trash", 0700)) goto fail;
100
101 trash = openat(home, ".trash", O_DIRECTORY);
102 if (trash < 0) goto fail;
103
104 return trash;
105 fail:
106 if (home > -1)
107 close(home);
108 return -1;
109 }
110
111 int trash_send(int fd, char *path, char *name) {
112
113 char buf[PATH_MAX * 2], id[ID_LENGTH + 1];
114 int info, len, error;
115
116 info = openat(client.trash, "info", O_WRONLY|O_CREAT|O_APPEND, 0600);
117 if (info < 0)
118 return -1;
119
120 do {
121 size_t i;
122 int try;
123
124 i = 0;
125 while (i < sizeof(id) - 1) {
126 id[i] = 'a' + rand() % 26;
127 i++;
128 }
129 id[sizeof(id) - 1] = '\0';
130
131 /* check if there's not already a file with that id */
132 try = openat(client.trash, id, O_RDONLY);
133 if (try < 0) break;
134 close(i);
135 } while (1);
136
137 trash_path(V(buf));
138 error = file_move(path, fd, name, client.trash, buf, id);
139 if (!error) {
140 len = snprintf(V(buf), "%s %s/%s\n", id, path, name);
141 error = -(write(info, buf, len) != len);
142 }
143 close(info);
144
145 return error;
146 }
147
148 int trash_clear() {
149
150 char path[PATH_MAX], cmd[PATH_MAX * 2];
151
152 if (trash_path(V(path))) return -1;
153 snprintf(V(cmd), "rm -r %s", path);
154 if (system(cmd)) return -1;
155
156 close(client.trash);
157 client.trash = trash_init();
158 if (client.trash < 0) return -1;
159
160 return 0;
161 }
162
163 int trash_restore(struct view *view) {
164
165 size_t i;
166 char path[PATH_MAX];
167 int error = 0;
168
169 if (view->fd != TRASH_FD) {
170 errno = EINVAL;
171 return -1;
172 }
173
174 if (trash_path(V(path))) return -1;
175
176 i = 0;
177 while (i < view->length) {
178
179 char src[PATH_MAX];
180 char id[ID_LENGTH + 1];
181 size_t j = i++;
182 int fd;
183
184 if (!view->entries[j].selected) continue;
185 memcpy(id, &((char*)view->other)[j * ID_LENGTH], ID_LENGTH);
186 id[ID_LENGTH] = '\0';
187 snprintf(V(src), "%s/%s", path, id);
188
189 /* check if file exist before using rename */
190 fd = open(view->entries[j].name, 0);
191 if (fd > -1) {
192 close(fd);
193 errno = EEXIST;
194 error = -1;
195 continue;
196 }
197
198 if (rename(src, view->entries[j].name)) {
199 char *name;
200 if (errno != EXDEV) return -1;
201 sstrcpy(src, view->entries[j].name);
202 name = strrchr(src, '/');
203 if (!name) return -1;
204 *name = '\0';
205 name++;
206 fd = open(src, O_DIRECTORY);
207 if (fd < 0) return -1;
208 if (file_move(path, client.trash, id, fd, src, name))
209 return -1;
210 }
211 view->entries[j].selected = -1;
212 }
213 return error;
214 }
215
216 int trash_refresh(struct view *view) {
217
218 void *next, *prev;
219 size_t i;
220 int fd, rewrite;
221
222 i = 0;
223 rewrite = 0;
224 while (i < view->length) {
225 switch (view->entries[i].selected) {
226 case -1:
227 rewrite = 1;
228 break;
229 case 1:
230 view->entries[i].selected = 0;
231 break;
232 }
233 i++;
234 }
235 if (!rewrite) return 0;
236
237 /* rewrite info file */
238 fd = openat(client.trash, "info", O_CREAT|O_WRONLY|O_TRUNC);
239 if (!fd) return -1;
240
241 i = 0;
242 while (i < view->length) {
243
244 char c;
245 size_t j = i++;
246
247 if (view->entries[j].selected == -1) continue;
248
249 write(fd, &((char*)view->other)[j * ID_LENGTH], ID_LENGTH);
250 c = ' ';
251 write(fd, &c, 1);
252 write(fd, view->entries[j].name,
253 strnlen(V(view->entries[j].name)));
254 c = '\n';
255 write(fd, &c, 1);
256 }
257 close(fd);
258
259 free(view->other);
260 free(view->entries);
261
262 next = view->next;
263 prev = view->prev;
264 trash_view(view);
265 view->next = next;
266 view->prev = prev;
267
268 return 0;
269 }
270
271 int trash_view(struct view* view) {
272
273 int fd;
274 size_t i;
275 char buf[PATH_MAX * 2];
276
277 PZERO(view);
278 sstrcpy(view->path, "Trash");
279 view->fd = TRASH_FD;
280
281 fd = openat(client.trash, "info", O_RDONLY);
282 if (fd < 0) return 0;
283
284 i = 0;
285 while (i < sizeof(buf)) {
286
287 void *ptr;
288 char id[ID_LENGTH];
289 char c;
290 size_t j;
291
292 j = read(fd, V(id));
293 if (!j) { /* success : end of file */
294 close(fd);
295 return 0;
296 }
297 if (j != sizeof(id)) break;
298
299 j = 0;
300 while (j < sizeof(id)) {
301 if (id[j] > 'z' || id[j] < 'a') break;
302 j++;
303 }
304 if (j != sizeof(id)) break;
305
306 if (read(fd, &c, 1) != 1 || c != ' ') break;
307
308 j = 0;
309 while (j < sizeof(buf)) {
310 if (read(fd, &c, 1) != 1) break;
311 if (c == '\n') {
312 buf[j] = 0;
313 break;
314 }
315 buf[j] = c;
316 j++;
317 }
318
319 ptr = realloc(view->entries, sizeof(struct entry) * (i + 1));
320 if (!ptr) break;
321 view->entries = ptr;
322 RZERO(view->entries[i]);
323
324 sstrcpy(view->entries[i].name, buf);
325 view->entries[i].type = 0;
326 view->length = i + 1;
327
328 ptr = realloc(view->other, (i + 1) * ID_LENGTH + 1);
329 if (!ptr) break;
330 view->other = ptr;
331 memcpy(&((char*)view->other)[i * ID_LENGTH], V(id));
332
333 i++;
334 }
335
336 free(view->other);
337 free(view->entries);
338 close(fd);
339 return -1;
340 }
341