2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2016 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/>.
21 #include "claws-features.h"
25 #include <glib/gi18n.h>
27 #include "libarchive_archive.h"
30 # include "archiver.h"
32 # include "mainwindow.h"
36 #include <sys/types.h>
40 #include <archive_entry.h>
50 #define READ_BLOCK_SIZE 10240
57 static GSList* msg_trash_list = NULL;
58 static GSList* file_list = NULL;
59 static gboolean stop_action = FALSE;
62 static int permissions = 0;
65 static void free_msg_trash(MsgTrash* trash) {
67 debug_print("Freeing files in %s\n", folder_item_get_name(trash->item));
69 g_slist_free(trash->msgs);
75 MsgTrash* new_msg_trash(FolderItem* item) {
79 g_return_val_if_fail(item != NULL, NULL);
81 /* FolderType must be F_MH, F_MBOX, F_MAILDIR or F_IMAP */
82 type = item->folder->klass->type;
83 if (!(type == F_MH || type == F_MBOX ||
84 type == F_MAILDIR || type == F_IMAP))
86 msg_trash = g_new0(MsgTrash, 1);
87 msg_trash->item = item;
88 msg_trash->msgs = NULL;
89 msg_trash_list = g_slist_prepend(msg_trash_list, msg_trash);
94 void archive_free_archived_files() {
99 for (l = msg_trash_list; l; l = g_slist_next(l)) {
100 mt = (MsgTrash *) l->data;
101 debug_print("Trashing messages in folder: %s\n",
102 folder_item_get_name(mt->item));
103 res = folder_item_remove_msgs(mt->item, mt->msgs);
104 debug_print("Result was %d\n", res);
107 g_slist_free(msg_trash_list);
108 msg_trash_list = NULL;
111 void archive_add_msg_mark(MsgTrash* trash, MsgInfo* msg) {
112 g_return_if_fail(trash != NULL || msg != NULL);
113 debug_print("Marking msg #%d for removal\n", msg->msgnum);
114 trash->msgs = g_slist_prepend(trash->msgs, msg);
117 static void free_all(GDate* date, gchar** parts) {
124 static gboolean is_iso_string(gchar** items) {
131 debug_print("Date part %d: %s\n", i, item);
134 if (strlen(item) != 4)
139 if (strlen(item) != 2)
146 debug_print("Leaving\n");
150 static GDate* iso2GDate(const gchar* date) {
152 gchar** parts = NULL;
155 g_return_val_if_fail(date != NULL, NULL);
157 gdate = g_date_new();
158 parts = g_strsplit(date, "-", 3);
161 if (! is_iso_string(parts))
163 for (i = 0; i < 3; i++) {
164 int t = atoi(parts[i]);
167 if (t < 1 || t > 9999) {
168 free_all(gdate, parts);
171 g_date_set_year(gdate, t);
174 if (t < 1 || t > 12) {
175 free_all(gdate, parts);
178 g_date_set_month(gdate, t);
181 if (t < 1 || t > 31) {
182 free_all(gdate, parts);
185 g_date_set_day(gdate, t);
193 gboolean before_date(time_t msg_mtime, const gchar* before) {
199 debug_print("Cut-off date: %s\n", before);
200 if ((date = iso2GDate(before)) == NULL) {
201 g_warning("Bad date format: %s", before);
205 file_t = g_date_new();
206 g_date_set_time_t(file_t, msg_mtime);
208 if (debug_get_mode()) {
209 pos = g_new0(char, 100);
210 g_date_strftime(pos, 100, "%F", file_t);
211 fprintf(stderr, "File date: %s\n", pos);
215 if (! g_date_valid(file_t)) {
216 g_warning("Invalid msg date");
220 res = (g_date_compare(file_t, date) >= 0) ? FALSE : TRUE;
225 static void archive_free_file_info(struct file_info* file) {
236 void stop_archiving() {
237 debug_print("stop action set to true\n");
241 void archive_free_file_list(gboolean md5, gboolean rename) {
242 struct file_info* file = NULL;
245 debug_print("freeing file list\n");
249 file = (struct file_info *) file_list->data;
250 if (!rename && md5 && g_str_has_suffix(file->name, ".md5")) {
251 path = g_strdup_printf("%s/%s", file->path, file->name);
252 debug_print("unlinking %s\n", path);
257 path = g_strdup_printf("%s/%s", file->path, file->name);
258 debug_print("unlinking %s\n", path);
262 archive_free_file_info(file);
263 file_list->data = NULL;
264 file_list = g_slist_next(file_list);
267 g_slist_free(file_list);
272 static struct file_info* archive_new_file_info() {
273 struct file_info* new_file_info = malloc(sizeof(struct file_info));
275 new_file_info->path = NULL;
276 new_file_info->name = NULL;
277 return new_file_info;
280 static void archive_add_to_list(struct file_info* file) {
283 file_list = g_slist_prepend(file_list, (gpointer) file);
286 static gchar* strip_leading_dot_slash(gchar* path) {
287 if (path && strlen(path) > 1 && path[0] == '.' && path[1] == '/')
288 return g_strdup(&(path[2]));
290 return g_strdup(path);
293 static gchar* get_full_path(struct file_info* file) {
294 char* path = malloc(PATH_MAX);
296 if (file->path && *(file->path))
297 sprintf(path, "%s/%s", file->path, file->name);
299 sprintf(path, "%s", file->name);
304 static gchar* strip_leading_slash(gchar* path) {
305 gchar* stripped = path;
306 gchar* result = NULL;
308 if (stripped && stripped[0] == '/') {
310 result = g_strdup(stripped);
313 result = g_strdup(path);
317 static int archive_get_permissions() {
322 void archive_set_permissions(int perm) {
326 static int archive_copy_data(struct archive* in, struct archive* out) {
330 int res = ARCHIVE_OK;
332 while (res == ARCHIVE_OK) {
333 res = archive_read_data_block(in, &buf, &size, &offset);
334 if (res == ARCHIVE_OK) {
335 res = archive_write_data_block(out, buf, size, offset);
338 return (res == ARCHIVE_EOF) ? ARCHIVE_OK : res;
342 void archive_add_file(gchar* path) {
343 struct file_info* file;
344 gchar* filename = NULL;
346 g_return_if_fail(path != NULL);
349 debug_print("add %s to list\n", path);
351 filename = g_strrstr_len(path, strlen(path), "/");
353 g_warning("no filename in path '%s'", path);
354 g_return_if_fail(filename != NULL);
357 file = archive_new_file_info();
358 file->name = g_strdup(filename);
359 file->path = strip_leading_dot_slash(dirname(path));
360 archive_add_to_list(file);
363 GSList* archive_get_file_list() {
368 const gchar* archive_extract(const char* archive_name, int flags) {
371 struct archive_entry* entry;
372 int res = ARCHIVE_OK;
374 const char* result == NULL;
376 g_return_val_if_fail(archive_name != NULL, ARCHIVE_FATAL);
378 fprintf(stdout, "%s: extracting\n", archive_name);
379 in = archive_read_new();
380 if ((res = archive_read_support_format_tar(in)) == ARCHIVE_OK) {
381 if ((res = archive_read_support_compression_gzip(in)) == ARCHIVE_OK) {
382 #if ARCHIVE_VERSION_NUMBER < 3000000
383 if ((res = archive_read_open_file(
385 if ((res = archive_read_open_filename(
387 in, archive_name, READ_BLOCK_SIZE)) != ARCHIVE_OK) {
388 g_warning("%s: %s", archive_name, archive_error_string(in));
389 result = archive_error_string(in);
392 out = archive_write_disk_new();
393 if ((res = archive_write_disk_set_options(
394 out, flags)) == ARCHIVE_OK) {
395 res = archive_read_next_header(in, &entry);
396 while (res == ARCHIVE_OK) {
397 res = archive_write_header(out, entry);
398 if (res != ARCHIVE_OK) {
399 g_warning("%s", archive_error_string(out));
400 /* skip this file an continue */
404 res = archive_copy_data(in, out);
405 if (res != ARCHIVE_OK) {
406 g_warning("%s", archive_error_string(in));
407 /* skip this file an continue */
411 res = archive_read_next_header(in, &entry);
414 if (res == ARCHIVE_EOF)
416 if (res != ARCHIVE_OK) {
417 gchar *e = archive_error_string(in);
418 g_warning("%s: %s", archive_name, e? e: "unknown error");
423 result = archive_error_string(out);
424 archive_read_close(in);
426 #if ARCHIVE_VERSION_NUMBER < 3000000
427 archive_read_finish(in);
429 archive_read_free(in);
433 result = archive_error_string(in);
436 result = archive_error_string(in);
441 const gchar* archive_create(const char* archive_name, GSList* files,
442 COMPRESS_METHOD method, ARCHIVE_FORMAT format) {
443 struct archive* arch;
444 struct archive_entry* entry;
449 struct file_info* file;
450 gchar* filename = NULL;
455 gint total = g_slist_length (files);
458 g_return_val_if_fail(files != NULL, "No files for archiving");
460 debug_print("File: %s\n", archive_name);
461 arch = archive_write_new();
464 #if ARCHIVE_VERSION_NUMBER < 3000000
465 if (archive_write_set_compression_gzip(arch) != ARCHIVE_OK)
467 if (archive_write_add_filter_gzip(arch) != ARCHIVE_OK)
469 return archive_error_string(arch);
472 #if ARCHIVE_VERSION_NUMBER < 3000000
473 if (archive_write_set_compression_bzip2(arch) != ARCHIVE_OK)
475 if (archive_write_add_filter_bzip2(arch) != ARCHIVE_OK)
477 return archive_error_string(arch);
480 #if ARCHIVE_VERSION_NUMBER < 3000000
481 if (archive_write_set_compression_compress(arch) != ARCHIVE_OK)
483 if (archive_write_add_filter_compress(arch) != ARCHIVE_OK)
485 return archive_error_string(arch);
487 #if ARCHIVE_VERSION_NUMBER >= 2006990
489 #if ARCHIVE_VERSION_NUMBER < 3000000
490 if (archive_write_set_compression_lzma(arch) != ARCHIVE_OK)
492 if (archive_write_add_filter_lzma(arch) != ARCHIVE_OK)
494 return archive_error_string(arch);
497 #if ARCHIVE_VERSION_NUMBER < 3000000
498 if (archive_write_set_compression_xz(arch) != ARCHIVE_OK)
500 if (archive_write_add_filter_xz(arch) != ARCHIVE_OK)
502 return archive_error_string(arch);
505 #if ARCHIVE_VERSION_NUMBER >= 3000000
507 if (archive_write_add_filter_lzip(arch) != ARCHIVE_OK)
508 return archive_error_string(arch);
511 #if ARCHIVE_VERSION_NUMBER >= 3001000
513 if (archive_write_add_filter_lrzip(arch) != ARCHIVE_OK)
514 return archive_error_string(arch);
517 if (archive_write_add_filter_lzop(arch) != ARCHIVE_OK)
518 return archive_error_string(arch);
521 if (archive_write_add_filter_grzip(arch) != ARCHIVE_OK)
522 return archive_error_string(arch);
525 #if ARCHIVE_VERSION_NUMBER >= 3001900
527 if (archive_write_add_filter_lz4(arch) != ARCHIVE_OK)
528 return archive_error_string(arch);
532 #if ARCHIVE_VERSION_NUMBER < 3000000
533 if (archive_write_set_compression_none(arch) != ARCHIVE_OK)
535 if (archive_write_add_filter_none(arch) != ARCHIVE_OK)
537 return archive_error_string(arch);
542 if (archive_write_set_format_ustar(arch) != ARCHIVE_OK)
543 return archive_error_string(arch);
546 if (archive_write_set_format_shar(arch) != ARCHIVE_OK)
547 return archive_error_string(arch);
550 if (archive_write_set_format_pax(arch) != ARCHIVE_OK)
551 return archive_error_string(arch);
554 if (archive_write_set_format_cpio(arch) != ARCHIVE_OK)
555 return archive_error_string(arch);
558 return "Missing archive format";
560 #if ARCHIVE_VERSION_NUMBER < 3000000
561 if (archive_write_open_file(arch, archive_name) != ARCHIVE_OK)
563 if (archive_write_open_filename(arch, archive_name) != ARCHIVE_OK)
565 return archive_error_string(arch);
567 while (files && ! stop_action) {
569 set_progress_print_all(num++, total, 30);
571 file = (struct file_info *) files->data;
574 filename = get_full_path(file);
575 /* libarchive will crash if instructed to add archive to it self */
576 if (g_utf8_collate(archive_name, filename) == 0) {
577 g_warning("%s: not dumping to '%s'", archive_name, filename);
579 debug_print("%s: not dumping to '%s'\n", archive_name, filename);
584 debug_print("Adding: %s\n", filename);
585 msg = g_strdup_printf("%s", filename);
586 set_progress_file_label(msg);
589 entry = archive_entry_new();
590 if ((fd = open(filename, O_RDONLY)) == -1) {
591 FILE_OP_ERROR(filename, "open");
594 if (lstat(filename, &st) == -1) {
595 FILE_OP_ERROR(filename, "lstat");
597 archive_entry_copy_stat(entry, &st);
598 archive_entry_set_pathname(entry, filename);
599 if (S_ISLNK(st.st_mode)) {
600 if ((buf = malloc(PATH_MAX + 1)) != NULL) {
601 if ((len = readlink(filename, buf, PATH_MAX)) < 0) {
602 FILE_OP_ERROR(filename, "readlink");
605 archive_entry_set_symlink(entry, buf);
607 archive_entry_set_size(entry, 0);
608 archive_write_header(arch, entry);
612 if (archive_write_header(arch, entry) != ARCHIVE_OK)
613 g_warning("%s", archive_error_string(arch));
614 if ((buf = malloc(READ_BLOCK_SIZE)) != NULL) {
615 len = read(fd, buf, READ_BLOCK_SIZE);
617 if (archive_write_data(arch, buf, len) == -1)
618 g_warning("%s", archive_error_string(arch));
619 memset(buf, 0, READ_BLOCK_SIZE);
620 len = read(fd, buf, READ_BLOCK_SIZE);
627 archive_entry_free(entry);
631 files = g_slist_next(files);
635 unlink(archive_name);
638 archive_write_close(arch);
639 #if ARCHIVE_VERSION_NUMBER < 3000000
640 archive_write_finish(arch);
642 archive_write_free(arch);
648 void archive_scan_folder(const char* dir) {
653 gchar path[PATH_MAX];
655 getcwd(cwd, PATH_MAX);
657 if (g_stat(dir, &st) == -1)
659 if (! S_ISDIR(st.st_mode))
661 if (!(root = opendir(dir)))
665 while ((ent = readdir(root)) != NULL) {
666 if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
668 g_stat(ent->d_name, &st);
669 sprintf(path, "%s/%s", dir, ent->d_name);
670 if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
671 archive_add_file(path);
673 else if (S_ISDIR(st.st_mode)) {
674 archive_scan_folder(path);
681 int main(int argc, char** argv) {
682 char* archive = NULL;
686 int perm = ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME |
687 ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_SECURE_SYMLINKS;
689 gboolean remove = FALSE;
690 const char *p = NULL;
693 getcwd(cwd, PATH_MAX);
695 while (*++argv && **argv == '-') {
698 while ((opt = *p++) != '\0') {
702 archive = (char *) p;
714 fprintf(stderr, "Missing archive name!\n");
718 fprintf(stderr, "Expected arguments after options!\n");
723 archive_scan_folder(*argv++);
724 res = archive_create(archive, file_list);
725 if (res != ARCHIVE_OK) {
726 fprintf(stderr, "%s: Creating archive failed\n", archive);
730 pid = (int) getpid();
731 sprintf(buf, "/tmp/%d", pid);
732 fprintf(stdout, "Creating: %s\n", buf);
735 if (strcmp(dirname(archive), ".") == 0)
736 sprintf(buf, "%s/%s", cwd, basename(archive));
738 sprintf(buf, "%s", archive);
739 archive_extract(buf, perm);
742 sprintf(buf, "rm -rf /tmp/%d", pid);
743 fprintf(stdout, "Executing: %s\n", buf);
746 archive_free_list(file_list);