Improve some sieve-related translations
[claws.git] / src / plugins / archive / libarchive_archive.c
blob4ecc6a01d40c23a6c6af9aa69e8e3c7357296f3f
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2018 Michael Rasmussen and the Claws Mail Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program 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
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #include "claws-features.h"
22 #endif
24 #include <glib.h>
25 #include <glib/gi18n.h>
26 #include <glib/gstdio.h>
28 #include "libarchive_archive.h"
30 #ifndef _TEST
31 # include "archiver.h"
32 # include "utils.h"
33 # include "mainwindow.h"
34 # include "folder.h"
35 #endif
37 #include <sys/types.h>
38 #include <sys/stat.h>
40 #include <archive.h>
41 #include <archive_entry.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <dirent.h>
48 #include <libgen.h>
50 #define READ_BLOCK_SIZE 10240
52 struct file_info {
53 char* path;
54 char* name;
57 static GSList* msg_trash_list = NULL;
58 static GSList* file_list = NULL;
59 static gboolean stop_action = FALSE;
61 #ifdef _TEST
62 static int permissions = 0;
63 #endif
65 static void free_msg_trash(MsgTrash* trash) {
66 if (trash) {
67 gchar *name = folder_item_get_name(trash->item);
69 debug_print("Freeing files in %s\n", name);
70 g_free(name);
71 if (trash->msgs) {
72 g_slist_free(trash->msgs);
74 g_free(trash);
78 MsgTrash* new_msg_trash(FolderItem* item) {
79 MsgTrash* msg_trash;
80 FolderType type;
82 g_return_val_if_fail(item != NULL, NULL);
84 /* FolderType must be F_MH, F_MBOX, F_MAILDIR or F_IMAP */
85 type = item->folder->klass->type;
86 if (!(type == F_MH || type == F_MBOX ||
87 type == F_MAILDIR || type == F_IMAP))
88 return NULL;
89 msg_trash = g_new0(MsgTrash, 1);
90 msg_trash->item = item;
91 msg_trash->msgs = NULL;
92 msg_trash_list = g_slist_prepend(msg_trash_list, msg_trash);
94 return msg_trash;
97 void archive_free_archived_files() {
98 MsgTrash* mt = NULL;
99 gint res;
100 GSList* l = NULL;
102 for (l = msg_trash_list; l; l = g_slist_next(l)) {
103 gchar *name;
105 mt = (MsgTrash *) l->data;
106 name = folder_item_get_name(mt->item);
107 debug_print("Trashing messages in folder: %s\n",
108 name);
109 g_free(name);
110 res = folder_item_remove_msgs(mt->item, mt->msgs);
111 debug_print("Result was %d\n", res);
112 free_msg_trash(mt);
114 g_slist_free(msg_trash_list);
115 msg_trash_list = NULL;
118 void archive_add_msg_mark(MsgTrash* trash, MsgInfo* msg) {
119 g_return_if_fail(trash != NULL || msg != NULL);
120 debug_print("Marking msg #%d for removal\n", msg->msgnum);
121 trash->msgs = g_slist_prepend(trash->msgs, msg);
124 static void free_all(GDate* date, gchar** parts) {
125 if (date)
126 g_date_free(date);
127 if (parts)
128 g_strfreev(parts);
131 static gboolean is_iso_string(gchar** items) {
132 int i = -1;
133 gchar* item;
135 while (*items) {
136 i++;
137 item = *items++;
138 debug_print("Date part %d: %s\n", i, item);
139 switch(i) {
140 case 0:
141 if (strlen(item) != 4)
142 return FALSE;
143 break;
144 case 1:
145 case 2:
146 if (strlen(item) != 2)
147 return FALSE;
148 break;
149 default:
150 return FALSE;
153 debug_print("Leaving\n");
154 return (i == 2);
157 static GDate* iso2GDate(const gchar* date) {
158 GDate* gdate;
159 gchar** parts;
160 int i;
162 g_return_val_if_fail(date != NULL, NULL);
164 gdate = g_date_new();
165 parts = g_strsplit(date, "-", 3);
166 if (!parts)
167 return NULL;
168 if (! is_iso_string(parts)) {
169 free_all(gdate, parts);
170 return NULL;
172 for (i = 0; i < 3; i++) {
173 int t = atoi(parts[i]);
174 switch (i) {
175 case 0:
176 if (t < 1 || t > 9999) {
177 free_all(gdate, parts);
178 return NULL;
180 g_date_set_year(gdate, t);
181 break;
182 case 1:
183 if (t < 1 || t > 12) {
184 free_all(gdate, parts);
185 return NULL;
187 g_date_set_month(gdate, t);
188 break;
189 case 2:
190 if (t < 1 || t > 31) {
191 free_all(gdate, parts);
192 return NULL;
194 g_date_set_day(gdate, t);
195 break;
198 g_strfreev(parts);
199 return gdate;
202 gboolean before_date(time_t msg_mtime, const gchar* before) {
203 gchar* pos = NULL;
204 GDate* date;
205 GDate* file_t;
206 gboolean res;
208 debug_print("Cut-off date: %s\n", before);
209 if ((date = iso2GDate(before)) == NULL) {
210 g_warning("bad date format: %s", before);
211 return FALSE;
214 file_t = g_date_new();
215 g_date_set_time_t(file_t, msg_mtime);
217 if (debug_get_mode()) {
218 pos = g_new0(char, 100);
219 g_date_strftime(pos, 100, "%F", file_t);
220 fprintf(stderr, "File date: %s\n", pos);
221 g_free(pos);
224 if (! g_date_valid(file_t)) {
225 g_warning("invalid msg date");
226 return FALSE;
229 res = (g_date_compare(file_t, date) >= 0) ? FALSE : TRUE;
230 g_date_free(file_t);
231 return res;
234 static void archive_free_file_info(struct file_info* file) {
235 if (! file)
236 return;
237 if (file->path)
238 g_free(file->path);
239 if (file->name)
240 g_free(file->name);
241 g_free(file);
242 file = NULL;
245 void stop_archiving() {
246 debug_print("stop action set to true\n");
247 stop_action = TRUE;
250 void archive_free_file_list(gboolean md5, gboolean rename) {
251 struct file_info* file = NULL;
252 gchar* path = NULL;
254 debug_print("freeing file list\n");
255 if (! file_list)
256 return;
257 while (file_list) {
258 file = (struct file_info *) file_list->data;
259 if (!rename && md5 && g_str_has_suffix(file->name, ".md5")) {
260 path = g_strdup_printf("%s/%s", file->path, file->name);
261 debug_print("unlinking %s\n", path);
262 if (g_unlink(path) < 0)
263 FILE_OP_ERROR(path, "g_unlink");
264 g_free(path);
266 if (rename) {
267 path = g_strdup_printf("%s/%s", file->path, file->name);
268 debug_print("unlinking %s\n", path);
269 if (g_unlink(path) < 0)
270 FILE_OP_ERROR(path, "g_unlink");
271 g_free(path);
273 archive_free_file_info(file);
274 file_list->data = NULL;
275 file_list = g_slist_next(file_list);
277 if (file_list) {
278 g_slist_free(file_list);
279 file_list = NULL;
283 static struct file_info* archive_new_file_info() {
284 struct file_info* new_file_info = malloc(sizeof(struct file_info));
286 new_file_info->path = NULL;
287 new_file_info->name = NULL;
288 return new_file_info;
291 static void archive_add_to_list(struct file_info* file) {
292 if (! file)
293 return;
294 file_list = g_slist_prepend(file_list, (gpointer) file);
297 static gchar* strip_leading_dot_slash(gchar* path) {
298 if (path && strlen(path) > 1 && path[0] == '.' && path[1] == '/')
299 return g_strdup(&(path[2]));
301 return g_strdup(path);
304 static gchar* get_full_path(struct file_info* file) {
305 char* path = malloc(PATH_MAX);
307 if (file->path && *(file->path))
308 sprintf(path, "%s/%s", file->path, file->name);
309 else
310 sprintf(path, "%s", file->name);
311 return path;
314 #ifdef _TEST
315 static gchar* strip_leading_slash(gchar* path) {
316 gchar* stripped = path;
317 gchar* result = NULL;
319 if (stripped && stripped[0] == '/') {
320 ++stripped;
321 result = g_strdup(stripped);
323 else
324 result = g_strdup(path);
325 return result;
328 static int archive_get_permissions() {
329 return permissions;
333 void archive_set_permissions(int perm) {
334 permissions = perm;
337 static int archive_copy_data(struct archive* in, struct archive* out) {
338 const void* buf;
339 size_t size;
340 off_t offset;
341 int res = ARCHIVE_OK;
343 while (res == ARCHIVE_OK) {
344 res = archive_read_data_block(in, &buf, &size, &offset);
345 if (res == ARCHIVE_OK) {
346 res = archive_write_data_block(out, buf, size, offset);
349 return (res == ARCHIVE_EOF) ? ARCHIVE_OK : res;
351 #endif
353 void archive_add_file(gchar* path) {
354 struct file_info* file;
355 gchar* filename = NULL;
357 g_return_if_fail(path != NULL);
359 #ifndef _TEST
360 debug_print("add %s to list\n", path);
361 #endif
362 filename = g_strrstr_len(path, strlen(path), "/");
363 if (! filename)
364 g_warning("no filename in path '%s'", path);
365 g_return_if_fail(filename != NULL);
367 filename++;
368 file = archive_new_file_info();
369 file->name = g_strdup(filename);
370 file->path = strip_leading_dot_slash(dirname(path));
371 archive_add_to_list(file);
374 GSList* archive_get_file_list() {
375 return file_list;
378 #ifdef _TEST
379 const gchar* archive_extract(const char* archive_name, int flags) {
380 struct archive* in;
381 struct archive* out;
382 struct archive_entry* entry;
383 int res = ARCHIVE_OK;
384 gchar* buf = NULL;
385 const char* result == NULL;
387 g_return_val_if_fail(archive_name != NULL, ARCHIVE_FATAL);
389 fprintf(stdout, "%s: extracting\n", archive_name);
390 in = archive_read_new();
391 if ((res = archive_read_support_format_tar(in)) == ARCHIVE_OK) {
392 if ((res = archive_read_support_compression_gzip(in)) == ARCHIVE_OK) {
393 #if ARCHIVE_VERSION_NUMBER < 3000000
394 if ((res = archive_read_open_file(
395 #else
396 if ((res = archive_read_open_filename(
397 #endif
398 in, archive_name, READ_BLOCK_SIZE)) != ARCHIVE_OK) {
399 g_warning("%s: %s", archive_name, archive_error_string(in));
400 result = archive_error_string(in);
402 else {
403 out = archive_write_disk_new();
404 if ((res = archive_write_disk_set_options(
405 out, flags)) == ARCHIVE_OK) {
406 res = archive_read_next_header(in, &entry);
407 while (res == ARCHIVE_OK) {
408 res = archive_write_header(out, entry);
409 if (res != ARCHIVE_OK) {
410 g_warning("%s", archive_error_string(out));
411 /* skip this file an continue */
412 res = ARCHIVE_OK;
414 else {
415 res = archive_copy_data(in, out);
416 if (res != ARCHIVE_OK) {
417 g_warning("%s", archive_error_string(in));
418 /* skip this file an continue */
419 res = ARCHIVE_OK;
421 else
422 res = archive_read_next_header(in, &entry);
425 if (res == ARCHIVE_EOF)
426 res = ARCHIVE_OK;
427 if (res != ARCHIVE_OK) {
428 gchar *e = archive_error_string(in);
429 g_warning("%s: %s", archive_name, e? e: "unknown error");
430 result = e;
433 else
434 result = archive_error_string(out);
435 archive_read_close(in);
437 #if ARCHIVE_VERSION_NUMBER < 3000000
438 archive_read_finish(in);
439 #else
440 archive_read_free(in);
441 #endif
443 else
444 result = archive_error_string(in);
446 else
447 result = archive_error_string(in);
448 return result;
450 #endif
452 const gchar* archive_create(const char* archive_name, GSList* files,
453 COMPRESS_METHOD method, ARCHIVE_FORMAT format) {
454 struct archive* arch;
456 #ifndef _TEST
457 gint num = 0;
458 gint total = g_slist_length (files);
459 #endif
461 g_return_val_if_fail(files != NULL, "No files for archiving");
463 debug_print("File: %s\n", archive_name);
464 arch = archive_write_new();
465 switch (method) {
466 case GZIP:
467 #if ARCHIVE_VERSION_NUMBER < 3000000
468 if (archive_write_set_compression_gzip(arch) != ARCHIVE_OK)
469 #else
470 if (archive_write_add_filter_gzip(arch) != ARCHIVE_OK)
471 #endif
472 return archive_error_string(arch);
473 break;
474 case BZIP2:
475 #if ARCHIVE_VERSION_NUMBER < 3000000
476 if (archive_write_set_compression_bzip2(arch) != ARCHIVE_OK)
477 #else
478 if (archive_write_add_filter_bzip2(arch) != ARCHIVE_OK)
479 #endif
480 return archive_error_string(arch);
481 break;
482 case COMPRESS:
483 #if ARCHIVE_VERSION_NUMBER < 3000000
484 if (archive_write_set_compression_compress(arch) != ARCHIVE_OK)
485 #else
486 if (archive_write_add_filter_compress(arch) != ARCHIVE_OK)
487 #endif
488 return archive_error_string(arch);
489 break;
490 #if ARCHIVE_VERSION_NUMBER >= 2006990
491 case LZMA:
492 #if ARCHIVE_VERSION_NUMBER < 3000000
493 if (archive_write_set_compression_lzma(arch) != ARCHIVE_OK)
494 #else
495 if (archive_write_add_filter_lzma(arch) != ARCHIVE_OK)
496 #endif
497 return archive_error_string(arch);
498 break;
499 case XZ:
500 #if ARCHIVE_VERSION_NUMBER < 3000000
501 if (archive_write_set_compression_xz(arch) != ARCHIVE_OK)
502 #else
503 if (archive_write_add_filter_xz(arch) != ARCHIVE_OK)
504 #endif
505 return archive_error_string(arch);
506 break;
507 #endif
508 #if ARCHIVE_VERSION_NUMBER >= 3000000
509 case LZIP:
510 if (archive_write_add_filter_lzip(arch) != ARCHIVE_OK)
511 return archive_error_string(arch);
512 break;
513 #endif
514 #if ARCHIVE_VERSION_NUMBER >= 3001000
515 case LRZIP:
516 if (archive_write_add_filter_lrzip(arch) != ARCHIVE_OK)
517 return archive_error_string(arch);
518 break;
519 case LZOP:
520 if (archive_write_add_filter_lzop(arch) != ARCHIVE_OK)
521 return archive_error_string(arch);
522 break;
523 case GRZIP:
524 if (archive_write_add_filter_grzip(arch) != ARCHIVE_OK)
525 return archive_error_string(arch);
526 break;
527 #endif
528 #if ARCHIVE_VERSION_NUMBER >= 3001900
529 case LZ4:
530 if (archive_write_add_filter_lz4(arch) != ARCHIVE_OK)
531 return archive_error_string(arch);
532 break;
533 #endif
534 case NO_COMPRESS:
535 #if ARCHIVE_VERSION_NUMBER < 3000000
536 if (archive_write_set_compression_none(arch) != ARCHIVE_OK)
537 #else
538 if (archive_write_add_filter_none(arch) != ARCHIVE_OK)
539 #endif
540 return archive_error_string(arch);
541 break;
543 switch (format) {
544 case TAR:
545 if (archive_write_set_format_ustar(arch) != ARCHIVE_OK)
546 return archive_error_string(arch);
547 break;
548 case SHAR:
549 if (archive_write_set_format_shar(arch) != ARCHIVE_OK)
550 return archive_error_string(arch);
551 break;
552 case PAX:
553 if (archive_write_set_format_pax(arch) != ARCHIVE_OK)
554 return archive_error_string(arch);
555 break;
556 case CPIO:
557 if (archive_write_set_format_cpio(arch) != ARCHIVE_OK)
558 return archive_error_string(arch);
559 break;
560 case NO_FORMAT:
561 return "Missing archive format";
563 #if ARCHIVE_VERSION_NUMBER < 3000000
564 if (archive_write_open_file(arch, archive_name) != ARCHIVE_OK)
565 #else
566 if (archive_write_open_filename(arch, archive_name) != ARCHIVE_OK)
567 #endif
568 return archive_error_string(arch);
570 while (files && ! stop_action) {
571 struct file_info* file;
572 gchar* filename = NULL;
574 #ifndef _TEST
575 set_progress_print_all(num++, total, 30);
576 #endif
577 file = (struct file_info *) files->data;
578 if (!file)
579 continue;
580 filename = get_full_path(file);
581 /* libarchive will crash if instructed to add archive to it self */
582 if (g_utf8_collate(archive_name, filename) == 0) {
583 g_warning("%s: not dumping to '%s'", archive_name, filename);
584 #ifndef _TEST
585 debug_print("%s: not dumping to '%s'\n", archive_name, filename);
586 #endif
588 else {
589 struct archive_entry* entry;
590 char* buf = NULL;
591 ssize_t len;
592 GError* err = NULL;
593 GStatBuf st;
594 int fd;
595 gchar* msg = NULL;
597 #ifndef _TEST
598 debug_print("Adding: %s\n", filename);
599 msg = g_strdup_printf("%s", filename);
600 set_progress_file_label(msg);
601 g_free(msg);
602 #endif
603 if ((fd = g_open(filename, O_RDONLY, 0)) == -1) {
604 FILE_OP_ERROR(filename, "g_open");
606 else {
607 if (g_stat(filename, &st) == -1) {
608 FILE_OP_ERROR(filename, "g_stat");
609 } else {
610 entry = archive_entry_new();
611 archive_entry_copy_stat(entry, &st);
612 archive_entry_set_pathname(entry, filename);
613 if (S_ISLNK(st.st_mode)) {
615 buf = g_file_read_link(filename, &err);
616 if (err) {
617 FILE_OP_ERROR(filename, "g_file_read_link");
618 g_clear_error(&err);
619 } else {
620 archive_entry_set_symlink(entry, buf);
621 g_free(buf);
622 archive_entry_set_size(entry, 0);
623 archive_write_header(arch, entry);
626 else {
627 if (archive_write_header(arch, entry) != ARCHIVE_OK)
628 g_warning("%s", archive_error_string(arch));
629 if ((buf = malloc(READ_BLOCK_SIZE)) != NULL) {
630 len = read(fd, buf, READ_BLOCK_SIZE);
631 while (len > 0) {
632 if (archive_write_data(arch, buf, len) == -1)
633 g_warning("%s", archive_error_string(arch));
634 memset(buf, 0, READ_BLOCK_SIZE);
635 len = read(fd, buf, READ_BLOCK_SIZE);
637 g_free(buf);
640 archive_entry_free(entry);
642 if (!g_close(fd, &err) || err) {
643 FILE_OP_ERROR(filename, "g_close");
644 if (err)
645 g_clear_error(&err);
649 g_free(filename);
650 files = g_slist_next(files);
652 #ifndef _TEST
653 if (stop_action)
654 unlink(archive_name);
655 stop_action = FALSE;
656 #endif
657 archive_write_close(arch);
658 #if ARCHIVE_VERSION_NUMBER < 3000000
659 archive_write_finish(arch);
660 #else
661 archive_write_free(arch);
662 #endif
663 return NULL;
666 #ifdef _TEST
667 void archive_scan_folder(const char* dir) {
668 GStatBuf st;
669 DIR* root;
670 struct dirent* ent;
671 gchar cwd[PATH_MAX];
672 gchar path[PATH_MAX];
674 getcwd(cwd, PATH_MAX);
676 if (g_stat(dir, &st) == -1)
677 return;
678 if (! S_ISDIR(st.st_mode))
679 return;
680 if (!(root = opendir(dir)))
681 return;
682 chdir(dir);
684 while ((ent = readdir(root)) != NULL) {
685 if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
686 continue;
687 if (g_stat(ent->d_name, &st) == -1) {
688 FILE_OP_ERROR(filename, "g_stat");
689 continue;
691 sprintf(path, "%s/%s", dir, ent->d_name);
692 if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
693 archive_add_file(path);
695 else if (S_ISDIR(st.st_mode)) {
696 archive_scan_folder(path);
699 chdir(cwd);
700 closedir(root);
703 int main(int argc, char** argv) {
704 char* archive = NULL;
705 char buf[PATH_MAX];
706 int pid;
707 int opt;
708 int perm = ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME |
709 ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_SECURE_SYMLINKS;
710 gchar cwd[PATH_MAX];
711 gboolean remove = FALSE;
712 const char *p = NULL;
713 int res;
715 getcwd(cwd, PATH_MAX);
717 while (*++argv && **argv == '-') {
718 p = *argv + 1;
720 while ((opt = *p++) != '\0') {
721 switch(opt) {
722 case 'a':
723 if (*p != '\0')
724 archive = (char *) p;
725 else
726 archive = *++argv;
727 p += strlen(p);
728 break;
729 case 'r':
730 remove = TRUE;
731 break;
735 if (! archive) {
736 fprintf(stderr, "Missing archive name!\n");
737 return EXIT_FAILURE;
739 if (!*argv) {
740 fprintf(stderr, "Expected arguments after options!\n");
741 return EXIT_FAILURE;
744 while (*argv) {
745 archive_scan_folder(*argv++);
746 res = archive_create(archive, file_list);
747 if (res != ARCHIVE_OK) {
748 fprintf(stderr, "%s: Creating archive failed\n", archive);
749 return EXIT_FAILURE;
752 pid = (int) getpid();
753 sprintf(buf, "/tmp/%d", pid);
754 fprintf(stdout, "Creating: %s\n", buf);
755 mkdir(buf, 0700);
756 chdir(buf);
757 if (strcmp(dirname(archive), ".") == 0)
758 sprintf(buf, "%s/%s", cwd, basename(archive));
759 else
760 sprintf(buf, "%s", archive);
761 archive_extract(buf, perm);
762 chdir(cwd);
763 if (remove) {
764 sprintf(buf, "rm -rf /tmp/%d", pid);
765 fprintf(stdout, "Executing: %s\n", buf);
766 system(buf);
768 archive_free_list(file_list);
769 return EXIT_SUCCESS;
771 #endif
773 void archiver_set_tooltip(GtkWidget* widget, gchar* text) {
774 gtk_widget_set_tooltip_text(widget, text);
775 g_free(text);