💾 Archived View for gemini.rmf-dev.com › repo › Vaati › mz › files › 762de32970ed22078aaef00f843b65d… captured on 2023-12-28 at 15:51:58. 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 <dirent.h>
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <pwd.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <fts.h>
31 #include "client.h"
32 #include "strlcpy.h"
33 #include "file.h"
34 #include "view.h"
35 #include "trash.h"
36 #include "util.h"
37
38 #define TRASH "/.trash"
39 #define ID_LENGTH 32
40
41 static int gethome(char *buf, size_t length) {
42
43 struct passwd *pw;
44 char *home;
45 int fd;
46
47 home = getenv("HOME");
48 if (home) {
49 fd = open(home, O_DIRECTORY);
50 if (fd > -1) {
51 close(fd);
52 return strlcpy(buf, home, length);
53 }
54 }
55
56 pw = getpwuid(geteuid());
57 if (!pw) return -1;
58 fd = open(pw->pw_dir, O_DIRECTORY);
59 if (fd < 0) return -1;
60 close(fd);
61 return strlcpy(buf, pw->pw_dir, length);
62 }
63
64 static int trash_path(char *path, size_t length) {
65
66 char buf[1024];
67
68 int len = gethome(buf, length);
69 if (len == -1 || len + sizeof(TRASH) >= length) goto clean;
70
71 len = strlcpy(path, buf, length);
72 strlcpy(&path[len], TRASH, sizeof(path) - length);
73
74 if (!strncmp(path, buf, length)) goto clean;
75
76 return 0;
77 clean: /* make sure we're not deleting a whole folder by accident */
78 memset(path, 0, length);
79 strlcpy(path, "/nonexistent/folder", sizeof(path));
80 return -1;
81 }
82
83 int trash_init() {
84 char path[PATH_MAX];
85 int home, trash;
86
87 if (gethome(V(path)) == -1) return -1;
88
89 home = open(path, O_DIRECTORY);
90 if (home < 0) goto fail;
91
92 trash = openat(home, ".trash", O_DIRECTORY);
93 if (trash > -1) {
94 close(home);
95 return trash;
96 }
97
98 if (mkdirat(home, ".trash", 0700)) goto fail;
99
100 trash = openat(home, ".trash", O_DIRECTORY);
101 if (trash < 0) goto fail;
102
103 close(home);
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_rawpath(struct view *view, char *out, size_t length) {
164
165 char path[PATH_MAX];
166
167 if (view->fd != TRASH_FD) return -1;
168 if (trash_path(V(path))) return -1;
169 if (snprintf(out, length, "%s/%s", path,
170 (char*)SELECTED(view).other) >= (int)length)
171 return -1;
172 return 0;
173 }
174
175 int trash_restore(struct view *view) {
176
177 size_t i;
178 char path[PATH_MAX];
179 int error = 0;
180
181 if (view->fd != TRASH_FD) {
182 errno = EINVAL;
183 return -1;
184 }
185
186 if (trash_path(V(path))) return -1;
187
188 i = 0;
189 while (i < view->length) {
190
191 char src[PATH_MAX];
192 char *id;
193 size_t j = i++;
194 int fd;
195
196 if (!view->entries[j].selected) continue;
197 id = view->entries[j].other;
198 snprintf(V(src), "%s/%s", path, id);
199
200 /* check if file exist before using rename */
201 fd = open(view->entries[j].name, 0);
202 if (fd > -1) {
203 close(fd);
204 errno = EEXIST;
205 error = -1;
206 continue;
207 }
208
209 if (rename(src, view->entries[j].name)) {
210 char *name;
211 int ret;
212 if (errno != EXDEV) return -1;
213 STRCPY(src, view->entries[j].name);
214 name = strrchr(src, '/');
215 if (!name) return -1;
216 *name = '\0';
217 name++;
218 fd = open(src, O_DIRECTORY);
219 if (fd < 0) return -1;
220 ret = file_move(path, client.trash, id, fd, src, name);
221 close(fd);
222 if (ret < 0) return -1;
223 }
224 view->entries[j].selected = -1;
225 }
226 return error;
227 }
228
229 int trash_refresh(struct view *view) {
230
231 void *next, *prev;
232 size_t i;
233 int fd, rewrite;
234
235 i = 0;
236 rewrite = 0;
237 while (i < view->length) {
238 switch (view->entries[i].selected) {
239 case -1:
240 rewrite = 1;
241 break;
242 case 1:
243 view->entries[i].selected = 0;
244 break;
245 }
246 i++;
247 }
248 if (!rewrite) return 0;
249
250 /* rewrite info file */
251 fd = openat(client.trash, "info", O_CREAT|O_WRONLY|O_TRUNC);
252 if (!fd) return -1;
253
254 i = 0;
255 while (i < view->length) {
256
257 char c;
258 size_t j = i++;
259
260 if (view->entries[j].selected == -1) continue;
261
262 write(fd, view->entries[j].other, ID_LENGTH);
263 c = ' ';
264 write(fd, &c, 1);
265 write(fd, view->entries[j].name,
266 strnlen(V(view->entries[j].name)));
267 c = '\n';
268 write(fd, &c, 1);
269 }
270 close(fd);
271
272 i = 0;
273 while (i < view->length) {
274 free(view->entries[i].other);
275 }
276
277 free(view->entries);
278
279 next = view->next;
280 prev = view->prev;
281 trash_view(view);
282 view->next = next;
283 view->prev = prev;
284
285 return 0;
286 }
287
288 int trash_view(struct view* view) {
289
290 int fd;
291 size_t i;
292 char buf[PATH_MAX * 2];
293
294 PZERO(view);
295 STRCPY(view->path, "Trash");
296 view->fd = TRASH_FD;
297
298 fd = openat(client.trash, "info", O_RDONLY);
299 if (fd < 0) return 0;
300
301 i = 0;
302 while (1) {
303
304 void *ptr;
305 char id[ID_LENGTH];
306 char c;
307 size_t j;
308
309 j = read(fd, V(id));
310 if (!j) { /* success : end of file */
311 close(fd);
312 qsort(view->entries, view->length,
313 sizeof(struct entry), file_sort);
314 return 0;
315 }
316 if (j != sizeof(id)) break;
317
318 j = 0;
319 while (j < sizeof(id)) {
320 if (id[j] > 'z' || id[j] < 'a') break;
321 j++;
322 }
323 if (j != sizeof(id)) break;
324
325 if (read(fd, &c, 1) != 1 || c != ' ') break;
326
327 j = 0;
328 while (j < sizeof(buf)) {
329 if (read(fd, &c, 1) != 1) break;
330 if (c == '\n') {
331 buf[j] = 0;
332 break;
333 }
334 buf[j] = c;
335 j++;
336 }
337
338 ptr = realloc(view->entries, sizeof(struct entry) * (i + 1));
339 if (!ptr) break;
340 view->entries = ptr;
341 RZERO(view->entries[i]);
342
343 STRCPY(view->entries[i].name, buf);
344 view->entries[i].type = DT_REG;
345 {
346 struct stat s;
347 char path[ID_LENGTH + 1];
348 memcpy(path, id, ID_LENGTH);
349 path[ID_LENGTH] = 0;
350 view->entries[i].type =
351 fstatat(client.trash, path, &s, 0) ?
352 DT_REG : (S_ISDIR(s.st_mode) ?
353 DT_DIR : DT_REG);
354 }
355 view->length = i + 1;
356
357 view->entries[i].other = calloc(ID_LENGTH + 1, 1);
358 if (!view->entries[i].other) break;
359 memcpy(view->entries[i].other, V(id));
360
361 i++;
362 }
363
364 for (i = 0; i < view->length; i++)
365 free(view->entries[i].other);
366 free(view->entries);
367 close(fd);
368 return -1;
369 }
370