2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2024 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>
26 #include <glib/gstdio.h>
28 #include "libarchive_archive.h"
31 # include "archiver.h"
33 # include "mainwindow.h"
40 #include <sys/types.h>
44 #include <archive_entry.h>
52 #define READ_BLOCK_SIZE 10240
59 static GSList
* msg_trash_list
= NULL
;
60 static GSList
* file_list
= NULL
;
61 static gboolean stop_action
= FALSE
;
64 static int permissions
= 0;
67 static void free_msg_trash(MsgTrash
* trash
) {
69 gchar
*name
= folder_item_get_name(trash
->item
);
71 debug_print("Freeing files in %s\n", name
);
74 g_slist_free(trash
->msgs
);
80 MsgTrash
* new_msg_trash(FolderItem
* item
) {
84 g_return_val_if_fail(item
!= NULL
, NULL
);
86 /* FolderType must be F_MH, F_MBOX, F_MAILDIR or F_IMAP */
87 type
= item
->folder
->klass
->type
;
88 if (!(type
== F_MH
|| type
== F_MBOX
||
89 type
== F_MAILDIR
|| type
== F_IMAP
))
91 msg_trash
= g_new0(MsgTrash
, 1);
92 msg_trash
->item
= item
;
93 msg_trash
->msgs
= NULL
;
94 msg_trash_list
= g_slist_prepend(msg_trash_list
, msg_trash
);
99 void archive_free_archived_files() {
104 for (l
= msg_trash_list
; l
; l
= g_slist_next(l
)) {
107 mt
= (MsgTrash
*) l
->data
;
108 name
= folder_item_get_name(mt
->item
);
109 debug_print("Trashing messages in folder: %s\n",
112 res
= folder_item_remove_msgs(mt
->item
, mt
->msgs
);
113 debug_print("Result was %d\n", res
);
116 g_slist_free(msg_trash_list
);
117 msg_trash_list
= NULL
;
120 void archive_add_msg_mark(MsgTrash
* trash
, MsgInfo
* msg
) {
121 g_return_if_fail(trash
!= NULL
|| msg
!= NULL
);
122 debug_print("Marking msg #%d for removal\n", msg
->msgnum
);
123 trash
->msgs
= g_slist_prepend(trash
->msgs
, msg
);
126 static void free_all(GDate
* date
, gchar
** parts
) {
133 static gboolean
is_iso_string(gchar
** items
) {
140 debug_print("Date part %d: %s\n", i
, item
);
143 if (strlen(item
) != 4)
148 if (strlen(item
) != 2)
155 debug_print("Leaving\n");
159 static GDate
* iso2GDate(const gchar
* date
) {
164 g_return_val_if_fail(date
!= NULL
, NULL
);
166 gdate
= g_date_new();
167 parts
= g_strsplit(date
, "-", 3);
170 if (! is_iso_string(parts
)) {
171 free_all(gdate
, parts
);
174 for (i
= 0; i
< 3; i
++) {
175 int t
= atoi(parts
[i
]);
178 if (t
< 1 || t
> 9999) {
179 free_all(gdate
, parts
);
182 g_date_set_year(gdate
, t
);
185 if (t
< 1 || t
> 12) {
186 free_all(gdate
, parts
);
189 g_date_set_month(gdate
, t
);
192 if (t
< 1 || t
> 31) {
193 free_all(gdate
, parts
);
196 g_date_set_day(gdate
, t
);
204 gboolean
before_date(time_t msg_mtime
, const gchar
* before
) {
210 debug_print("Cut-off date: %s\n", before
);
211 if ((date
= iso2GDate(before
)) == NULL
) {
212 g_warning("bad date format: %s", before
);
216 file_t
= g_date_new();
217 g_date_set_time_t(file_t
, msg_mtime
);
219 if (debug_get_mode()) {
220 pos
= g_new0(char, 100);
221 g_date_strftime(pos
, 100, "%F", file_t
);
222 fprintf(stderr
, "File date: %s\n", pos
);
226 if (! g_date_valid(file_t
)) {
227 g_warning("invalid msg date");
231 res
= (g_date_compare(file_t
, date
) >= 0) ? FALSE
: TRUE
;
236 static void archive_free_file_info(struct file_info
* file
) {
247 void stop_archiving(void) {
248 debug_print("stop action set to true\n");
252 void archive_free_file_list(gboolean md5
, gboolean rename
) {
253 struct file_info
* file
= NULL
;
256 debug_print("freeing file list\n");
260 file
= (struct file_info
*) file_list
->data
;
261 if (!rename
&& md5
&& g_str_has_suffix(file
->name
, ".md5")) {
262 path
= g_strdup_printf("%s/%s", file
->path
, file
->name
);
263 debug_print("unlinking %s\n", path
);
264 if (g_unlink(path
) < 0)
265 FILE_OP_ERROR(path
, "g_unlink");
269 path
= g_strdup_printf("%s/%s", file
->path
, file
->name
);
270 debug_print("unlinking %s\n", path
);
271 if (g_unlink(path
) < 0)
272 FILE_OP_ERROR(path
, "g_unlink");
275 archive_free_file_info(file
);
276 file_list
->data
= NULL
;
277 file_list
= g_slist_next(file_list
);
280 g_slist_free(file_list
);
285 static struct file_info
* archive_new_file_info() {
286 struct file_info
* new_file_info
= malloc(sizeof(struct file_info
));
288 new_file_info
->path
= NULL
;
289 new_file_info
->name
= NULL
;
290 return new_file_info
;
293 static void archive_add_to_list(struct file_info
* file
) {
296 file_list
= g_slist_prepend(file_list
, (gpointer
) file
);
299 static gchar
* strip_leading_dot_slash(gchar
* path
) {
300 if (path
&& strlen(path
) > 1 && path
[0] == '.' && path
[1] == '/')
301 return g_strdup(&(path
[2]));
303 return g_strdup(path
);
306 static gchar
* get_full_path(struct file_info
* file
) {
307 char* path
= malloc(PATH_MAX
);
309 if (file
->path
&& *(file
->path
))
310 sprintf(path
, "%s/%s", file
->path
, file
->name
);
312 sprintf(path
, "%s", file
->name
);
317 static gchar
* strip_leading_slash(gchar
* path
) {
318 gchar
* stripped
= path
;
319 gchar
* result
= NULL
;
321 if (stripped
&& stripped
[0] == '/') {
323 result
= g_strdup(stripped
);
326 result
= g_strdup(path
);
330 static int archive_get_permissions() {
335 void archive_set_permissions(int perm
) {
339 static int archive_copy_data(struct archive
* in
, struct archive
* out
) {
343 int res
= ARCHIVE_OK
;
345 while (res
== ARCHIVE_OK
) {
346 res
= archive_read_data_block(in
, &buf
, &size
, &offset
);
347 if (res
== ARCHIVE_OK
) {
348 res
= archive_write_data_block(out
, buf
, size
, offset
);
351 return (res
== ARCHIVE_EOF
) ? ARCHIVE_OK
: res
;
355 void archive_add_file(gchar
* path
) {
356 struct file_info
* file
;
357 gchar
* filename
= NULL
;
359 g_return_if_fail(path
!= NULL
);
361 #ifndef DEBUG_ARCHIVE
362 debug_print("add %s to list\n", path
);
364 filename
= g_strrstr_len(path
, strlen(path
), "/");
366 g_warning("no filename in path '%s'", path
);
367 g_return_if_fail(filename
!= NULL
);
370 file
= archive_new_file_info();
371 file
->name
= g_strdup(filename
);
372 file
->path
= strip_leading_dot_slash(dirname(path
));
373 archive_add_to_list(file
);
376 GSList
* archive_get_file_list() {
381 const gchar
* archive_extract(const char* archive_name
, int flags
) {
384 struct archive_entry
* entry
;
385 int res
= ARCHIVE_OK
;
386 const char* result
= NULL
;
388 g_return_val_if_fail(archive_name
!= NULL
, NULL
);
390 fprintf(stdout
, "%s: extracting\n", archive_name
);
391 in
= archive_read_new();
392 if ((res
= archive_read_support_format_tar(in
)) == ARCHIVE_OK
) {
393 if ((res
= archive_read_support_compression_gzip(in
)) == ARCHIVE_OK
) {
394 #if ARCHIVE_VERSION_NUMBER < 3000000
395 if ((res
= archive_read_open_file(
397 if ((res
= archive_read_open_filename(
399 in
, archive_name
, READ_BLOCK_SIZE
)) != ARCHIVE_OK
) {
400 g_warning("%s: %s", archive_name
, archive_error_string(in
));
401 result
= archive_error_string(in
);
404 out
= archive_write_disk_new();
405 if ((res
= archive_write_disk_set_options(
406 out
, flags
)) == ARCHIVE_OK
) {
407 res
= archive_read_next_header(in
, &entry
);
408 while (res
== ARCHIVE_OK
) {
409 res
= archive_write_header(out
, entry
);
410 if (res
!= ARCHIVE_OK
) {
411 g_warning("%s", archive_error_string(out
));
412 /* skip this file an continue */
416 res
= archive_copy_data(in
, out
);
417 if (res
!= ARCHIVE_OK
) {
418 g_warning("%s", archive_error_string(in
));
419 /* skip this file an continue */
423 res
= archive_read_next_header(in
, &entry
);
426 if (res
== ARCHIVE_EOF
)
428 if (res
!= ARCHIVE_OK
) {
429 const gchar
*e
= archive_error_string(in
);
430 g_warning("%s: %s", archive_name
, e
? e
: "unknown error");
435 result
= archive_error_string(out
);
436 archive_read_close(in
);
438 #if ARCHIVE_VERSION_NUMBER < 3000000
439 archive_read_finish(in
);
441 archive_read_free(in
);
445 result
= archive_error_string(in
);
448 result
= archive_error_string(in
);
453 const gchar
* archive_create(const char* archive_name
, GSList
* files
,
454 COMPRESS_METHOD method
, ARCHIVE_FORMAT format
) {
455 struct archive
* arch
;
457 #ifndef DEBUG_ARCHIVE
459 gint total
= g_slist_length (files
);
462 g_return_val_if_fail(files
!= NULL
, "No files for archiving");
464 debug_print("File: %s\n", archive_name
);
465 arch
= archive_write_new();
468 #if ARCHIVE_VERSION_NUMBER < 3000000
469 if (archive_write_set_compression_gzip(arch
) != ARCHIVE_OK
)
471 if (archive_write_add_filter_gzip(arch
) != ARCHIVE_OK
)
473 return archive_error_string(arch
);
476 #if ARCHIVE_VERSION_NUMBER < 3000000
477 if (archive_write_set_compression_bzip2(arch
) != ARCHIVE_OK
)
479 if (archive_write_add_filter_bzip2(arch
) != ARCHIVE_OK
)
481 return archive_error_string(arch
);
484 #if ARCHIVE_VERSION_NUMBER < 3000000
485 if (archive_write_set_compression_compress(arch
) != ARCHIVE_OK
)
487 if (archive_write_add_filter_compress(arch
) != ARCHIVE_OK
)
489 return archive_error_string(arch
);
491 #if ARCHIVE_VERSION_NUMBER >= 2006990
493 #if ARCHIVE_VERSION_NUMBER < 3000000
494 if (archive_write_set_compression_lzma(arch
) != ARCHIVE_OK
)
496 if (archive_write_add_filter_lzma(arch
) != ARCHIVE_OK
)
498 return archive_error_string(arch
);
501 #if ARCHIVE_VERSION_NUMBER < 3000000
502 if (archive_write_set_compression_xz(arch
) != ARCHIVE_OK
)
504 if (archive_write_add_filter_xz(arch
) != ARCHIVE_OK
)
506 return archive_error_string(arch
);
509 #if ARCHIVE_VERSION_NUMBER >= 3000000
511 if (archive_write_add_filter_lzip(arch
) != ARCHIVE_OK
)
512 return archive_error_string(arch
);
515 #if ARCHIVE_VERSION_NUMBER >= 3001000
517 if (archive_write_add_filter_lrzip(arch
) != ARCHIVE_OK
)
518 return archive_error_string(arch
);
521 if (archive_write_add_filter_lzop(arch
) != ARCHIVE_OK
)
522 return archive_error_string(arch
);
525 if (archive_write_add_filter_grzip(arch
) != ARCHIVE_OK
)
526 return archive_error_string(arch
);
529 #if ARCHIVE_VERSION_NUMBER >= 3001900
531 if (archive_write_add_filter_lz4(arch
) != ARCHIVE_OK
)
532 return archive_error_string(arch
);
536 #if ARCHIVE_VERSION_NUMBER < 3000000
537 if (archive_write_set_compression_none(arch
) != ARCHIVE_OK
)
539 if (archive_write_add_filter_none(arch
) != ARCHIVE_OK
)
541 return archive_error_string(arch
);
546 if (archive_write_set_format_ustar(arch
) != ARCHIVE_OK
)
547 return archive_error_string(arch
);
550 if (archive_write_set_format_shar(arch
) != ARCHIVE_OK
)
551 return archive_error_string(arch
);
554 if (archive_write_set_format_pax(arch
) != ARCHIVE_OK
)
555 return archive_error_string(arch
);
558 if (archive_write_set_format_cpio(arch
) != ARCHIVE_OK
)
559 return archive_error_string(arch
);
562 return "Missing archive format";
564 #if ARCHIVE_VERSION_NUMBER < 3000000
565 if (archive_write_open_file(arch
, archive_name
) != ARCHIVE_OK
)
567 if (archive_write_open_filename(arch
, archive_name
) != ARCHIVE_OK
)
569 return archive_error_string(arch
);
571 while (files
&& ! stop_action
) {
572 struct file_info
* file
;
573 gchar
* filename
= NULL
;
575 #ifndef DEBUG_ARCHIVE
576 set_progress_print_all(num
++, total
, 30);
578 file
= (struct file_info
*) files
->data
;
581 filename
= get_full_path(file
);
582 /* libarchive will crash if instructed to add archive to it self */
583 if (g_utf8_collate(archive_name
, filename
) == 0) {
584 g_warning("%s: not dumping to '%s'", archive_name
, filename
);
585 #ifndef DEBUG_ARCHIVE
586 debug_print("%s: not dumping to '%s'\n", archive_name
, filename
);
590 struct archive_entry
* entry
;
597 #ifndef DEBUG_ARCHIVE
600 debug_print("Adding: %s\n", filename
);
601 msg
= g_strdup_printf("%s", filename
);
602 set_progress_file_label(msg
);
605 if ((fd
= g_open(filename
, O_RDONLY
, 0)) == -1) {
606 FILE_OP_ERROR(filename
, "g_open");
609 if (g_stat(filename
, &st
) == -1) {
610 FILE_OP_ERROR(filename
, "g_stat");
612 entry
= archive_entry_new();
613 archive_entry_copy_stat(entry
, &st
);
614 archive_entry_set_pathname(entry
, filename
);
615 if (S_ISLNK(st
.st_mode
)) {
617 buf
= g_file_read_link(filename
, &err
);
619 FILE_OP_ERROR(filename
, "g_file_read_link");
622 archive_entry_set_symlink(entry
, buf
);
624 archive_entry_set_size(entry
, 0);
625 archive_write_header(arch
, entry
);
629 if (archive_write_header(arch
, entry
) != ARCHIVE_OK
)
630 g_warning("%s", archive_error_string(arch
));
631 if ((buf
= malloc(READ_BLOCK_SIZE
)) != NULL
) {
632 len
= read(fd
, buf
, READ_BLOCK_SIZE
);
634 if (archive_write_data(arch
, buf
, len
) == -1)
635 g_warning("%s", archive_error_string(arch
));
636 memset(buf
, 0, READ_BLOCK_SIZE
);
637 len
= read(fd
, buf
, READ_BLOCK_SIZE
);
642 archive_entry_free(entry
);
644 if (!g_close(fd
, &err
) || err
) {
645 FILE_OP_ERROR(filename
, "g_close");
652 files
= g_slist_next(files
);
654 #ifndef DEBUG_ARCHIVE
656 unlink(archive_name
);
659 archive_write_close(arch
);
660 #if ARCHIVE_VERSION_NUMBER < 3000000
661 archive_write_finish(arch
);
663 archive_write_free(arch
);
669 void archive_scan_folder(const char* dir
) {
674 gchar path
[PATH_MAX
];
675 GError
*error
= NULL
;
677 getcwd(cwd
, PATH_MAX
);
679 if (g_stat(dir
, &st
) == -1)
681 if (! S_ISDIR(st
.st_mode
))
683 if (!(root
= g_dir_open(dir
, 0, &error
))) {
684 debug_print("opening '%s' failed: %d (%s)\n", dir
, error
->code
, error
->message
);
689 while ((ent
= g_dir_read_name(root
)) != NULL
) {
690 if (strcmp(".", ent
) == 0 || strcmp("..", ent
) == 0)
692 if (g_stat(ent
, &st
) == -1) {
693 FILE_OP_ERROR(ent
, "g_stat");
696 sprintf(path
, "%s/%s", dir
, ent
);
697 if (S_ISREG(st
.st_mode
) || S_ISLNK(st
.st_mode
)) {
698 archive_add_file(path
);
700 else if (S_ISDIR(st
.st_mode
)) {
701 archive_scan_folder(path
);
708 int main(int argc
, char** argv
) {
709 char* archive
= NULL
;
713 int perm
= ARCHIVE_EXTRACT_PERM
| ARCHIVE_EXTRACT_TIME
|
714 ARCHIVE_EXTRACT_ACL
| ARCHIVE_EXTRACT_FFLAGS
| ARCHIVE_EXTRACT_SECURE_SYMLINKS
;
716 gboolean remove
= FALSE
;
717 const char *p
= NULL
;
720 getcwd(cwd
, PATH_MAX
);
722 while (*++argv
&& **argv
== '-') {
725 while ((opt
= *p
++) != '\0') {
729 archive
= (char *) p
;
741 fprintf(stderr
, "Missing archive name!\n");
745 fprintf(stderr
, "Expected arguments after options!\n");
750 archive_scan_folder(*argv
++);
751 res
= archive_create(archive
, file_list
, GZIP
, TAR
);
753 fprintf(stderr
, "%s: Creating archive failed [%s]\n", archive
, res
);
757 pid
= (int) getpid();
758 sprintf(buf
, "/tmp/%d", pid
);
759 fprintf(stdout
, "Creating: %s\n", buf
);
762 if (strcmp(dirname(archive
), ".") == 0)
763 sprintf(buf
, "%s/%s", cwd
, basename(archive
));
765 sprintf(buf
, "%s", archive
);
766 archive_extract(buf
, perm
);
769 sprintf(buf
, "rm -rf /tmp/%d", pid
);
770 fprintf(stdout
, "Executing: %s\n", buf
);
773 if (file_list
!= NULL
)
774 g_slist_free(file_list
);
779 void archiver_set_tooltip(GtkWidget
* widget
, gchar
* text
) {
780 gtk_widget_set_tooltip_text(widget
, text
);