now min version of glib is 2.20, remove all irrelevent conditionals
[claws.git] / src / plugins / archive / libarchive_archive.c
blob478202c3c56a8e6c17424a2029b7b250871e1a0f
1 /* vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·: */
3 /*
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/>.
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #include "claws-features.h"
25 #endif
27 #include <glib.h>
28 #include <glib/gi18n.h>
30 #include "libarchive_archive.h"
32 #ifndef _TEST
33 # include "archiver.h"
34 # include "utils.h"
35 # include "mainwindow.h"
36 # include "folder.h"
37 #endif
39 #include <sys/types.h>
40 #include <sys/stat.h>
42 #include <archive.h>
43 #include <archive_entry.h>
44 #include <fcntl.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <dirent.h>
50 #include <glib.h>
51 #include <libgen.h>
53 #define READ_BLOCK_SIZE 10240
55 struct file_info {
56 char* path;
57 char* name;
60 static GSList* msg_trash_list = NULL;
61 static GSList* file_list = NULL;
62 static gboolean stop_action = FALSE;
64 #ifdef _TEST
65 static int permissions = 0;
66 #endif
68 static void free_msg_trash(MsgTrash* trash) {
69 if (trash) {
70 debug_print("Freeing files in %s\n", folder_item_get_name(trash->item));
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 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);
108 free_msg_trash(mt);
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) {
121 if (date)
122 g_date_free(date);
123 if (parts)
124 g_strfreev(parts);
127 static gboolean is_iso_string(gchar** items) {
128 int i = -1;
129 gchar* item;
131 while (*items) {
132 i++;
133 item = *items++;
134 debug_print("Date part %d: %s\n", i, item);
135 switch(i) {
136 case 0:
137 if (strlen(item) != 4)
138 return FALSE;
139 break;
140 case 1:
141 case 2:
142 if (strlen(item) != 2)
143 return FALSE;
144 break;
145 default:
146 return FALSE;
149 debug_print("Leaving\n");
150 return (i == 2);
153 static GDate* iso2GDate(const gchar* date) {
154 GDate* gdate;
155 gchar** parts = NULL;
156 int i;
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))
163 return NULL;
164 if (!parts)
165 return NULL;
166 for (i = 0; i < 3; i++) {
167 int t = atoi(parts[i]);
168 switch (i) {
169 case 0:
170 if (t < 1 || t > 9999) {
171 free_all(gdate, parts);
172 return NULL;
174 g_date_set_year(gdate, t);
175 break;
176 case 1:
177 if (t < 1 || t > 12) {
178 free_all(gdate, parts);
179 return NULL;
181 g_date_set_month(gdate, t);
182 break;
183 case 2:
184 if (t < 1 || t > 31) {
185 free_all(gdate, parts);
186 return NULL;
188 g_date_set_day(gdate, t);
189 break;
192 g_strfreev(parts);
193 return gdate;
196 gboolean before_date(time_t msg_mtime, const gchar* before) {
197 gchar* pos = NULL;
198 GDate* date;
199 GDate* file_t;
200 gboolean res;
202 debug_print("Cut-off date: %s\n", before);
203 if ((date = iso2GDate(before)) == NULL) {
204 g_warning("Bad date format: %s\n", before);
205 return FALSE;
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);
215 g_free(pos);
218 if (! g_date_valid(file_t)) {
219 g_warning("Invalid msg date\n");
220 return FALSE;
223 res = (g_date_compare(file_t, date) >= 0) ? FALSE : TRUE;
224 g_date_free(file_t);
225 return res;
228 static void archive_free_file_info(struct file_info* file) {
229 if (! file)
230 return;
231 if (file->path)
232 g_free(file->path);
233 if (file->name)
234 g_free(file->name);
235 g_free(file);
236 file = NULL;
239 void stop_archiving() {
240 debug_print("stop action set to true\n");
241 stop_action = TRUE;
244 void archive_free_file_list(gboolean md5, gboolean rename) {
245 struct file_info* file = NULL;
246 gchar* path = NULL;
248 debug_print("freeing file list\n");
249 if (! file_list)
250 return;
251 while (file_list) {
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);
256 g_unlink(path);
257 g_free(path);
259 if (rename) {
260 path = g_strdup_printf("%s/%s", file->path, file->name);
261 debug_print("unlinking %s\n", path);
262 g_unlink(path);
263 g_free(path);
265 archive_free_file_info(file);
266 file_list->data = NULL;
267 file_list = g_slist_next(file_list);
269 if (file_list) {
270 g_slist_free(file_list);
271 file_list = NULL;
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) {
284 if (! file)
285 return;
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] == '.') {
294 ++stripped;
295 if (stripped && stripped[0] == '/')
296 ++stripped;
297 result = g_strdup(stripped);
299 else
300 result = g_strdup(path);
301 return result;
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 = archive_new_file_info();
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("%s\n", path);
365 g_return_if_fail(filename != NULL);
367 filename++;
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() {
374 return file_list;
377 #ifdef _TEST
378 const gchar* archive_extract(const char* archive_name, int flags) {
379 struct archive* in;
380 struct archive* out;
381 struct archive_entry* entry;
382 int res = ARCHIVE_OK;
383 gchar* buf = NULL;
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);
397 g_free(buf);
398 result = archive_error_string(in);
400 else {
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);
412 g_free(buf);
413 /* skip this file an continue */
414 res = ARCHIVE_OK;
416 else {
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);
422 g_free(buf);
423 /* skip this file an continue */
424 res = ARCHIVE_OK;
426 else
427 res = archive_read_next_header(in, &entry);
430 if (res == ARCHIVE_EOF)
431 res = ARCHIVE_OK;
432 if (res != ARCHIVE_OK) {
433 buf = g_strdup_printf("%s\n", archive_error_string(in));
434 if (*buf == '\n') {
435 g_free(buf);
436 buf = g_strdup_printf("%s: Unknown error\n", archive_name);
438 g_warning("%s\n", buf);
439 g_free(buf);
440 result = archive_error_string(in);
443 else
444 result = archive_error_string(out);
445 archive_read_close(in);
447 archive_read_finish(in);
449 else
450 result = archive_error_string(in);
452 else
453 result = archive_error_string(in);
454 return result;
456 #endif
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;
462 char* buf = NULL;
463 ssize_t len;
464 int fd;
465 struct stat st;
466 struct file_info* file;
467 gchar* filename = NULL;
468 gchar* msg = NULL;
470 #ifndef _TEST
471 gint num = 0;
472 gint total = g_slist_length (files);
473 #endif
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();
479 switch (method) {
480 case ZIP:
481 if (archive_write_set_compression_gzip(arch) != ARCHIVE_OK)
482 return archive_error_string(arch);
483 break;
484 case BZIP2:
485 if (archive_write_set_compression_bzip2(arch) != ARCHIVE_OK)
486 return archive_error_string(arch);
487 break;
488 #if NEW_ARCHIVE_API
489 case COMPRESS:
490 if (archive_write_set_compression_compress(arch) != ARCHIVE_OK)
491 return archive_error_string(arch);
492 break;
493 #endif
494 case NO_COMPRESS:
495 if (archive_write_set_compression_none(arch) != ARCHIVE_OK)
496 return archive_error_string(arch);
497 break;
499 switch (format) {
500 case TAR:
501 if (archive_write_set_format_ustar(arch) != ARCHIVE_OK)
502 return archive_error_string(arch);
503 break;
504 case SHAR:
505 if (archive_write_set_format_shar(arch) != ARCHIVE_OK)
506 return archive_error_string(arch);
507 break;
508 case PAX:
509 if (archive_write_set_format_pax(arch) != ARCHIVE_OK)
510 return archive_error_string(arch);
511 break;
512 case CPIO:
513 if (archive_write_set_format_cpio(arch) != ARCHIVE_OK)
514 return archive_error_string(arch);
515 break;
516 case NO_FORMAT:
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) {
523 #ifndef _TEST
524 set_progress_print_all(num++, total, 30);
525 #endif
526 file = (struct file_info *) files->data;
527 if (!file)
528 continue;
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) {
532 buf = NULL;
533 buf = g_strdup_printf(
534 "%s: Not dumping to %s", archive_name, filename);
535 g_warning("%s\n", buf);
536 #ifndef _TEST
537 debug_print("%s\n", buf);
538 #endif
539 g_free(buf);
541 else {
542 #ifndef _TEST
543 debug_print("Adding: %s\n", filename);
544 msg = g_strdup_printf("%s", filename);
545 set_progress_file_label(msg);
546 g_free(msg);
547 #endif
548 entry = archive_entry_new();
549 lstat(filename, &st);
550 if ((fd = open(filename, O_RDONLY)) == -1) {
551 perror("open file");
553 else {
554 archive_entry_copy_stat(entry, &st);
555 archive_entry_set_pathname(entry, filename);
556 if (S_ISLNK(st.st_mode)) {
557 buf = NULL;
558 buf = malloc(PATH_MAX + 1);
559 if ((len = readlink(filename, buf, PATH_MAX)) < 0)
560 perror("error in readlink");
561 else
562 buf[len] = '\0';
563 archive_entry_set_symlink(entry, buf);
564 g_free(buf);
565 archive_entry_set_size(entry, 0);
566 archive_write_header(arch, entry);
568 else {
569 if (archive_write_header(arch, entry) != ARCHIVE_OK)
570 g_warning("%s", archive_error_string(arch));
571 buf = NULL;
572 buf = malloc(READ_BLOCK_SIZE);
573 len = read(fd, buf, READ_BLOCK_SIZE);
574 while (len > 0) {
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);
580 g_free(buf);
582 close(fd);
583 archive_entry_free(entry);
586 g_free(filename);
587 files = g_slist_next(files);
589 #ifndef _TEST
590 if (stop_action)
591 unlink(archive_name);
592 stop_action = FALSE;
593 #endif
594 archive_write_close(arch);
595 archive_write_finish(arch);
596 return NULL;
599 #ifdef _TEST
600 void archive_scan_folder(const char* dir) {
601 struct stat st;
602 DIR* root;
603 struct dirent* ent;
604 gchar cwd[PATH_MAX];
605 gchar path[PATH_MAX];
607 getcwd(cwd, PATH_MAX);
609 if (g_stat(dir, &st) == -1)
610 return;
611 if (! S_ISDIR(st.st_mode))
612 return;
613 if (!(root = opendir(dir)))
614 return;
615 chdir(dir);
617 while ((ent = readdir(root)) != NULL) {
618 if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
619 continue;
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);
629 chdir(cwd);
630 closedir(root);
633 int main(int argc, char** argv) {
634 char* archive = NULL;
635 char buf[PATH_MAX];
636 int pid;
637 int opt;
638 int perm = ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME |
639 ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_SECURE_SYMLINKS;
640 gchar cwd[PATH_MAX];
641 gboolean remove = FALSE;
642 const char *p = NULL;
643 int res;
645 getcwd(cwd, PATH_MAX);
647 while (*++argv && **argv == '-') {
648 p = *argv + 1;
650 while ((opt = *p++) != '\0') {
651 switch(opt) {
652 case 'a':
653 if (*p != '\0')
654 archive = (char *) p;
655 else
656 archive = *++argv;
657 p += strlen(p);
658 break;
659 case 'r':
660 remove = TRUE;
661 break;
665 if (! archive) {
666 fprintf(stderr, "Missing archive name!\n");
667 return EXIT_FAILURE;
669 if (!*argv) {
670 fprintf(stderr, "Expected arguments after options!\n");
671 return EXIT_FAILURE;
674 while (*argv) {
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);
679 return EXIT_FAILURE;
682 pid = (int) getpid();
683 sprintf(buf, "/tmp/%d", pid);
684 fprintf(stdout, "Creating: %s\n", buf);
685 mkdir(buf, 0700);
686 chdir(buf);
687 if (strcmp(dirname(archive), ".") == 0)
688 sprintf(buf, "%s/%s", cwd, basename(archive));
689 else
690 sprintf(buf, "%s", archive);
691 archive_extract(buf, perm);
692 chdir(cwd);
693 if (remove) {
694 sprintf(buf, "rm -rf /tmp/%d", pid);
695 fprintf(stdout, "Executing: %s\n", buf);
696 system(buf);
698 archive_free_list(file_list);
699 return EXIT_SUCCESS;
701 #endif