1 /* vim: set textwidth=80 tabstop=4 */
4 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
5 * Copyright (C) 1999-2017 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>
33 #include <sys/types.h>
38 #include "gtk/gtkutils.h"
39 #include "common/claws.h"
40 #include "common/version.h"
41 #include "common/md5.h"
43 #include "mainwindow.h"
47 #include "foldersel.h"
49 #include "procheader.h"
50 #include "libarchive_archive.h"
52 #include "archiver_prefs.h"
53 #include "alertpanel.h"
57 typedef struct _progress_widget progress_widget;
58 struct _progress_widget {
59 GtkWidget* progress_dialog;
64 GtkWidget* file_label;
71 A_FILE_EXISTS = 1 << 1,
72 A_FILE_IS_LINK = 1 << 2,
73 A_FILE_IS_DIR = 1 << 3,
74 A_FILE_NO_WRITE = 1 << 4,
75 A_FILE_UNKNOWN = 1 << 5
78 static progress_widget* progress = NULL;
80 static progress_widget* init_progress() {
81 progress_widget* ptr = malloc(sizeof(*ptr));
83 debug_print("creating progress struct\n");
84 ptr->progress_dialog = NULL;
88 ptr->add_label = NULL;
89 ptr->file_label = NULL;
96 static void progress_dialog_cb(GtkWidget* widget, gint action, gpointer data) {
97 struct ArchivePage* page = (struct ArchivePage *) data;
99 debug_print("Cancel operation\n");
101 page->cancelled = TRUE;
102 archive_free_file_list(page->md5, page->rename);
103 gtk_widget_destroy(widget);
106 static void create_progress_dialog(struct ArchivePage* page) {
107 gchar* title = g_strdup_printf("%s %s", _("Archiving"), page->path);
108 MainWindow* mainwin = mainwindow_get_mainwindow();
110 progress->position = 0;
111 progress->progress_dialog = gtk_dialog_new_with_buttons (
113 GTK_WINDOW(mainwin->window),
114 GTK_DIALOG_DESTROY_WITH_PARENT,
120 progress->progress_dialog,
122 G_CALLBACK(progress_dialog_cb),
125 progress->frame = gtk_frame_new(_("Press Cancel button to stop archiving"));
126 gtk_frame_set_shadow_type(GTK_FRAME(progress->frame),
127 GTK_SHADOW_ETCHED_OUT);
128 gtk_container_set_border_width(GTK_CONTAINER(progress->frame), 4);
129 gtk_container_add(GTK_CONTAINER(
130 GTK_DIALOG(progress->progress_dialog)->vbox), progress->frame);
132 progress->vbox1 = gtk_vbox_new (FALSE, 4);
133 gtk_container_set_border_width (GTK_CONTAINER (progress->vbox1), 4);
134 gtk_container_add(GTK_CONTAINER(progress->frame), progress->vbox1);
136 progress->hbox1 = gtk_hbox_new(FALSE, 8);
137 gtk_container_set_border_width(GTK_CONTAINER(progress->hbox1), 8);
138 gtk_box_pack_start(GTK_BOX(progress->vbox1),
139 progress->hbox1, FALSE, FALSE, 0);
141 progress->add_label = gtk_label_new(_("Archiving:"));
142 gtk_box_pack_start(GTK_BOX(progress->hbox1),
143 progress->add_label, FALSE, FALSE, 0);
145 progress->file_label = gtk_label_new("");
146 gtk_label_set_ellipsize(GTK_LABEL(progress->file_label),
147 PANGO_ELLIPSIZE_START);
148 gtk_box_pack_start(GTK_BOX(progress->hbox1),
149 progress->file_label, TRUE, TRUE, 0);
151 progress->hbox1 = gtk_hbox_new(FALSE, 8);
152 gtk_container_set_border_width(GTK_CONTAINER(progress->hbox1), 8);
153 gtk_box_pack_start(GTK_BOX(progress->vbox1),
154 progress->hbox1, FALSE, FALSE, 0);
156 progress->progress = gtk_progress_bar_new();
157 gtk_box_pack_start(GTK_BOX(progress->hbox1),
158 progress->progress, TRUE, TRUE, 0);
159 gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(progress->progress), 0.25);
161 gtk_window_set_default_size(GTK_WINDOW(progress->progress_dialog), 400, 80);
162 gtk_widget_show_all(progress->progress_dialog);
165 static struct ArchivePage* init_archive_page() {
166 struct ArchivePage* page = malloc(sizeof(struct ArchivePage));
168 debug_print("creating ArchivePage\n");
173 page->response = FALSE;
174 page->force_overwrite = FALSE;
175 page->compress_methods = NULL;
176 page->archive_formats = NULL;
177 page->recursive = NULL;
178 page->cancelled = FALSE;
181 page->rename = FALSE;
182 page->rename_files = NULL;
183 page->isoDate = NULL;
184 page->unlink_files = NULL;
185 page->unlink = FALSE;
190 static void dispose_archive_page(struct ArchivePage* page) {
191 debug_print("freeing ArchivePage\n");
201 static gboolean valid_file_name(gchar* file) {
204 for (i = 0; INVALID_UNIX_CHARS[i] != '\0'; i++) {
205 if (g_utf8_strchr(file, g_utf8_strlen(file, -1), INVALID_UNIX_CHARS[i]))
211 static COMPRESS_METHOD get_compress_method(GSList* btn) {
215 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn->data))) {
216 name = gtk_widget_get_name(GTK_WIDGET(btn->data));
217 if (strcmp("GZIP", name) == 0) {
218 debug_print("GZIP compression enabled\n");
221 else if (strcmp("BZIP", name) == 0) {
222 debug_print("BZIP2 compression enabled\n");
225 else if (strcmp("COMPRESS", name) == 0) {
226 debug_print("COMPRESS compression enabled\n");
229 #if ARCHIVE_VERSION_NUMBER >= 2006990
230 else if (strcmp("LZMA", name) == 0) {
231 debug_print("LZMA compression enabled\n");
234 else if (strcmp("XZ", name) == 0) {
235 debug_print("XZ compression enabled\n");
239 #if ARCHIVE_VERSION_NUMBER >= 3000000
240 else if (strcmp("LZIP", name) == 0) {
241 debug_print("LZIP compression enabled\n");
245 #if ARCHIVE_VERSION_NUMBER >= 3001000
246 else if (strcmp("LRZIP", name) == 0) {
247 debug_print("LRZIP compression enabled\n");
250 else if (strcmp("LZOP", name) == 0) {
251 debug_print("LZOP compression enabled\n");
254 else if (strcmp("GRZIP", name) == 0) {
255 debug_print("GRZIP compression enabled\n");
259 #if ARCHIVE_VERSION_NUMBER >= 3001900
260 else if (strcmp("LZ4", name) == 0) {
261 debug_print("LZ4 compression enabled\n");
265 else if (strcmp("NONE", name) == 0) {
266 debug_print("Compression disabled\n");
270 btn = g_slist_next(btn);
275 static ARCHIVE_FORMAT get_archive_format(GSList* btn) {
279 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn->data))) {
280 name = gtk_widget_get_name(GTK_WIDGET(btn->data));
281 if (strcmp("TAR", name) == 0) {
282 debug_print("TAR archive enabled\n");
285 else if (strcmp("SHAR", name) == 0) {
286 debug_print("SHAR archive enabled\n");
289 else if (strcmp("PAX", name) == 0) {
290 debug_print("PAX archive enabled\n");
293 else if (strcmp("CPIO", name) == 0) {
294 debug_print("CPIO archive enabled\n");
298 btn = g_slist_next(btn);
303 static void create_md5sum(const gchar* file, const gchar* md5_file) {
306 gchar* md5sum = malloc(33);
308 debug_print("Creating md5sum file: %s\n", md5_file);
309 if (md5_hex_digest_file(md5sum, (const unsigned char *) file) == -1) {
313 debug_print("md5sum: %s\n", md5sum);
315 open(md5_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) == -1) {
316 FILE_OP_ERROR(md5_file, "create");
320 text = g_strrstr_len(file, strlen(file), "/");
323 text = g_strdup_printf("%s %s\n", md5sum, text);
326 text = g_strdup_printf("%s %s\n", md5sum, file);
328 debug_print("md5sum: %s\n", text);
329 if (write(fd, text, strlen(text)) < 0)
330 FILE_OP_ERROR(md5_file, "write");
335 static gchar* descriptive_file_name(
336 struct ArchivePage* page, const gchar* file, MsgInfo *msginfo) {
337 gchar* new_file = NULL;
338 gchar *name, *p, *to, *from, *date, *subject;
340 debug_print("renaming file\n");
341 p = g_strrstr_len(file, strlen(file), "/");
342 p = g_strndup(file, p - file);
346 to = g_strdup(msginfo->to);
352 from = g_strdup(msginfo->from);
353 extract_address(from);
358 date = g_strdup(msginfo->date);
359 subst_for_shellsafe_filename(date);
360 /* if not on windows we need to subst some more */
361 subst_chars(date, ":", '_');
365 if (msginfo->subject) {
366 subject = g_strdup(msginfo->subject);
367 subst_for_shellsafe_filename(subject);
368 /* if not on windows we need to subst some more */
369 subst_chars(subject, ":", '_');
372 subject = g_strdup("");
373 name = g_strdup_printf("%s_%s@%s@%s", date, from, to, subject);
374 /* ensure file name is not larger than 96 chars (max file name size
375 * is 100 chars but reserve for .md5)
377 if (strlen(name) > 96)
380 new_file = g_strconcat(p, "/", name, NULL);
389 debug_print("New_file: %s\n", new_file);
390 if (link(file, new_file) != 0) {
391 if (errno != EEXIST) {
392 FILE_OP_ERROR(new_file, "link");
394 new_file = g_strdup(file);
395 page->rename = FALSE;
402 static void walk_folder(struct ArchivePage* page, FolderItem* item,
403 gboolean recursive) {
410 gchar* md5_file = NULL;
413 MsgTrash* msg_trash = NULL;
414 const gchar* date = NULL;
416 if (recursive && ! page->cancelled) {
417 debug_print("Scanning recursive\n");
418 node = item->node->children;
419 while(node && ! page->cancelled) {
420 debug_print("Number of nodes: %d\n", g_node_n_children(node));
422 child = FOLDER_ITEM(node->data);
423 debug_print("new node: %d messages\n", child->total_msgs);
424 walk_folder(page, child, recursive);
429 if (! page->cancelled) {
430 date = gtk_entry_get_text(GTK_ENTRY(page->isoDate));
431 debug_print("cut-off date: %s\n", date);
433 page->files += item->total_msgs;
434 msglist = folder_item_get_msg_list(item);
435 msg_trash = new_msg_trash(item);
436 for (cur = msglist; cur && ! page->cancelled; cur = cur->next) {
437 msginfo = (MsgInfo *) cur->data;
438 debug_print("%s_%s_%s_%s\n",
439 msginfo->date, msginfo->to, msginfo->from, msginfo->subject);
440 file = folder_item_fetch_msg(item, msginfo->msgnum);
441 if (date && strlen(date) > 0 && !before_date(msginfo->date_t, date)) {
445 page->total_size += msginfo->size;
446 /*debug_print("Processing: %s\n", file);*/
449 archive_add_msg_mark(msg_trash, msginfo);
452 file = descriptive_file_name(page, file, msginfo);
454 /* Could not create a descriptive name */
455 file = folder_item_fetch_msg(item, msginfo->msgnum);
459 md5_file = g_strdup_printf("%s.md5", file);
460 create_md5sum(file, md5_file);
461 archive_add_file(md5_file);
464 archive_add_file(file);
468 if (count % 350 == 0) {
469 debug_print("pulse progressbar\n");
470 text = g_strdup_printf(
471 "Scanning %s: %d files", item->name, count);
472 gtk_progress_bar_set_text(
473 GTK_PROGRESS_BAR(progress->progress), text);
475 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(progress->progress));
480 procmsg_msg_list_free(msglist);
484 static AFileTest file_is_writeable(struct ArchivePage* page) {
487 if (g_file_test(page->name, G_FILE_TEST_EXISTS) &&
488 ! page->force_overwrite)
489 return A_FILE_EXISTS;
490 if (g_file_test(page->name, G_FILE_TEST_IS_SYMLINK))
491 return A_FILE_IS_LINK;
492 if (g_file_test(page->name, G_FILE_TEST_IS_DIR))
493 return A_FILE_IS_DIR;
494 if ((fd = open(page->name, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)) == -1) {
496 case EACCES: return A_FILE_NO_WRITE;
497 case EEXIST: return A_FILE_OK;
498 default: return A_FILE_UNKNOWN;
503 claws_unlink(page->name);
508 static gboolean archiver_save_files(struct ArchivePage* page) {
510 COMPRESS_METHOD method;
511 ARCHIVE_FORMAT format;
515 const gchar* res = NULL;
518 gboolean folder_not_set;
519 gboolean file_not_set;
521 /* check if folder to archive and target archive filename are set */
522 folder_not_set = (*gtk_entry_get_text(GTK_ENTRY(page->folder)) == '\0');
523 file_not_set = (*gtk_entry_get_text(GTK_ENTRY(page->file)) == '\0');
524 if (folder_not_set || file_not_set) {
525 alertpanel_error(_("Some uninitialized data prevents from starting\n"
526 "the archiving process:\n"
528 folder_not_set ? _("\n- the folder to archive is not set") : "",
529 file_not_set ? _("\n- the name for archive is not set") : "");
532 /* sync page struct info with folder and archive name edit widgets */
537 page->path = g_strdup(gtk_entry_get_text(GTK_ENTRY(page->folder)));
538 g_strstrip(page->path);
539 debug_print("page->path: %s\n", page->path);
545 page->name = g_strdup(gtk_entry_get_text(GTK_ENTRY(page->file)));
546 g_strstrip(page->name);
547 debug_print("page->name: %s\n", page->name);
549 if ((perm = file_is_writeable(page)) != A_FILE_OK) {
552 msg = g_strdup_printf(_("%s: Exists. Continue anyway?"), page->name);
555 msg = g_strdup_printf(_("%s: Is a link. Cannot continue"), page->name);
558 msg = g_strdup_printf(_("%s: Is a directory. Cannot continue"), page->name);
560 case A_FILE_NO_WRITE:
561 msg = g_strdup_printf(_("%s: Missing permissions. Cannot continue"), page->name);
564 msg = g_strdup_printf(_("%s: Unknown error. Cannot continue"), page->name);
569 if (perm == A_FILE_EXISTS) {
572 aval = alertpanel_full(_("Creating archive"), msg,
573 GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL, FALSE,
574 NULL, ALERT_WARNING, G_ALERTDEFAULT);
576 if (aval != G_ALERTALTERNATE)
579 alertpanel_error(msg);
584 if (! valid_file_name(page->name)) {
585 alertpanel_error(_("Not a valid file name:\n%s."), page->name);
588 item = folder_find_item_from_identifier(page->path);
590 alertpanel_error(_("Not a valid Claws Mail folder:\n%s."), page->name);
594 page->total_size = 0;
595 page->rename = gtk_toggle_button_get_active(
596 GTK_TOGGLE_BUTTON(page->rename_files));
597 recursive = gtk_toggle_button_get_active(
598 GTK_TOGGLE_BUTTON(page->recursive));
599 page->md5 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->md5sum));
600 page->unlink = gtk_toggle_button_get_active(
601 GTK_TOGGLE_BUTTON(page->unlink_files));
602 create_progress_dialog(page);
603 walk_folder(page, item, recursive);
606 list = archive_get_file_list();
607 orig_file = (page->md5) ? page->files * 2 : page->files;
608 debug_print("md5: %d, orig: %d, md5: %d\n",
609 page->md5, page->files, orig_file);
610 if (orig_file != g_slist_length(list)) {
613 msg = g_strdup_printf(
614 _("Adding files in folder failed\n"
615 "Files in folder: %d\n"
616 "Files in list: %d\n"
617 "\nContinue anyway?"),
618 orig_file, g_slist_length(list));
619 aval = alertpanel_full(_("Creating archive"), msg,
620 GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL, FALSE,
621 NULL, ALERT_WARNING, G_ALERTDEFAULT);
623 if (aval != G_ALERTALTERNATE) {
624 archive_free_file_list(page->md5, page->rename);
628 method = get_compress_method(page->compress_methods);
629 format = get_archive_format(page->archive_formats);
630 if ((res = archive_create(page->name, list, method, format)) != NULL) {
631 alertpanel_error(_("Archive creation error:\n%s"), res);
632 archive_free_file_list(page->md5, page->rename);
636 archive_free_archived_files();
641 static void entry_change_cb(GtkWidget* widget, gpointer data) {
642 const gchar* name = gtk_widget_get_name(widget);
643 struct ArchivePage* page = (struct ArchivePage *) data;
645 if (strcmp("folder", name) == 0) {
646 page->path = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget)));
647 debug_print("page->folder = %s\n", page->path);
649 else if (strcmp("file", name) == 0) {
650 page->name = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget)));
651 page->force_overwrite = FALSE;
652 debug_print("page->name = %s\n", page->name);
656 static void show_result(struct ArchivePage* page) {
668 GtkTreeViewColumn* header;
669 GtkCellRenderer* renderer;
672 gchar* method = NULL;
673 gchar* format = NULL;
675 MainWindow* mainwin = mainwindow_get_mainwindow();
677 switch (get_compress_method(page->compress_methods)) {
679 method = g_strdup("GZIP");
682 method = g_strdup("BZIP2");
685 method = g_strdup("Compress");
687 #if ARCHIVE_VERSION_NUMBER >= 2006990
689 method = g_strdup("LZMA");
692 method = g_strdup("XZ");
695 #if ARCHIVE_VERSION_NUMBER >= 3000000
697 method = g_strdup("LZIP");
700 #if ARCHIVE_VERSION_NUMBER >= 3001000
702 method = g_strdup("LRZIP");
705 method = g_strdup("LZOP");
708 method = g_strdup("GRZIP");
711 #if ARCHIVE_VERSION_NUMBER >= 3001900
713 method = g_strdup("LZ4");
717 method = g_strdup("No Compression");
721 switch (get_archive_format(page->archive_formats)) {
723 format = g_strdup("TAR");
726 format = g_strdup("SHAR");
729 format = g_strdup("PAX");
732 format = g_strdup("CPIO");
735 format = g_strdup("NO FORMAT");
738 if (g_stat(page->name, &st) == -1) {
739 alertpanel_error("Could not get size of archive file '%s'.", page->name);
742 dialog = gtk_dialog_new_with_buttons(
744 GTK_WINDOW(mainwin->window),
745 GTK_DIALOG_DESTROY_WITH_PARENT,
749 g_signal_connect_swapped(
752 G_CALLBACK(gtk_widget_destroy),
755 list = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
761 "headers-clickable", FALSE,
762 "reorderable", FALSE,
763 "enable-search", FALSE,
766 renderer = gtk_cell_renderer_text_new();
768 header = gtk_tree_view_column_new_with_attributes(
769 _("Attributes"), renderer, "text", STRING1, NULL);
770 gtk_tree_view_append_column(view, header);
772 header = gtk_tree_view_column_new_with_attributes(
773 _("Values"), renderer, "text", STRING2, NULL);
774 gtk_tree_view_append_column(view, header);
777 GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), GTK_WIDGET(view));
779 gtk_list_store_append(list, &iter);
782 STRING1, _("Archive"),
783 STRING2, page->name, -1);
785 gtk_list_store_append(list, &iter);
788 STRING1, _("Archive format"),
789 STRING2, format, -1);
792 gtk_list_store_append(list, &iter);
795 STRING1, _("Compression method"),
796 STRING2, method, -1);
799 gtk_list_store_append(list, &iter);
800 msg = g_strdup_printf("%d", (page->md5) ? page->files * 2 : page->files);
803 STRING1, _("Number of files"),
807 gtk_list_store_append(list, &iter);
808 msg = g_strdup_printf("%d byte(s)", (guint) st.st_size);
811 STRING1, _("Archive Size"),
815 gtk_list_store_append(list, &iter);
816 msg = g_strdup_printf("%d byte(s)", page->total_size);
819 STRING1, _("Folder Size"),
823 gtk_list_store_append(list, &iter);
824 msg = g_strdup_printf("%d%%",
825 (guint)((st.st_size * 100) / page->total_size));
828 STRING1, _("Compression level"),
832 gtk_list_store_append(list, &iter);
833 msg = g_strdup_printf("%s", (page->md5) ? _("Yes") : _("No"));
836 STRING1, _("MD5 checksum"),
840 gtk_list_store_append(list, &iter);
841 msg = g_strdup_printf("%s", (page->rename) ? _("Yes") : _("No"));
844 STRING1, _("Descriptive names"),
848 gtk_list_store_append(list, &iter);
849 msg = g_strdup_printf("%s", (page->unlink) ? _("Yes") : _("No"));
852 STRING1, _("Delete selected files"),
856 msg = g_strdup(gtk_entry_get_text(GTK_ENTRY(page->isoDate)));
858 gtk_list_store_append(list, &iter);
861 STRING1, _("Select mails before"),
866 gtk_window_set_default_size(GTK_WINDOW(dialog), 320, 260);
868 gtk_widget_show_all(dialog);
871 static void archiver_dialog_cb(GtkWidget* widget, gint action, gpointer data) {
872 struct ArchivePage* page = (struct ArchivePage *) data;
873 gboolean result = FALSE;
876 case GTK_RESPONSE_ACCEPT:
877 debug_print("User chose OK\n");
878 page->response = TRUE;
881 debug_print("User chose CANCEL\n");
882 page->response = FALSE;
883 archiver_gtk_done(page, widget);
886 debug_print("Settings:\naction: %d\n", page->response);
887 if (page->response) {
888 debug_print("Settings:\nfolder: %s\nname: %s\n",
889 (page->path) ? page->path : "(null)",
890 (page->name) ? page->name : "(null)");
891 result = archiver_save_files(page);
892 debug_print("Result->archiver_save_files: %d\n", result);
893 if (progress->progress_dialog &&
894 GTK_IS_WIDGET(progress->progress_dialog))
895 gtk_widget_destroy(progress->progress_dialog);
896 if (result && ! page->cancelled) {
898 archive_free_file_list(page->md5, page->rename);
899 archiver_gtk_done(page, widget);
902 if (page->cancelled) {
903 archiver_gtk_done(page, widget);
909 static void foldersel_cb(GtkWidget *widget, gpointer data)
914 struct ArchivePage* page = (struct ArchivePage *) data;
916 item = foldersel_folder_sel(NULL, FOLDER_SEL_MOVE, NULL, FALSE,
917 _("Select folder to archive"));
918 if (item && (item_id = folder_item_get_identifier(item)) != NULL) {
919 gtk_editable_delete_text(GTK_EDITABLE(page->folder), 0, -1);
920 gtk_editable_insert_text(GTK_EDITABLE(page->folder),
921 item_id, strlen(item_id), &newpos);
922 page->path = g_strdup(item_id);
925 debug_print("Folder to archive: %s\n",
926 gtk_entry_get_text(GTK_ENTRY(page->folder)));
929 static void filesel_cb(GtkWidget *widget, gpointer data)
934 const gchar* homedir;
935 struct ArchivePage* page = (struct ArchivePage *) data;
937 dialog = gtk_file_chooser_dialog_new(
938 _("Select file name for archive [suffix should reflect archive like .tgz]"),
940 GTK_FILE_CHOOSER_ACTION_SAVE,
941 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
942 GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
944 homedir = g_getenv("HOME");
946 homedir = g_get_home_dir();
948 if (archiver_prefs.save_folder)
949 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
950 archiver_prefs.save_folder);
952 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), homedir);
953 if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_RESPONSE_APPLY) {
954 file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
956 gtk_editable_delete_text(GTK_EDITABLE(page->file), 0, -1);
957 gtk_editable_insert_text(GTK_EDITABLE(page->file),
958 file, strlen(file), &newpos);
959 page->name = g_strdup(file);
961 page->force_overwrite = TRUE;
964 gtk_widget_destroy(dialog);
965 debug_print("Name for archive: %s\n",
966 gtk_entry_get_text(GTK_ENTRY(page->file)));
969 void set_progress_file_label(const gchar* file) {
970 debug_print("IsLabel: %s, Update label: %s\n",
971 GTK_IS_WIDGET(progress->file_label)? "Yes" : "No", file);
972 if (GTK_IS_WIDGET(progress->file_label))
973 gtk_label_set_text(GTK_LABEL(progress->file_label), file);
976 void set_progress_print_all(guint fraction, guint total, guint step) {
979 if (GTK_IS_WIDGET(progress->progress)) {
980 if ((fraction - progress->position) % step == 0) {
981 debug_print("frac: %d, total: %d, step: %d, prog->pos: %d\n",
982 fraction, total, step, progress->position);
983 gtk_progress_bar_set_fraction(
984 GTK_PROGRESS_BAR(progress->progress),
985 (total == 0) ? 0 : (gfloat)fraction / (gfloat)total);
986 text_count = g_strdup_printf(_("%ld of %ld"),
987 (long) fraction, (long) total);
988 gtk_progress_bar_set_text(
989 GTK_PROGRESS_BAR(progress->progress), text_count);
991 progress->position = fraction;
997 void archiver_gtk_show() {
1002 GtkWidget* folder_label;
1003 GtkWidget* folder_select;
1004 GtkWidget* file_label;
1005 GtkWidget* file_select;
1006 GtkWidget* gzip_radio_btn;
1007 GtkWidget* bzip_radio_btn;
1008 GtkWidget* compress_radio_btn;
1009 #if ARCHIVE_VERSION_NUMBER >= 2006990
1010 GtkWidget* lzma_radio_btn;
1011 GtkWidget* xz_radio_btn;
1013 #if ARCHIVE_VERSION_NUMBER >= 3000000
1014 GtkWidget* lzip_radio_btn;
1016 #if ARCHIVE_VERSION_NUMBER >= 3001000
1017 GtkWidget* lrzip_radio_btn;
1018 GtkWidget* lzop_radio_btn;
1019 GtkWidget* grzip_radio_btn;
1021 #if ARCHIVE_VERSION_NUMBER >= 3001900
1022 GtkWidget* lz4_radio_btn;
1024 GtkWidget* no_radio_btn;
1025 GtkWidget* shar_radio_btn;
1026 GtkWidget* pax_radio_btn;
1027 GtkWidget* cpio_radio_btn;
1028 GtkWidget* tar_radio_btn;
1029 struct ArchivePage* page;
1030 MainWindow* mainwin = mainwindow_get_mainwindow();
1032 /*debug_set_mode(TRUE);*/
1033 progress = init_progress();
1035 page = init_archive_page();
1037 dialog = gtk_dialog_new_with_buttons (
1038 _("Create Archive"),
1039 GTK_WINDOW(mainwin->window),
1040 GTK_DIALOG_DESTROY_WITH_PARENT,
1042 GTK_RESPONSE_CANCEL,
1044 GTK_RESPONSE_ACCEPT,
1050 G_CALLBACK(archiver_dialog_cb),
1053 frame = gtk_frame_new(_("Enter Archiver arguments"));
1054 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);
1055 gtk_container_set_border_width(GTK_CONTAINER(frame), 4);
1056 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), frame);
1058 vbox1 = gtk_vbox_new (FALSE, 4);
1059 gtk_container_set_border_width (GTK_CONTAINER (vbox1), 4);
1060 gtk_container_add(GTK_CONTAINER(frame), vbox1);
1062 hbox1 = gtk_hbox_new(FALSE, 4);
1063 gtk_container_set_border_width(GTK_CONTAINER(hbox1), 4);
1064 gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, FALSE, 0);
1066 folder_label = gtk_label_new(_("Folder to archive"));
1067 gtk_box_pack_start(GTK_BOX(hbox1), folder_label, FALSE, FALSE, 0);
1069 page->folder = gtk_entry_new();
1070 gtk_widget_set_name(page->folder, "folder");
1071 gtk_box_pack_start(GTK_BOX(hbox1), page->folder, TRUE, TRUE, 0);
1072 CLAWS_SET_TIP(page->folder,
1073 _("Folder which is the root of the archive"));
1075 folder_select = gtkut_get_browse_directory_btn(_("_Browse"));
1076 gtk_box_pack_start(GTK_BOX(hbox1), folder_select, FALSE, FALSE, 0);
1077 CLAWS_SET_TIP(folder_select,
1078 _("Click this button to select a folder which is to be root of the archive"));
1080 hbox1 = gtk_hbox_new(FALSE, 4);
1081 gtk_container_set_border_width(GTK_CONTAINER(hbox1), 4);
1082 gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, FALSE, 0);
1084 file_label = gtk_label_new(_("Name for archive"));
1085 gtk_box_pack_start(GTK_BOX(hbox1), file_label, FALSE, FALSE, 0);
1087 page->file = gtk_entry_new();
1088 gtk_widget_set_name(page->file, "file");
1089 gtk_box_pack_start(GTK_BOX(hbox1), page->file, TRUE, TRUE, 0);
1090 CLAWS_SET_TIP(page->file, _("Archive location and name"));
1092 file_select = gtkut_get_browse_directory_btn(_("_Select"));
1093 gtk_box_pack_start(GTK_BOX(hbox1), file_select, FALSE, FALSE, 0);
1094 CLAWS_SET_TIP(file_select,
1095 _("Click this button to select a name and location for the archive"));
1097 frame = gtk_frame_new(_("Choose compression"));
1098 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);
1099 gtk_container_set_border_width(GTK_CONTAINER(frame), 4);
1100 gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0);
1102 hbox1 = gtk_hbox_new(FALSE, 4);
1103 gtk_container_set_border_width(GTK_CONTAINER(hbox1), 4);
1104 gtk_container_add(GTK_CONTAINER(frame), hbox1);
1106 gzip_radio_btn = gtk_radio_button_new_with_mnemonic(NULL, "G_ZIP");
1107 gtk_widget_set_name(gzip_radio_btn, "GZIP");
1108 gtk_box_pack_start(GTK_BOX(hbox1), gzip_radio_btn, FALSE, FALSE, 0);
1109 archiver_set_tooltip(gzip_radio_btn, g_strdup_printf(_("Choose this option to use %s compression for the archive"), "GZIP"));
1111 bzip_radio_btn = gtk_radio_button_new_with_mnemonic_from_widget(
1112 GTK_RADIO_BUTTON(gzip_radio_btn), "BZIP_2");
1113 gtk_widget_set_name(bzip_radio_btn, "BZIP");
1114 gtk_box_pack_start(GTK_BOX(hbox1), bzip_radio_btn, FALSE, FALSE, 0);
1115 archiver_set_tooltip(bzip_radio_btn, g_strdup_printf(_("Choose this option to use %s compression for the archive"), "BZIP2"));
1117 compress_radio_btn = gtk_radio_button_new_with_mnemonic_from_widget(
1118 GTK_RADIO_BUTTON(gzip_radio_btn), "Com_press");
1119 gtk_widget_set_name(compress_radio_btn, "COMPRESS");
1120 gtk_box_pack_start(GTK_BOX(hbox1), compress_radio_btn, FALSE, FALSE, 0);
1121 archiver_set_tooltip(compress_radio_btn, g_strdup_printf(_("Choose this option to use %s compression for the archive"), "COMPRESS"));
1123 #if ARCHIVE_VERSION_NUMBER >= 2006990
1124 lzma_radio_btn = gtk_radio_button_new_with_mnemonic_from_widget(
1125 GTK_RADIO_BUTTON(gzip_radio_btn), "_LZMA");
1126 gtk_widget_set_name(lzma_radio_btn, "LZMA");
1127 gtk_box_pack_start(GTK_BOX(hbox1), lzma_radio_btn, FALSE, FALSE, 0);
1128 archiver_set_tooltip(lzma_radio_btn, g_strdup_printf(_("Choose this option to use %s compression for the archive"), "LZMA"));
1130 xz_radio_btn = gtk_radio_button_new_with_mnemonic_from_widget(
1131 GTK_RADIO_BUTTON(gzip_radio_btn), "_XZ");
1132 gtk_widget_set_name(xz_radio_btn, "XZ");
1133 gtk_box_pack_start(GTK_BOX(hbox1), xz_radio_btn, FALSE, FALSE, 0);
1134 archiver_set_tooltip(xz_radio_btn, g_strdup_printf(_("Choose this option to use %s compression for the archive"), "XZ"));
1137 #if ARCHIVE_VERSION_NUMBER >= 3000000
1138 lzip_radio_btn = gtk_radio_button_new_with_mnemonic_from_widget(
1139 GTK_RADIO_BUTTON(gzip_radio_btn), "_LZIP");
1140 gtk_widget_set_name(lzip_radio_btn, "LZIP");
1141 gtk_box_pack_start(GTK_BOX(hbox1), lzip_radio_btn, FALSE, FALSE, 0);
1142 archiver_set_tooltip(lzip_radio_btn, g_strdup_printf(_("Choose this option to use %s compression for the archive"), "LZIP"));
1145 #if ARCHIVE_VERSION_NUMBER >= 3001000
1146 lrzip_radio_btn = gtk_radio_button_new_with_mnemonic_from_widget(
1147 GTK_RADIO_BUTTON(gzip_radio_btn), "L_RZIP");
1148 gtk_widget_set_name(lrzip_radio_btn, "LRZIP");
1149 gtk_box_pack_start(GTK_BOX(hbox1), lrzip_radio_btn, FALSE, FALSE, 0);
1150 archiver_set_tooltip(lrzip_radio_btn, g_strdup_printf(_("Choose this option to use %s compression for the archive"), "LRZIP"));
1152 lzop_radio_btn = gtk_radio_button_new_with_mnemonic_from_widget(
1153 GTK_RADIO_BUTTON(gzip_radio_btn), "LZ_OP");
1154 gtk_widget_set_name(lzop_radio_btn, "LZOP");
1155 gtk_box_pack_start(GTK_BOX(hbox1), lzop_radio_btn, FALSE, FALSE, 0);
1156 archiver_set_tooltip(lzop_radio_btn, g_strdup_printf(_("Choose this option to use %s compression for the archive"), "LZOP"));
1158 grzip_radio_btn = gtk_radio_button_new_with_mnemonic_from_widget(
1159 GTK_RADIO_BUTTON(gzip_radio_btn), "_GRZIP");
1160 gtk_widget_set_name(grzip_radio_btn, "GRZIP");
1161 gtk_box_pack_start(GTK_BOX(hbox1), grzip_radio_btn, FALSE, FALSE, 0);
1162 archiver_set_tooltip(grzip_radio_btn, g_strdup_printf(_("Choose this option to use %s compression for the archive"), "GRZIP"));
1165 #if ARCHIVE_VERSION_NUMBER >= 3001900
1166 lz4_radio_btn = gtk_radio_button_new_with_mnemonic_from_widget(
1167 GTK_RADIO_BUTTON(gzip_radio_btn), "LZ_4");
1168 gtk_widget_set_name(lz4_radio_btn, "LZ4");
1169 gtk_box_pack_start(GTK_BOX(hbox1), lz4_radio_btn, FALSE, FALSE, 0);
1170 archiver_set_tooltip(lz4_radio_btn, g_strdup_printf(_("Choose this option to use %s compression for the archive"), "LZ4"));
1173 no_radio_btn = gtk_radio_button_new_with_mnemonic_from_widget(
1174 GTK_RADIO_BUTTON(gzip_radio_btn), _("_None"));
1175 gtk_widget_set_name(no_radio_btn, "NONE");
1176 gtk_box_pack_start(GTK_BOX(hbox1), no_radio_btn, FALSE, FALSE, 0);
1177 archiver_set_tooltip(no_radio_btn, g_strdup_printf(_("Choose this option to use %s compression for the archive"), "NO"));
1179 page->compress_methods =
1180 gtk_radio_button_get_group(GTK_RADIO_BUTTON(gzip_radio_btn));
1182 switch (archiver_prefs.compression) {
1183 case COMPRESSION_GZIP:
1184 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gzip_radio_btn), TRUE);
1186 case COMPRESSION_BZIP:
1187 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bzip_radio_btn), TRUE);
1189 case COMPRESSION_COMPRESS:
1190 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compress_radio_btn), TRUE);
1192 #if ARCHIVE_VERSION_NUMBER >= 2006990
1193 case COMPRESSION_LZMA:
1194 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lzma_radio_btn), TRUE);
1196 case COMPRESSION_XZ:
1197 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(xz_radio_btn), TRUE);
1200 #if ARCHIVE_VERSION_NUMBER >= 3000000
1201 case COMPRESSION_LZIP:
1202 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lzip_radio_btn), TRUE);
1205 #if ARCHIVE_VERSION_NUMBER >= 3001000
1206 case COMPRESSION_LRZIP:
1207 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lrzip_radio_btn), TRUE);
1209 case COMPRESSION_LZOP:
1210 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lzop_radio_btn), TRUE);
1212 case COMPRESSION_GRZIP:
1213 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grzip_radio_btn), TRUE);
1216 #if ARCHIVE_VERSION_NUMBER >= 3001900
1217 case COMPRESSION_LZ4:
1218 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lz4_radio_btn), TRUE);
1221 case COMPRESSION_NONE:
1222 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(no_radio_btn), TRUE);
1226 frame = gtk_frame_new(_("Choose format"));
1227 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);
1228 gtk_container_set_border_width(GTK_CONTAINER(frame), 4);
1229 gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0);
1231 hbox1 = gtk_hbox_new(FALSE, 4);
1232 gtk_container_set_border_width(GTK_CONTAINER(hbox1), 4);
1233 gtk_container_add(GTK_CONTAINER(frame), hbox1);
1235 tar_radio_btn = gtk_radio_button_new_with_mnemonic(NULL, "_TAR");
1236 gtk_widget_set_name(tar_radio_btn, "TAR");
1237 gtk_box_pack_start(GTK_BOX(hbox1), tar_radio_btn, FALSE, FALSE, 0);
1238 archiver_set_tooltip(tar_radio_btn, g_strdup_printf(_("Choose this to use %s as format for the archive"), "TAR"));
1240 shar_radio_btn = gtk_radio_button_new_with_mnemonic_from_widget(
1241 GTK_RADIO_BUTTON(tar_radio_btn), "S_HAR");
1242 gtk_widget_set_name(shar_radio_btn, "SHAR");
1243 gtk_box_pack_start(GTK_BOX(hbox1), shar_radio_btn, FALSE, FALSE, 0);
1244 archiver_set_tooltip(shar_radio_btn, g_strdup_printf(_("Choose this to use %s as format for the archive"), "SHAR"));
1246 cpio_radio_btn = gtk_radio_button_new_with_mnemonic_from_widget(
1247 GTK_RADIO_BUTTON(tar_radio_btn), "CP_IO");
1248 gtk_widget_set_name(cpio_radio_btn, "CPIO");
1249 gtk_box_pack_start(GTK_BOX(hbox1), cpio_radio_btn, FALSE, FALSE, 0);
1250 archiver_set_tooltip(cpio_radio_btn, g_strdup_printf(_("Choose this to use %s as format for the archive"), "CPIO"));
1252 pax_radio_btn = gtk_radio_button_new_with_mnemonic_from_widget(
1253 GTK_RADIO_BUTTON(tar_radio_btn), "PA_X");
1254 gtk_widget_set_name(pax_radio_btn, "PAX");
1255 gtk_box_pack_start(GTK_BOX(hbox1), pax_radio_btn, FALSE, FALSE, 0);
1256 archiver_set_tooltip(pax_radio_btn, g_strdup_printf(_("Choose this to use %s as format for the archive"), "PAX"));
1258 page->archive_formats =
1259 gtk_radio_button_get_group(GTK_RADIO_BUTTON(tar_radio_btn));
1261 switch (archiver_prefs.format) {
1263 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tar_radio_btn), TRUE);
1266 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(shar_radio_btn), TRUE);
1269 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cpio_radio_btn), TRUE);
1272 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pax_radio_btn), TRUE);
1276 frame = gtk_frame_new(_("Miscellaneous options"));
1277 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);
1278 gtk_container_set_border_width(GTK_CONTAINER(frame), 4);
1279 gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0);
1281 hbox1 = gtk_hbox_new(FALSE, 4);
1282 gtk_container_set_border_width(GTK_CONTAINER(hbox1), 4);
1283 gtk_container_add(GTK_CONTAINER(frame), hbox1);
1285 page->recursive = gtk_check_button_new_with_mnemonic(_("_Recursive"));
1286 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->recursive), archiver_prefs.recursive);
1287 gtk_box_pack_start(GTK_BOX(hbox1), page->recursive, FALSE, FALSE, 0);
1288 CLAWS_SET_TIP(page->recursive,
1289 _("Choose this option to include subfolders in the archive"));
1291 page->md5sum = gtk_check_button_new_with_mnemonic(_("_MD5sum"));
1292 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->md5sum), archiver_prefs.md5sum);
1293 gtk_box_pack_start(GTK_BOX(hbox1), page->md5sum, FALSE, FALSE, 0);
1294 CLAWS_SET_TIP(page->md5sum,
1295 _("Choose this option to add MD5 checksums for each file in the archive.\n"
1296 "Be aware though, that this dramatically increases the time it\n"
1297 "will take to create the archive"));
1299 page->rename_files = gtk_check_button_new_with_mnemonic(_("R_ename"));
1300 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->rename_files), archiver_prefs.rename);
1301 gtk_box_pack_start(GTK_BOX(hbox1), page->rename_files, FALSE, FALSE, 0);
1302 CLAWS_SET_TIP(page->rename_files,
1303 _("Choose this option to use descriptive names for each file in the archive.\n"
1304 "The naming scheme: date_from@to@subject.\n"
1305 "Names will be truncated to max 96 characters"));
1307 page->unlink_files = gtk_check_button_new_with_mnemonic(_("_Delete"));
1308 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->unlink_files), archiver_prefs.unlink);
1309 gtk_box_pack_start(GTK_BOX(hbox1), page->unlink_files, FALSE, FALSE, 0);
1310 CLAWS_SET_TIP(page->unlink_files,
1311 _("Choose this option to delete mails after archiving\n"
1312 "At this point only handles IMAP4, Local mbox and POP3"));
1315 frame = gtk_frame_new(_("Selection options"));
1316 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);
1317 gtk_container_set_border_width(GTK_CONTAINER(frame), 4);
1318 gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0);
1320 hbox1 = gtk_hbox_new(FALSE, 4);
1321 gtk_container_set_border_width(GTK_CONTAINER(hbox1), 4);
1322 gtk_container_add(GTK_CONTAINER(frame), hbox1);
1324 file_label = gtk_label_new(_("Select mails before"));
1325 gtk_box_pack_start(GTK_BOX(hbox1), file_label, FALSE, FALSE, 0);
1327 page->isoDate = gtk_entry_new();
1328 gtk_widget_set_name(page->isoDate, "isoDate");
1329 gtk_box_pack_start(GTK_BOX(hbox1), page->isoDate, TRUE, TRUE, 0);
1330 CLAWS_SET_TIP(page->isoDate,
1331 _("Select emails before a certain date\n"
1332 "Date must comply to ISO-8601 [YYYY-MM-DD]"));
1334 g_signal_connect(G_OBJECT(folder_select), "clicked",
1335 G_CALLBACK(foldersel_cb), page);
1336 g_signal_connect(G_OBJECT(file_select), "clicked",
1337 G_CALLBACK(filesel_cb), page);
1338 g_signal_connect(G_OBJECT(page->folder), "activate",
1339 G_CALLBACK(entry_change_cb), page);
1340 g_signal_connect(G_OBJECT(page->file), "activate",
1341 G_CALLBACK(entry_change_cb), page);
1343 gtk_widget_show_all(dialog);
1346 void archiver_gtk_done(struct ArchivePage* page, GtkWidget* widget) {
1347 dispose_archive_page(page);
1349 gtk_widget_destroy(widget);
1350 /*debug_set_mode(FALSE);*/