1 /* vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·: */
4 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
5 * Copyright (C) 1999-2008 Michael Rasmussen and the Claws Mail Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "claws-features.h"
28 #include <glib/gi18n.h>
30 #include "libarchive_archive.h"
33 # include "archiver.h"
35 # include "mainwindow.h"
39 #include <sys/types.h>
43 #include <archive_entry.h>
53 #define READ_BLOCK_SIZE 10240
60 static GSList
* msg_trash_list
= NULL
;
61 static GSList
* file_list
= NULL
;
62 static gboolean stop_action
= FALSE
;
65 static int permissions
= 0;
68 static void free_msg_trash(MsgTrash
* trash
) {
70 debug_print("Freeing files in %s\n", folder_item_get_name(trash
->item
));
72 g_slist_free(trash
->msgs
);
78 MsgTrash
* new_msg_trash(FolderItem
* item
) {
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
))
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
);
97 void archive_free_archived_files() {
102 for (l
= msg_trash_list
; l
; l
= g_slist_next(l
)) {
103 mt
= (MsgTrash
*) l
->data
;
104 debug_print("Trashing messages in folder: %s\n",
105 folder_item_get_name(mt
->item
));
106 res
= folder_item_remove_msgs(mt
->item
, mt
->msgs
);
107 debug_print("Result was %d\n", res
);
110 g_slist_free(msg_trash_list
);
111 msg_trash_list
= NULL
;
114 void archive_add_msg_mark(MsgTrash
* trash
, MsgInfo
* msg
) {
115 g_return_if_fail(trash
!= NULL
|| msg
!= NULL
);
116 debug_print("Marking msg #%d for removal\n", msg
->msgnum
);
117 trash
->msgs
= g_slist_prepend(trash
->msgs
, msg
);
120 static void free_all(GDate
* date
, gchar
** parts
) {
127 static gboolean
is_iso_string(gchar
** items
) {
134 debug_print("Date part %d: %s\n", i
, item
);
137 if (strlen(item
) != 4)
142 if (strlen(item
) != 2)
149 debug_print("Leaving\n");
153 static GDate
* iso2GDate(const gchar
* date
) {
155 gchar
** parts
= NULL
;
158 g_return_val_if_fail(date
!= NULL
, NULL
);
160 gdate
= g_date_new();
161 parts
= g_strsplit(date
, "-", 3);
162 if (! is_iso_string(parts
))
166 for (i
= 0; i
< 3; i
++) {
167 int t
= atoi(parts
[i
]);
170 if (t
< 1 || t
> 9999) {
171 free_all(gdate
, parts
);
174 g_date_set_year(gdate
, t
);
177 if (t
< 1 || t
> 12) {
178 free_all(gdate
, parts
);
181 g_date_set_month(gdate
, t
);
184 if (t
< 1 || t
> 31) {
185 free_all(gdate
, parts
);
188 g_date_set_day(gdate
, t
);
196 gboolean
before_date(time_t msg_mtime
, const gchar
* before
) {
202 debug_print("Cut-off date: %s\n", before
);
203 if ((date
= iso2GDate(before
)) == NULL
) {
204 g_warning("Bad date format: %s\n", before
);
208 file_t
= g_date_new();
209 g_date_set_time_t(file_t
, msg_mtime
);
211 if (debug_get_mode()) {
212 pos
= g_new0(char, 100);
213 g_date_strftime(pos
, 100, "%F", file_t
);
214 fprintf(stderr
, "File date: %s\n", pos
);
218 if (! g_date_valid(file_t
)) {
219 g_warning("Invalid msg date\n");
223 res
= (g_date_compare(file_t
, date
) >= 0) ? FALSE
: TRUE
;
228 static void archive_free_file_info(struct file_info
* file
) {
239 void stop_archiving() {
240 debug_print("stop action set to true\n");
244 void archive_free_file_list(gboolean md5
, gboolean rename
) {
245 struct file_info
* file
= NULL
;
248 debug_print("freeing file list\n");
252 file
= (struct file_info
*) file_list
->data
;
253 if (!rename
&& md5
&& g_str_has_suffix(file
->name
, ".md5")) {
254 path
= g_strdup_printf("%s/%s", file
->path
, file
->name
);
255 debug_print("unlinking %s\n", path
);
260 path
= g_strdup_printf("%s/%s", file
->path
, file
->name
);
261 debug_print("unlinking %s\n", path
);
265 archive_free_file_info(file
);
266 file_list
->data
= NULL
;
267 file_list
= g_slist_next(file_list
);
270 g_slist_free(file_list
);
275 static struct file_info
* archive_new_file_info() {
276 struct file_info
* new_file_info
= malloc(sizeof(struct file_info
));
278 new_file_info
->path
= NULL
;
279 new_file_info
->name
= NULL
;
280 return new_file_info
;
283 static void archive_add_to_list(struct file_info
* file
) {
286 file_list
= g_slist_prepend(file_list
, (gpointer
) file
);
289 static gchar
* strip_leading_dot_slash(gchar
* path
) {
290 gchar
* stripped
= path
;
291 gchar
* result
= NULL
;
293 if (stripped
&& stripped
[0] == '.') {
295 if (stripped
&& stripped
[0] == '/')
297 result
= g_strdup(stripped
);
300 result
= 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
);
310 sprintf(path
, "%s", file
->name
);
315 static gchar
* strip_leading_slash(gchar
* path
) {
316 gchar
* stripped
= path
;
317 gchar
* result
= NULL
;
319 if (stripped
&& stripped
[0] == '/') {
321 result
= g_strdup(stripped
);
324 result
= g_strdup(path
);
328 static int archive_get_permissions() {
333 void archive_set_permissions(int perm
) {
337 static int archive_copy_data(struct archive
* in
, struct archive
* out
) {
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
;
353 void archive_add_file(gchar
* path
) {
354 struct file_info
* file
= archive_new_file_info();
355 gchar
* filename
= NULL
;
357 g_return_if_fail(path
!= NULL
);
360 debug_print("add %s to list\n", path
);
362 filename
= g_strrstr_len(path
, strlen(path
), "/");
364 g_warning("%s\n", path
);
365 g_return_if_fail(filename
!= NULL
);
368 file
->name
= g_strdup(filename
);
369 file
->path
= strip_leading_dot_slash(dirname(path
));
370 archive_add_to_list(file
);
373 GSList
* archive_get_file_list() {
378 const gchar
* archive_extract(const char* archive_name
, int flags
) {
381 struct archive_entry
* entry
;
382 int res
= ARCHIVE_OK
;
384 const char* result
== NULL
;
386 g_return_val_if_fail(archive_name
!= NULL
, ARCHIVE_FATAL
);
388 fprintf(stdout
, "%s: extracting\n", archive_name
);
389 in
= archive_read_new();
390 if ((res
= archive_read_support_format_tar(in
)) == ARCHIVE_OK
) {
391 if ((res
= archive_read_support_compression_gzip(in
)) == ARCHIVE_OK
) {
392 if ((res
= archive_read_open_file(
393 in
, archive_name
, READ_BLOCK_SIZE
)) != ARCHIVE_OK
) {
394 buf
= g_strdup_printf(
395 "%s: %s\n", archive_name
, archive_error_string(in
));
396 g_warning("%s\n", buf
);
398 result
= archive_error_string(in
);
401 out
= archive_write_disk_new();
402 if ((res
= archive_write_disk_set_options(
403 out
, flags
)) == ARCHIVE_OK
) {
404 res
= archive_read_next_header(in
, &entry
);
405 while (res
== ARCHIVE_OK
) {
406 fprintf(stdout
, "%s\n", archive_entry_pathname(entry
));
407 res
= archive_write_header(out
, entry
);
408 if (res
!= ARCHIVE_OK
) {
409 buf
= g_strdup_printf("%s\n",
410 archive_error_string(out
));
411 g_warning("%s\n", buf
);
413 /* skip this file an continue */
417 res
= archive_copy_data(in
, out
);
418 if (res
!= ARCHIVE_OK
) {
419 buf
= g_strdup_printf("%s\n",
420 archive_error_string(in
));
421 g_warning("%s\n", buf
);
423 /* skip this file an continue */
427 res
= archive_read_next_header(in
, &entry
);
430 if (res
== ARCHIVE_EOF
)
432 if (res
!= ARCHIVE_OK
) {
433 buf
= g_strdup_printf("%s\n", archive_error_string(in
));
436 buf
= g_strdup_printf("%s: Unknown error\n", archive_name
);
438 g_warning("%s\n", buf
);
440 result
= archive_error_string(in
);
444 result
= archive_error_string(out
);
445 archive_read_close(in
);
447 archive_read_finish(in
);
450 result
= archive_error_string(in
);
453 result
= archive_error_string(in
);
458 const gchar
* archive_create(const char* archive_name
, GSList
* files
,
459 COMPRESS_METHOD method
, ARCHIVE_FORMAT format
) {
460 struct archive
* arch
;
461 struct archive_entry
* entry
;
466 struct file_info
* file
;
467 gchar
* filename
= NULL
;
472 gint total
= g_slist_length (files
);
475 g_return_val_if_fail(files
!= NULL
, "No files for archiving");
477 debug_print("File: %s\n", archive_name
);
478 arch
= archive_write_new();
481 if (archive_write_set_compression_gzip(arch
) != ARCHIVE_OK
)
482 return archive_error_string(arch
);
485 if (archive_write_set_compression_bzip2(arch
) != ARCHIVE_OK
)
486 return archive_error_string(arch
);
490 if (archive_write_set_compression_compress(arch
) != ARCHIVE_OK
)
491 return archive_error_string(arch
);
495 if (archive_write_set_compression_none(arch
) != ARCHIVE_OK
)
496 return archive_error_string(arch
);
501 if (archive_write_set_format_ustar(arch
) != ARCHIVE_OK
)
502 return archive_error_string(arch
);
505 if (archive_write_set_format_shar(arch
) != ARCHIVE_OK
)
506 return archive_error_string(arch
);
509 if (archive_write_set_format_pax(arch
) != ARCHIVE_OK
)
510 return archive_error_string(arch
);
513 if (archive_write_set_format_cpio(arch
) != ARCHIVE_OK
)
514 return archive_error_string(arch
);
517 return "Missing archive format";
519 if (archive_write_open_file(arch
, archive_name
) != ARCHIVE_OK
)
520 return archive_error_string(arch
);
522 while (files
&& ! stop_action
) {
524 set_progress_print_all(num
++, total
, 30);
526 file
= (struct file_info
*) files
->data
;
529 filename
= get_full_path(file
);
530 /* libarchive will crash if instructed to add archive to it self */
531 if (g_utf8_collate(archive_name
, filename
) == 0) {
533 buf
= g_strdup_printf(
534 "%s: Not dumping to %s", archive_name
, filename
);
535 g_warning("%s\n", buf
);
537 debug_print("%s\n", buf
);
543 debug_print("Adding: %s\n", filename
);
544 msg
= g_strdup_printf("%s", filename
);
545 set_progress_file_label(msg
);
548 entry
= archive_entry_new();
549 lstat(filename
, &st
);
550 if ((fd
= open(filename
, O_RDONLY
)) == -1) {
554 archive_entry_copy_stat(entry
, &st
);
555 archive_entry_set_pathname(entry
, filename
);
556 if (S_ISLNK(st
.st_mode
)) {
558 buf
= malloc(PATH_MAX
+ 1);
559 if ((len
= readlink(filename
, buf
, PATH_MAX
)) < 0)
560 perror("error in readlink");
563 archive_entry_set_symlink(entry
, buf
);
565 archive_entry_set_size(entry
, 0);
566 archive_write_header(arch
, entry
);
569 if (archive_write_header(arch
, entry
) != ARCHIVE_OK
)
570 g_warning("%s", archive_error_string(arch
));
572 buf
= malloc(READ_BLOCK_SIZE
);
573 len
= read(fd
, buf
, READ_BLOCK_SIZE
);
575 if (archive_write_data(arch
, buf
, len
) == -1)
576 g_warning("%s", archive_error_string(arch
));
577 memset(buf
, 0, READ_BLOCK_SIZE
);
578 len
= read(fd
, buf
, READ_BLOCK_SIZE
);
583 archive_entry_free(entry
);
587 files
= g_slist_next(files
);
591 unlink(archive_name
);
594 archive_write_close(arch
);
595 archive_write_finish(arch
);
600 void archive_scan_folder(const char* dir
) {
605 gchar path
[PATH_MAX
];
607 getcwd(cwd
, PATH_MAX
);
609 if (g_stat(dir
, &st
) == -1)
611 if (! S_ISDIR(st
.st_mode
))
613 if (!(root
= opendir(dir
)))
617 while ((ent
= readdir(root
)) != NULL
) {
618 if (strcmp(".", ent
->d_name
) == 0 || strcmp("..", ent
->d_name
) == 0)
620 g_stat(ent
->d_name
, &st
);
621 sprintf(path
, "%s/%s", dir
, ent
->d_name
);
622 if (S_ISREG(st
.st_mode
) || S_ISLNK(st
.st_mode
)) {
623 archive_add_file(path
);
625 else if (S_ISDIR(st
.st_mode
)) {
626 archive_scan_folder(path
);
633 int main(int argc
, char** argv
) {
634 char* archive
= NULL
;
638 int perm
= ARCHIVE_EXTRACT_PERM
| ARCHIVE_EXTRACT_TIME
|
639 ARCHIVE_EXTRACT_ACL
| ARCHIVE_EXTRACT_FFLAGS
| ARCHIVE_EXTRACT_SECURE_SYMLINKS
;
641 gboolean remove
= FALSE
;
642 const char *p
= NULL
;
645 getcwd(cwd
, PATH_MAX
);
647 while (*++argv
&& **argv
== '-') {
650 while ((opt
= *p
++) != '\0') {
654 archive
= (char *) p
;
666 fprintf(stderr
, "Missing archive name!\n");
670 fprintf(stderr
, "Expected arguments after options!\n");
675 archive_scan_folder(*argv
++);
676 res
= archive_create(archive
, file_list
);
677 if (res
!= ARCHIVE_OK
) {
678 fprintf(stderr
, "%s: Creating archive failed\n", archive
);
682 pid
= (int) getpid();
683 sprintf(buf
, "/tmp/%d", pid
);
684 fprintf(stdout
, "Creating: %s\n", buf
);
687 if (strcmp(dirname(archive
), ".") == 0)
688 sprintf(buf
, "%s/%s", cwd
, basename(archive
));
690 sprintf(buf
, "%s", archive
);
691 archive_extract(buf
, perm
);
694 sprintf(buf
, "rm -rf /tmp/%d", pid
);
695 fprintf(stdout
, "Executing: %s\n", buf
);
698 archive_free_list(file_list
);