2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto 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 2 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, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <glib/gi18n.h>
30 #include <sys/types.h>
47 #include "prefs_gtk.h"
49 #include "filtering.h"
50 #include "procheader.h"
53 #include "folder_item_prefs.h"
54 #include "remotefolder.h"
55 #include "partial_download.h"
56 #include "statusbar.h"
61 /* Dependecies to be removed ?! */
62 #include "prefs_common.h"
63 #include "prefs_account.h"
65 /* Define possible missing constants for Windows. */
77 static GList *folder_list = NULL;
78 static GSList *class_list = NULL;
79 static GSList *folder_unloaded_list = NULL;
81 void folder_init (Folder *folder,
84 static gchar *folder_item_get_cache_file (FolderItem *item);
85 static gchar *folder_item_get_mark_file (FolderItem *item);
86 static gchar *folder_get_list_path (void);
87 static GNode *folder_get_xml_node (Folder *folder);
88 static Folder *folder_get_from_xml (GNode *node);
89 static void folder_update_op_count_rec (GNode *node);
92 static void folder_get_persist_prefs_recursive
93 (GNode *node, GHashTable *pptable);
94 static gboolean persist_prefs_free (gpointer key, gpointer val, gpointer data);
95 static void folder_item_read_cache (FolderItem *item);
96 gint folder_item_scan_full (FolderItem *item, gboolean filtering);
97 static void folder_item_update_with_msg (FolderItem *item, FolderItemUpdateFlags update_flags,
99 static GHashTable *folder_persist_prefs_new (Folder *folder);
100 static void folder_persist_prefs_free (GHashTable *pptable);
101 static void folder_item_restore_persist_prefs (FolderItem *item, GHashTable *pptable);
104 void folder_system_init(void)
106 folder_register_class(mh_get_class());
107 folder_register_class(imap_get_class());
108 folder_register_class(news_get_class());
111 static GSList *folder_get_class_list(void)
116 void folder_register_class(FolderClass *klass)
118 GSList *xmllist, *cur;
120 debug_print("registering folder class %s\n", klass->idstr);
122 class_list = g_slist_append(class_list, klass);
124 xmllist = g_slist_copy(folder_unloaded_list);
125 for (cur = xmllist; cur != NULL; cur = g_slist_next(cur)) {
126 GNode *node = (GNode *) cur->data;
127 XMLNode *xmlnode = (XMLNode *) node->data;
128 GList *cur = xmlnode->tag->attr;
130 for (; cur != NULL; cur = g_list_next(cur)) {
131 XMLAttr *attr = (XMLAttr *) cur->data;
133 if (!attr || !attr->name || !attr->value) continue;
134 if (!strcmp(attr->name, "type") && !strcmp(attr->value, klass->idstr)) {
137 folder = folder_get_from_xml(node);
139 folder_unloaded_list = g_slist_remove(folder_unloaded_list, node);
146 g_slist_free(xmllist);
149 void folder_unregister_class(FolderClass *klass)
151 GList *folderlist, *cur;
153 debug_print("unregistering folder class %s\n", klass->idstr);
155 class_list = g_slist_remove(class_list, klass);
157 folderlist = g_list_copy(folder_get_list());
158 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
159 Folder *folder = (Folder *) cur->data;
161 if (folder->klass == klass) {
162 GNode *xmlnode = folder_get_xml_node(folder);
163 folder_unloaded_list = g_slist_append(folder_unloaded_list, xmlnode);
164 folder_destroy(folder);
167 g_list_free(folderlist);
170 Folder *folder_new(FolderClass *klass, const gchar *name, const gchar *path)
172 Folder *folder = NULL;
175 g_return_val_if_fail(klass != NULL, NULL);
177 name = name ? name : path;
178 folder = klass->new_folder(name, path);
180 /* Create root folder item */
181 item = folder_item_new(folder, name, NULL);
185 item->folder = folder;
186 folder->node = item->node = g_node_new(item);
192 void folder_init(Folder *folder, const gchar *name)
194 g_return_if_fail(folder != NULL);
196 folder_set_name(folder, name);
198 /* Init folder data */
199 folder->account = NULL;
201 folder->inbox = NULL;
202 folder->outbox = NULL;
203 folder->draft = NULL;
204 folder->queue = NULL;
205 folder->trash = NULL;
208 static void reset_parent_type(FolderItem *item, gpointer data) {
209 item->parent_stype = -1;
212 void folder_item_change_type(FolderItem *item, SpecialFolderItemType newtype)
214 Folder *folder = NULL;
215 FolderUpdateData hookdata;
217 folder = item->folder;
218 /* unset previous root of newtype */
221 folder_item_change_type(folder->inbox, F_NORMAL);
222 folder->inbox = item;
225 folder_item_change_type(folder->outbox, F_NORMAL);
226 folder->outbox = item;
229 folder_item_change_type(folder->queue, F_NORMAL);
230 folder->queue = item;
233 folder_item_change_type(folder->draft, F_NORMAL);
234 folder->draft = item;
237 folder_item_change_type(folder->trash, F_NORMAL);
238 folder->trash = item;
244 /* set new type for current folder and sons */
245 item->stype = newtype;
246 folder_func_to_all_folders(reset_parent_type, NULL);
248 hookdata.folder = folder;
249 hookdata.update_flags = FOLDER_TREE_CHANGED;
250 hookdata.item = NULL;
251 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
254 void folder_destroy(Folder *folder)
256 g_return_if_fail(folder != NULL);
257 g_return_if_fail(folder->klass->destroy_folder != NULL);
259 folder_remove(folder);
261 folder_tree_destroy(folder);
263 folder->klass->destroy_folder(folder);
265 g_free(folder->name);
269 void folder_set_xml(Folder *folder, XMLTag *tag)
272 FolderItem *rootitem = NULL;
274 if ((folder->node != NULL) && (folder->node->data != NULL))
275 rootitem = (FolderItem *) folder->node->data;
277 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
278 XMLAttr *attr = (XMLAttr *) cur->data;
280 if (!attr || !attr->name || !attr->value) continue;
281 if (!strcmp(attr->name, "name")) {
282 g_free(folder->name);
283 folder->name = g_strdup(attr->value);
284 if (rootitem != NULL) {
285 g_free(rootitem->name);
286 rootitem->name = g_strdup(attr->value);
288 } else if (!strcmp(attr->name, "account_id")) {
289 PrefsAccount *account;
291 account = account_find_from_id(atoi(attr->value));
293 g_warning("account_id: %s not found\n", attr->value);
295 folder->account = account;
296 account->folder = folder;
298 } else if (!strcmp(attr->name, "collapsed")) {
299 if (rootitem != NULL)
300 rootitem->collapsed = *attr->value == '1' ? TRUE : FALSE;
301 } else if (!strcmp(attr->name, "sort")) {
302 folder->sort = atoi(attr->value);
307 XMLTag *folder_get_xml(Folder *folder)
311 tag = xml_tag_new("folder");
314 xml_tag_add_attr(tag, xml_attr_new("name", folder->name));
316 xml_tag_add_attr(tag, xml_attr_new_int("account_id", folder->account->account_id));
317 if (folder->node && folder->node->data) {
318 FolderItem *rootitem = (FolderItem *) folder->node->data;
320 xml_tag_add_attr(tag, xml_attr_new("collapsed", rootitem->collapsed ? "1" : "0"));
322 xml_tag_add_attr(tag, xml_attr_new_int("sort", folder->sort));
327 FolderItem *folder_item_new(Folder *folder, const gchar *name, const gchar *path)
329 FolderItem *item = NULL;
331 g_return_val_if_fail(folder != NULL, NULL);
333 if (folder->klass->item_new) {
334 item = folder->klass->item_new(folder);
336 item = g_new0(FolderItem, 1);
339 g_return_val_if_fail(item != NULL, NULL);
341 item->stype = F_NORMAL;
342 item->name = g_strdup(name);
343 item->path = g_strdup(path);
346 item->unread_msgs = 0;
347 item->unreadmarked_msgs = 0;
348 item->marked_msgs = 0;
349 item->total_msgs = 0;
353 item->no_sub = FALSE;
354 item->no_select = FALSE;
355 item->collapsed = FALSE;
356 item->thread_collapsed = FALSE;
357 item->threaded = TRUE;
358 item->ret_rcpt = FALSE;
359 item->opened = FALSE;
360 item->node = g_node_new(item);
362 item->account = NULL;
363 item->apply_sub = FALSE;
364 item->mark_queue = NULL;
366 item->parent_stype = -1;
368 item->sort_key = SORT_BY_DATE;
369 item->sort_type = SORT_ASCENDING;
371 item->prefs = folder_item_prefs_new();
376 void folder_item_append(FolderItem *parent, FolderItem *item)
378 g_return_if_fail(parent != NULL);
379 g_return_if_fail(parent->folder != NULL);
380 g_return_if_fail(parent->node != NULL);
381 g_return_if_fail(item != NULL);
383 item->folder = parent->folder;
384 g_node_append(parent->node, item->node);
387 void folder_item_remove(FolderItem *item)
389 GNode *node, *start_node;
390 FolderUpdateData hookdata;
392 g_return_if_fail(item != NULL);
393 g_return_if_fail(item->folder != NULL);
394 g_return_if_fail(item->folder->node != NULL);
396 start_node = node = item->node;
398 node = item->folder->node;
400 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
401 node = node->children;
403 /* remove my children */
404 while (node != NULL) {
405 if (node && node->data) {
406 FolderItem *sub_item = (FolderItem*) node->data;
408 folder_item_remove(sub_item);
413 if (item->cache != NULL) {
414 msgcache_destroy(item->cache);
418 hookdata.folder = item->folder;
419 hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_REMOVE_FOLDERITEM;
420 hookdata.item = item;
421 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
425 if (item->folder->node == node)
426 item->folder->node = NULL;
428 folder_item_destroy(item);
430 g_node_destroy(node);
433 void folder_item_remove_children(FolderItem *item)
437 g_return_if_fail(item != NULL);
438 g_return_if_fail(item->folder != NULL);
439 g_return_if_fail(item->node != NULL);
441 node = item->node->children;
442 while (node != NULL) {
444 folder_item_remove(FOLDER_ITEM(node->data));
449 void folder_item_destroy(FolderItem *item)
453 g_return_if_fail(item != NULL);
455 folder = item->folder;
457 if (folder->inbox == item)
458 folder->inbox = NULL;
459 else if (folder->outbox == item)
460 folder->outbox = NULL;
461 else if (folder->draft == item)
462 folder->draft = NULL;
463 else if (folder->queue == item)
464 folder->queue = NULL;
465 else if (folder->trash == item)
466 folder->trash = NULL;
470 folder_item_free_cache(item, TRUE);
472 folder_item_prefs_free(item->prefs);
476 if (item->folder != NULL) {
477 if(item->folder->klass->item_destroy) {
478 item->folder->klass->item_destroy(item->folder, item);
485 FolderItem *folder_item_parent(FolderItem *item)
487 g_return_val_if_fail(item != NULL, NULL);
488 g_return_val_if_fail(item->node != NULL, NULL);
490 if (item->node->parent == NULL)
492 return (FolderItem *) item->node->parent->data;
495 void folder_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
499 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
500 XMLAttr *attr = (XMLAttr *) cur->data;
502 if (!attr || !attr->name || !attr->value) continue;
503 if (!strcmp(attr->name, "type")) {
504 if (!g_ascii_strcasecmp(attr->value, "normal"))
505 item->stype = F_NORMAL;
506 else if (!g_ascii_strcasecmp(attr->value, "inbox"))
507 item->stype = F_INBOX;
508 else if (!g_ascii_strcasecmp(attr->value, "outbox"))
509 item->stype = F_OUTBOX;
510 else if (!g_ascii_strcasecmp(attr->value, "draft"))
511 item->stype = F_DRAFT;
512 else if (!g_ascii_strcasecmp(attr->value, "queue"))
513 item->stype = F_QUEUE;
514 else if (!g_ascii_strcasecmp(attr->value, "trash"))
515 item->stype = F_TRASH;
516 } else if (!strcmp(attr->name, "name")) {
518 item->name = g_strdup(attr->value);
519 } else if (!strcmp(attr->name, "path")) {
521 item->path = g_strdup(attr->value);
522 } else if (!strcmp(attr->name, "mtime"))
523 item->mtime = strtoul(attr->value, NULL, 10);
524 else if (!strcmp(attr->name, "new"))
525 item->new_msgs = atoi(attr->value);
526 else if (!strcmp(attr->name, "unread"))
527 item->unread_msgs = atoi(attr->value);
528 else if (!strcmp(attr->name, "unreadmarked"))
529 item->unreadmarked_msgs = atoi(attr->value);
530 else if (!strcmp(attr->name, "marked"))
531 item->marked_msgs = atoi(attr->value);
532 else if (!strcmp(attr->name, "order"))
533 item->order = atoi(attr->value);
534 else if (!strcmp(attr->name, "total"))
535 item->total_msgs = atoi(attr->value);
536 else if (!strcmp(attr->name, "no_sub"))
537 item->no_sub = *attr->value == '1' ? TRUE : FALSE;
538 else if (!strcmp(attr->name, "no_select"))
539 item->no_select = *attr->value == '1' ? TRUE : FALSE;
540 else if (!strcmp(attr->name, "collapsed"))
541 item->collapsed = *attr->value == '1' ? TRUE : FALSE;
542 else if (!strcmp(attr->name, "thread_collapsed"))
543 item->thread_collapsed = *attr->value == '1' ? TRUE : FALSE;
544 else if (!strcmp(attr->name, "threaded"))
545 item->threaded = *attr->value == '1' ? TRUE : FALSE;
546 else if (!strcmp(attr->name, "hidereadmsgs"))
547 item->hide_read_msgs = *attr->value == '1' ? TRUE : FALSE;
548 else if (!strcmp(attr->name, "reqretrcpt"))
549 item->ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
550 else if (!strcmp(attr->name, "sort_key")) {
551 if (!strcmp(attr->value, "none"))
552 item->sort_key = SORT_BY_NONE;
553 else if (!strcmp(attr->value, "number"))
554 item->sort_key = SORT_BY_NUMBER;
555 else if (!strcmp(attr->value, "size"))
556 item->sort_key = SORT_BY_SIZE;
557 else if (!strcmp(attr->value, "date"))
558 item->sort_key = SORT_BY_DATE;
559 else if (!strcmp(attr->value, "from"))
560 item->sort_key = SORT_BY_FROM;
561 else if (!strcmp(attr->value, "subject"))
562 item->sort_key = SORT_BY_SUBJECT;
563 else if (!strcmp(attr->value, "score"))
564 item->sort_key = SORT_BY_SCORE;
565 else if (!strcmp(attr->value, "label"))
566 item->sort_key = SORT_BY_LABEL;
567 else if (!strcmp(attr->value, "mark"))
568 item->sort_key = SORT_BY_MARK;
569 else if (!strcmp(attr->value, "unread"))
570 item->sort_key = SORT_BY_STATUS;
571 else if (!strcmp(attr->value, "mime"))
572 item->sort_key = SORT_BY_MIME;
573 else if (!strcmp(attr->value, "to"))
574 item->sort_key = SORT_BY_TO;
575 else if (!strcmp(attr->value, "locked"))
576 item->sort_key = SORT_BY_LOCKED;
577 } else if (!strcmp(attr->name, "sort_type")) {
578 if (!strcmp(attr->value, "ascending"))
579 item->sort_type = SORT_ASCENDING;
581 item->sort_type = SORT_DESCENDING;
582 } else if (!strcmp(attr->name, "account_id")) {
583 PrefsAccount *account;
585 account = account_find_from_id(atoi(attr->value));
587 g_warning("account_id: %s not found\n", attr->value);
589 item->account = account;
590 } else if (!strcmp(attr->name, "apply_sub"))
591 item->apply_sub = *attr->value == '1' ? TRUE : FALSE;
595 XMLTag *folder_item_get_xml(Folder *folder, FolderItem *item)
597 static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
598 "draft", "queue", "trash"};
599 static gchar *sort_key_str[] = {"none", "number", "size", "date",
600 "from", "subject", "score", "label",
601 "mark", "unread", "mime", "to",
606 tag = xml_tag_new("folderitem");
608 xml_tag_add_attr(tag, xml_attr_new("type", folder_item_stype_str[item->stype]));
610 xml_tag_add_attr(tag, xml_attr_new("name", item->name));
612 xml_tag_add_attr(tag, xml_attr_new("path", item->path));
614 xml_tag_add_attr(tag, xml_attr_new("no_sub", "1"));
616 xml_tag_add_attr(tag, xml_attr_new("no_select", "1"));
617 xml_tag_add_attr(tag, xml_attr_new("collapsed", item->collapsed && item->node->children ? "1" : "0"));
618 xml_tag_add_attr(tag, xml_attr_new("thread_collapsed", item->thread_collapsed ? "1" : "0"));
619 xml_tag_add_attr(tag, xml_attr_new("threaded", item->threaded ? "1" : "0"));
620 xml_tag_add_attr(tag, xml_attr_new("hidereadmsgs", item->hide_read_msgs ? "1" : "0"));
622 xml_tag_add_attr(tag, xml_attr_new("reqretrcpt", "1"));
624 if (item->sort_key != SORT_BY_NONE) {
625 xml_tag_add_attr(tag, xml_attr_new("sort_key", sort_key_str[item->sort_key]));
626 xml_tag_add_attr(tag, xml_attr_new("sort_type", item->sort_type == SORT_ASCENDING ? "ascending" : "descending"));
629 value = g_strdup_printf("%ld", (unsigned long int) item->mtime);
630 xml_tag_add_attr(tag, xml_attr_new("mtime", value));
632 xml_tag_add_attr(tag, xml_attr_new_int("new", item->new_msgs));
633 xml_tag_add_attr(tag, xml_attr_new_int("unread", item->unread_msgs));
634 xml_tag_add_attr(tag, xml_attr_new_int("unreadmarked", item->unreadmarked_msgs));
635 xml_tag_add_attr(tag, xml_attr_new_int("marked", item->marked_msgs));
636 xml_tag_add_attr(tag, xml_attr_new_int("total", item->total_msgs));
637 xml_tag_add_attr(tag, xml_attr_new_int("order", item->order));
640 xml_tag_add_attr(tag, xml_attr_new_int("account_id", item->account->account_id));
642 xml_tag_add_attr(tag, xml_attr_new("apply_sub", "1"));
647 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
649 g_return_if_fail(folder != NULL);
651 folder->ui_func = func;
652 folder->ui_func_data = data;
655 void folder_set_name(Folder *folder, const gchar *name)
657 g_return_if_fail(folder != NULL);
659 g_free(folder->name);
660 folder->name = name ? g_strdup(name) : NULL;
661 if (folder->node && folder->node->data) {
662 FolderItem *item = (FolderItem *)folder->node->data;
665 item->name = name ? g_strdup(name) : NULL;
669 void folder_set_sort(Folder *folder, guint sort)
671 g_return_if_fail(folder != NULL);
673 if (folder->sort != sort) {
674 folder_remove(folder);
680 static gboolean folder_tree_destroy_func(GNode *node, gpointer data) {
681 FolderItem *item = (FolderItem *) node->data;
683 folder_item_destroy(item);
687 void folder_tree_destroy(Folder *folder)
691 g_return_if_fail(folder != NULL);
695 prefs_filtering_clear_folder(folder);
698 g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
699 folder_tree_destroy_func, NULL);
700 g_node_destroy(node);
705 void folder_add(Folder *folder)
710 FolderUpdateData hookdata;
712 g_return_if_fail(folder != NULL);
714 for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
715 cur_folder = FOLDER(cur->data);
716 if (cur_folder->sort < folder->sort)
720 folder_list = g_list_insert(folder_list, folder, i);
722 hookdata.folder = folder;
723 hookdata.update_flags = FOLDER_ADD_FOLDER;
724 hookdata.item = NULL;
725 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
728 void folder_remove(Folder *folder)
730 FolderUpdateData hookdata;
732 g_return_if_fail(folder != NULL);
734 folder_list = g_list_remove(folder_list, folder);
736 hookdata.folder = folder;
737 hookdata.update_flags = FOLDER_REMOVE_FOLDER;
738 hookdata.item = NULL;
739 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
742 GList *folder_get_list(void)
747 gint folder_read_list(void)
753 path = folder_get_list_path();
754 if (!is_file_exist(path)) return -1;
755 node = xml_parse_file(path);
756 if (!node) return -1;
758 xmlnode = node->data;
759 if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
760 g_warning("wrong folder list\n");
765 cur = node->children;
766 while (cur != NULL) {
769 folder = folder_get_from_xml(cur);
773 folder_unloaded_list = g_slist_append(folder_unloaded_list,
774 (gpointer) xml_copy_tree(cur));
779 if (folder_list || folder_unloaded_list)
785 void folder_write_list(void)
796 path = folder_get_list_path();
797 if ((pfile = prefs_write_open(path)) == NULL) return;
799 if (xml_file_put_xml_decl(pfile->fp) < 0) {
800 prefs_file_close_revert(pfile);
801 g_warning("failed to start write folder list.\n");
804 tag = xml_tag_new("folderlist");
806 xmlnode = xml_node_new(tag, NULL);
808 rootnode = g_node_new(xmlnode);
810 for (list = folder_list; list != NULL; list = list->next) {
814 node = folder_get_xml_node(folder);
816 g_node_append(rootnode, node);
819 for (slist = folder_unloaded_list; slist != NULL; slist = g_slist_next(slist)) {
820 GNode *node = (GNode *) slist->data;
822 g_node_append(rootnode, (gpointer) xml_copy_tree(node));
825 if (xml_write_tree(rootnode, pfile->fp) < 0) {
826 prefs_file_close_revert(pfile);
827 g_warning("failed to write folder list.\n");
828 } else if (prefs_file_close(pfile) < 0) {
829 g_warning("failed to write folder list.\n");
831 xml_free_tree(rootnode);
834 static gboolean folder_scan_tree_func(GNode *node, gpointer data)
836 GHashTable *pptable = (GHashTable *)data;
837 FolderItem *item = (FolderItem *)node->data;
839 folder_item_restore_persist_prefs(item, pptable);
840 folder_item_scan_full(item, FALSE);
845 void folder_scan_tree(Folder *folder, gboolean rebuild)
848 FolderUpdateData hookdata;
849 Folder *old_folder = folder;
851 if (!folder->klass->scan_tree)
854 pptable = folder_persist_prefs_new(folder);
857 folder_remove(folder);
859 if (folder->klass->scan_tree(folder) < 0) {
861 folder_add(old_folder);
866 hookdata.folder = folder;
867 hookdata.update_flags = FOLDER_TREE_CHANGED;
868 hookdata.item = NULL;
869 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
871 g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_scan_tree_func, pptable);
872 folder_persist_prefs_free(pptable);
874 prefs_matcher_read_config();
879 static gboolean folder_restore_prefs_func(GNode *node, gpointer data)
881 GHashTable *pptable = (GHashTable *)data;
882 FolderItem *item = (FolderItem *)node->data;
884 folder_item_restore_persist_prefs(item, pptable);
889 void folder_fast_scan_tree(Folder *folder)
892 FolderUpdateData hookdata;
894 if (!folder->klass->scan_tree)
897 pptable = folder_persist_prefs_new(folder);
899 if (folder->klass->scan_tree(folder) < 0) {
903 hookdata.folder = folder;
904 hookdata.update_flags = FOLDER_TREE_CHANGED;
905 hookdata.item = NULL;
906 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
908 g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_restore_prefs_func, pptable);
909 folder_persist_prefs_free(pptable);
911 prefs_matcher_read_config();
916 FolderItem *folder_create_folder(FolderItem *parent, const gchar *name)
918 FolderItem *new_item;
920 new_item = parent->folder->klass->create_folder(parent->folder, parent, name);
922 FolderUpdateData hookdata;
924 new_item->cache = msgcache_new();
926 hookdata.folder = new_item->folder;
927 hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDERITEM;
928 hookdata.item = new_item;
929 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
935 gint folder_item_rename(FolderItem *item, gchar *newname)
939 g_return_val_if_fail(item != NULL, -1);
940 g_return_val_if_fail(newname != NULL, -1);
942 retval = item->folder->klass->rename_folder(item->folder, item, newname);
945 FolderItemUpdateData hookdata;
946 FolderUpdateData hookdata2;
948 hookdata.item = item;
949 hookdata.update_flags = F_ITEM_UPDATE_NAME;
951 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
953 hookdata2.folder = item->folder;
954 hookdata2.item = item;
955 hookdata2.update_flags = FOLDER_RENAME_FOLDERITEM;
956 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata2);
966 guint unreadmarked_msgs;
971 struct FuncToAllFoldersData
973 FolderItemFunc function;
977 static gboolean folder_func_to_all_folders_func(GNode *node, gpointer data)
980 struct FuncToAllFoldersData *function_data = (struct FuncToAllFoldersData *) data;
982 g_return_val_if_fail(node->data != NULL, FALSE);
984 item = FOLDER_ITEM(node->data);
985 g_return_val_if_fail(item != NULL, FALSE);
987 function_data->function(item, function_data->data);
992 void folder_func_to_all_folders(FolderItemFunc function, gpointer data)
996 struct FuncToAllFoldersData function_data;
998 function_data.function = function;
999 function_data.data = data;
1001 for (list = folder_list; list != NULL; list = list->next) {
1002 folder = FOLDER(list->data);
1004 g_node_traverse(folder->node, G_PRE_ORDER,
1006 folder_func_to_all_folders_func,
1011 static void folder_count_total_msgs_func(FolderItem *item, gpointer data)
1013 struct TotalMsgCount *count = (struct TotalMsgCount *)data;
1015 count->new_msgs += item->new_msgs;
1016 count->unread_msgs += item->unread_msgs;
1017 count->unreadmarked_msgs += item->unreadmarked_msgs;
1018 count->marked_msgs += item->marked_msgs;
1019 count->total_msgs += item->total_msgs;
1022 struct TotalMsgStatus
1030 static gboolean folder_get_status_full_all_func(GNode *node, gpointer data)
1033 struct TotalMsgStatus *status = (struct TotalMsgStatus *)data;
1036 g_return_val_if_fail(node->data != NULL, FALSE);
1038 item = FOLDER_ITEM(node->data);
1040 if (!item->path) return FALSE;
1042 status->new += item->new_msgs;
1043 status->unread += item->unread_msgs;
1044 status->total += item->total_msgs;
1047 id = folder_item_get_identifier(item);
1048 g_string_append_printf(status->str, "%5d %5d %5d %s\n",
1049 item->new_msgs, item->unread_msgs,
1050 item->total_msgs, id);
1057 static void folder_get_status_full_all(GString *str, guint *new, guint *unread,
1062 struct TotalMsgStatus status;
1064 status.new = status.unread = status.total = 0;
1067 debug_print("Counting total number of messages...\n");
1069 for (list = folder_list; list != NULL; list = list->next) {
1070 folder = FOLDER(list->data);
1072 g_node_traverse(folder->node, G_PRE_ORDER,
1074 folder_get_status_full_all_func,
1079 *unread = status.unread;
1080 *total = status.total;
1083 gchar *folder_get_status(GPtrArray *folders, gboolean full)
1085 guint new, unread, total;
1090 new = unread = total = 0;
1092 str = g_string_new(NULL);
1095 for (i = 0; i < folders->len; i++) {
1098 item = g_ptr_array_index(folders, i);
1099 new += item->new_msgs;
1100 unread += item->unread_msgs;
1101 total += item->total_msgs;
1106 id = folder_item_get_identifier(item);
1107 g_string_append_printf(str, "%5d %5d %5d %s\n",
1108 item->new_msgs, item->unread_msgs,
1109 item->total_msgs, id);
1114 folder_get_status_full_all(full ? str : NULL,
1115 &new, &unread, &total);
1119 g_string_append_printf(str, "%5d %5d %5d\n", new, unread, total);
1121 g_string_append_printf(str, "%d %d %d\n", new, unread, total);
1124 g_string_free(str, FALSE);
1129 void folder_count_total_msgs(guint *new_msgs, guint *unread_msgs,
1130 guint *unreadmarked_msgs, guint *marked_msgs,
1133 struct TotalMsgCount count;
1135 count.new_msgs = count.unread_msgs = count.unreadmarked_msgs = count.total_msgs = 0;
1137 debug_print("Counting total number of messages...\n");
1139 folder_func_to_all_folders(folder_count_total_msgs_func, &count);
1141 *new_msgs = count.new_msgs;
1142 *unread_msgs = count.unread_msgs;
1143 *unreadmarked_msgs = count.unreadmarked_msgs;
1144 *marked_msgs = count.marked_msgs;
1145 *total_msgs = count.total_msgs;
1148 Folder *folder_find_from_path(const gchar *path)
1153 for (list = folder_list; list != NULL; list = list->next) {
1154 folder = list->data;
1155 if ((FOLDER_TYPE(folder) == F_MH ||
1156 FOLDER_TYPE(folder) == F_MBOX) &&
1157 !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
1164 Folder *folder_find_from_name(const gchar *name, FolderClass *klass)
1169 for (list = folder_list; list != NULL; list = list->next) {
1170 folder = list->data;
1171 if (folder->klass == klass &&
1172 strcmp2(name, folder->name) == 0)
1179 static gboolean folder_item_find_func(GNode *node, gpointer data)
1181 FolderItem *item = node->data;
1183 const gchar *path = d[0];
1185 if (path_cmp(path, item->path) != 0)
1193 FolderItem *folder_find_item_from_path(const gchar *path)
1197 GList *list = folder_get_list();
1199 folder = list ? list->data:NULL;
1201 g_return_val_if_fail(folder != NULL, NULL);
1203 d[0] = (gpointer)path;
1205 while (d[1] == NULL && list) {
1206 folder = FOLDER(list->data);
1207 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1208 folder_item_find_func, d);
1214 FolderItem *folder_find_child_item_by_name(FolderItem *item, const gchar *name)
1219 for (node = item->node->children; node != NULL; node = node->next) {
1221 child = FOLDER_ITEM(node->data);
1222 base = g_path_get_basename(child->path);
1223 if (strcmp2(base, name) == 0) {
1233 FolderClass *folder_get_class_from_string(const gchar *str)
1237 classlist = folder_get_class_list();
1238 for (; classlist != NULL; classlist = g_slist_next(classlist)) {
1239 FolderClass *class = (FolderClass *) classlist->data;
1240 if (g_ascii_strcasecmp(class->idstr, str) == 0)
1247 static gchar *folder_get_identifier(Folder *folder)
1251 g_return_val_if_fail(folder != NULL, NULL);
1253 type_str = folder->klass->idstr;
1254 return g_strconcat("#", type_str, "/", folder->name, NULL);
1257 gchar *folder_item_get_identifier(FolderItem *item)
1260 gchar *folder_id = NULL;
1262 g_return_val_if_fail(item != NULL, NULL);
1264 if (item->path == NULL)
1267 folder_id = folder_get_identifier(item->folder);
1268 id = g_strconcat(folder_id, "/", item->path, NULL);
1274 FolderItem *folder_find_item_from_identifier(const gchar *identifier)
1284 g_return_val_if_fail(identifier != NULL, NULL);
1286 if (*identifier != '#')
1287 return folder_find_item_from_path(identifier);
1289 Xstrdup_a(str, identifier, return NULL);
1291 p = strchr(str, '/');
1293 return folder_find_item_from_path(identifier);
1296 class = folder_get_class_from_string(&str[1]);
1298 return folder_find_item_from_path(identifier);
1303 return folder_find_item_from_path(identifier);
1307 folder = folder_find_from_name(name, class);
1309 return folder_find_item_from_path(identifier);
1313 d[0] = (gpointer)path;
1315 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1316 folder_item_find_func, d);
1321 * Get a displayable name for a FolderItem
1323 * \param item FolderItem for that a name should be created
1324 * \return Displayable name for item, returned string has to
1327 gchar *folder_item_get_name(FolderItem *item)
1331 g_return_val_if_fail(item != NULL, g_strdup(""));
1333 switch (item->stype) {
1335 name = g_strdup(!strcmp2(item->name, INBOX_DIR) ? _("Inbox") :
1339 name = g_strdup(!strcmp2(item->name, OUTBOX_DIR) ? _("Sent") :
1343 name = g_strdup(!strcmp2(item->name, QUEUE_DIR) ? _("Queue") :
1347 name = g_strdup(!strcmp2(item->name, TRASH_DIR) ? _("Trash") :
1351 name = g_strdup(!strcmp2(item->name, DRAFT_DIR) ? _("Drafts") :
1360 * should probably be done by a virtual function,
1361 * the folder knows the ui string and how to abbrev
1363 if (folder_item_parent(item) == NULL) {
1364 name = g_strconcat(item->name, " (", item->folder->klass->uistr, ")", NULL);
1366 if (FOLDER_CLASS(item->folder) == news_get_class() &&
1367 item->path && !strcmp2(item->name, item->path))
1368 name = get_abbrev_newsgroup_name
1370 prefs_common.ng_abbrev_len);
1372 name = g_strdup(item->name);
1377 name = g_strdup("");
1382 gboolean folder_have_mailbox (void)
1385 for (cur = folder_list; cur != NULL; cur = g_list_next(cur)) {
1386 Folder *folder = FOLDER(cur->data);
1387 if (folder->inbox && folder->outbox)
1393 FolderItem *folder_get_default_inbox(void)
1397 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1398 Folder * folder = FOLDER(flist->data);
1402 if (folder->inbox == NULL)
1404 if (folder->klass->type == F_UNKNOWN)
1407 return folder->inbox;
1413 FolderItem *folder_get_default_inbox_for_class(FolderType type)
1417 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1418 Folder * folder = FOLDER(flist->data);
1422 if (folder->inbox == NULL)
1424 if (folder->klass->type != type)
1427 return folder->inbox;
1433 FolderItem *folder_get_default_outbox(void)
1437 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1438 Folder * folder = FOLDER(flist->data);
1442 if (folder->outbox == NULL)
1444 if (folder->klass->type == F_UNKNOWN)
1447 return folder->outbox;
1453 FolderItem *folder_get_default_draft(void)
1457 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1458 Folder * folder = FOLDER(flist->data);
1462 if (folder->draft == NULL)
1464 if (folder->klass->type == F_UNKNOWN)
1467 return folder->draft;
1473 FolderItem *folder_get_default_queue(void)
1477 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1478 Folder * folder = FOLDER(flist->data);
1482 if (folder->queue == NULL)
1484 if (folder->klass->type == F_UNKNOWN)
1487 return folder->queue;
1493 FolderItem *folder_get_default_trash(void)
1497 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1498 Folder * folder = FOLDER(flist->data);
1502 if (folder->trash == NULL)
1504 if (folder->klass->type == F_UNKNOWN)
1507 return folder->trash;
1513 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type) \
1515 if (!folder->member) { \
1516 item = folder_item_new(folder, dir, dir); \
1517 item->stype = type; \
1518 folder_item_append(rootitem, item); \
1519 folder->member = item; \
1523 void folder_set_missing_folders(void)
1526 FolderItem *rootitem;
1530 for (list = folder_list; list != NULL; list = list->next) {
1531 folder = list->data;
1532 if (FOLDER_TYPE(folder) != F_MH) continue;
1533 rootitem = FOLDER_ITEM(folder->node->data);
1534 g_return_if_fail(rootitem != NULL);
1536 if (folder->inbox && folder->outbox && folder->draft &&
1537 folder->queue && folder->trash)
1540 if (folder->klass->create_tree(folder) < 0) {
1541 g_warning("%s: can't create the folder tree.\n",
1542 LOCAL_FOLDER(folder)->rootpath);
1546 CREATE_FOLDER_IF_NOT_EXIST(inbox, INBOX_DIR, F_INBOX);
1547 CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
1548 CREATE_FOLDER_IF_NOT_EXIST(draft, DRAFT_DIR, F_DRAFT);
1549 CREATE_FOLDER_IF_NOT_EXIST(queue, QUEUE_DIR, F_QUEUE);
1550 CREATE_FOLDER_IF_NOT_EXIST(trash, TRASH_DIR, F_TRASH);
1554 static gboolean folder_unref_account_func(GNode *node, gpointer data)
1556 FolderItem *item = node->data;
1557 PrefsAccount *account = data;
1559 if (item->account == account)
1560 item->account = NULL;
1565 void folder_unref_account_all(PrefsAccount *account)
1570 if (!account) return;
1572 for (list = folder_list; list != NULL; list = list->next) {
1573 folder = list->data;
1574 if (folder->account == account)
1575 folder->account = NULL;
1576 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1577 folder_unref_account_func, account);
1581 #undef CREATE_FOLDER_IF_NOT_EXIST
1583 gchar *folder_item_get_path(FolderItem *item)
1587 g_return_val_if_fail(item != NULL, NULL);
1588 folder = item->folder;
1589 g_return_val_if_fail(folder != NULL, NULL);
1591 return folder->klass->item_get_path(folder, item);
1594 void folder_item_set_default_flags(FolderItem *dest, MsgFlags *flags)
1596 if (!(folder_has_parent_of_type(dest, F_OUTBOX) ||
1597 folder_has_parent_of_type(dest, F_QUEUE) ||
1598 folder_has_parent_of_type(dest, F_DRAFT) ||
1599 folder_has_parent_of_type(dest, F_TRASH))) {
1600 flags->perm_flags = MSG_NEW|MSG_UNREAD;
1602 flags->perm_flags = 0;
1604 if (FOLDER_TYPE(dest->folder) == F_MH) {
1605 if (folder_has_parent_of_type(dest, F_QUEUE)) {
1606 MSG_SET_TMP_FLAGS(*flags, MSG_QUEUED);
1607 } else if (folder_has_parent_of_type(dest, F_DRAFT)) {
1608 MSG_SET_TMP_FLAGS(*flags, MSG_DRAFT);
1613 static gint folder_sort_cache_list_by_msgnum(gconstpointer a, gconstpointer b)
1615 MsgInfo *msginfo_a = (MsgInfo *) a;
1616 MsgInfo *msginfo_b = (MsgInfo *) b;
1618 return (msginfo_a->msgnum - msginfo_b->msgnum);
1621 static gint folder_sort_folder_list(gconstpointer a, gconstpointer b)
1623 guint gint_a = GPOINTER_TO_INT(a);
1624 guint gint_b = GPOINTER_TO_INT(b);
1626 return (gint_a - gint_b);
1629 void folder_item_process_open (FolderItem *item,
1630 void (*before_proc_func)(gpointer data),
1631 void (*after_proc_func)(gpointer data),
1637 if((item->folder->klass->scan_required != NULL) &&
1638 (item->folder->klass->scan_required(item->folder, item))) {
1639 folder_item_scan_full(item, TRUE);
1641 folder_item_syncronize_flags(item);
1645 buf = g_strdup_printf(_("Processing (%s)...\n"),
1646 item->path ? item->path : item->name);
1649 if (before_proc_func)
1650 before_proc_func(data);
1652 folder_item_apply_processing(item);
1654 if (after_proc_func)
1655 after_proc_func(data);
1657 item->processing_pending = FALSE;
1661 gint folder_item_open(FolderItem *item)
1664 if (item->no_select)
1667 if (item->scanning) {
1668 debug_print("%s is scanning... \n", item->path ? item->path : item->name);
1672 item->processing_pending = TRUE;
1673 folder_item_process_open (item, NULL, NULL, NULL);
1675 item->opened = TRUE;
1680 gint folder_item_close(FolderItem *item)
1682 GSList *mlist, *cur;
1685 g_return_val_if_fail(item != NULL, -1);
1687 if (item->no_select)
1690 if (item->new_msgs) {
1691 folder_item_update_freeze();
1692 mlist = folder_item_get_msg_list(item);
1693 for (cur = mlist ; cur != NULL ; cur = cur->next) {
1696 msginfo = (MsgInfo *) cur->data;
1697 if (MSG_IS_NEW(msginfo->flags))
1698 procmsg_msginfo_unset_flags(msginfo, MSG_NEW, 0);
1699 procmsg_msginfo_free(msginfo);
1701 g_slist_free(mlist);
1702 folder_item_update_thaw();
1705 folder_item_write_cache(item);
1707 folder_item_update(item, F_ITEM_UPDATE_MSGCNT);
1709 item->opened = FALSE;
1710 folder = item->folder;
1712 if (folder->klass->close == NULL)
1715 return folder->klass->close(folder, item);
1718 static MsgInfoList *get_msginfos(FolderItem *item, MsgNumberList *numlist)
1720 MsgInfoList *msglist = NULL;
1721 Folder *folder = item->folder;
1722 if (item->no_select)
1725 if (folder->klass->get_msginfos != NULL)
1726 msglist = folder->klass->get_msginfos(folder, item, numlist);
1728 MsgNumberList *elem;
1730 for (elem = numlist; elem != NULL; elem = g_slist_next(elem)) {
1734 num = GPOINTER_TO_INT(elem->data);
1735 msginfo = folder->klass->get_msginfo(folder, item, num);
1736 if (msginfo != NULL)
1737 msglist = g_slist_prepend(msglist, msginfo);
1744 static MsgInfo *get_msginfo(FolderItem *item, guint num)
1746 MsgNumberList numlist;
1747 MsgInfoList *msglist;
1748 MsgInfo *msginfo = NULL;
1750 numlist.data = GINT_TO_POINTER(num);
1751 numlist.next = NULL;
1752 msglist = get_msginfos(item, &numlist);
1753 if (msglist != NULL)
1754 msginfo = procmsg_msginfo_new_ref(msglist->data);
1755 procmsg_msg_list_free(msglist);
1760 static gint syncronize_flags(FolderItem *item, MsgInfoList *msglist)
1762 GRelation *relation;
1768 if(item->folder->klass->get_flags == NULL)
1770 if (item->no_select)
1773 relation = g_relation_new(2);
1774 g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
1775 folder_item_set_batch(item, TRUE);
1776 if ((ret = item->folder->klass->get_flags(
1777 item->folder, item, msglist, relation)) == 0) {
1780 MsgPermFlags permflags = 0;
1783 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1784 msginfo = (MsgInfo *) cur->data;
1786 tuples = g_relation_select(relation, msginfo, 0);
1787 skip = tuples->len < 1;
1789 permflags = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1790 g_tuples_destroy(tuples);
1794 if (msginfo->flags.perm_flags != permflags) {
1795 procmsg_msginfo_change_flags(msginfo,
1796 permflags & ~msginfo->flags.perm_flags, 0,
1797 ~permflags & msginfo->flags.perm_flags, 0);
1801 folder_item_set_batch(item, FALSE);
1802 g_relation_destroy(relation);
1807 gint folder_item_scan_full(FolderItem *item, gboolean filtering)
1810 GSList *folder_list = NULL, *cache_list = NULL;
1811 GSList *folder_list_cur, *cache_list_cur, *new_list = NULL;
1812 GSList *exists_list = NULL, *elem;
1813 GSList *newmsg_list = NULL;
1814 guint newcnt = 0, unreadcnt = 0, totalcnt = 0;
1815 guint markedcnt = 0, unreadmarkedcnt = 0;
1816 guint cache_max_num, folder_max_num, cache_cur_num, folder_cur_num;
1817 gboolean update_flags = 0, old_uids_valid = FALSE;
1818 GHashTable *subject_table = NULL;
1820 g_return_val_if_fail(item != NULL, -1);
1821 if (item->path == NULL) return -1;
1823 folder = item->folder;
1825 g_return_val_if_fail(folder != NULL, -1);
1826 g_return_val_if_fail(folder->klass->get_num_list != NULL, -1);
1828 item->scanning = TRUE;
1830 debug_print("Scanning folder %s for cache changes.\n", item->path ? item->path : "(null)");
1832 /* Get list of messages for folder and cache */
1833 if (folder->klass->get_num_list(item->folder, item, &folder_list, &old_uids_valid) < 0) {
1834 debug_print("Error fetching list of message numbers\n");
1835 item->scanning = FALSE;
1839 if(prefs_common.thread_by_subject) {
1840 subject_table = g_hash_table_new(g_str_hash, g_str_equal);
1843 if (old_uids_valid) {
1845 folder_item_read_cache(item);
1846 cache_list = msgcache_get_msg_list(item->cache);
1849 msgcache_destroy(item->cache);
1850 item->cache = msgcache_new();
1854 /* Sort both lists */
1855 cache_list = g_slist_sort(cache_list, folder_sort_cache_list_by_msgnum);
1856 folder_list = g_slist_sort(folder_list, folder_sort_folder_list);
1858 cache_list_cur = cache_list;
1859 folder_list_cur = folder_list;
1861 if (cache_list_cur != NULL) {
1862 GSList *cache_list_last;
1864 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1865 cache_list_last = g_slist_last(cache_list);
1866 cache_max_num = ((MsgInfo *)cache_list_last->data)->msgnum;
1868 cache_cur_num = G_MAXINT;
1872 if (folder_list_cur != NULL) {
1873 GSList *folder_list_last;
1875 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1876 folder_list_last = g_slist_last(folder_list);
1877 folder_max_num = GPOINTER_TO_INT(folder_list_last->data);
1879 folder_cur_num = G_MAXINT;
1883 while ((cache_cur_num != G_MAXINT) || (folder_cur_num != G_MAXINT)) {
1885 * Message only exists in the folder
1886 * Remember message for fetching
1888 if (folder_cur_num < cache_cur_num) {
1889 gboolean add = FALSE;
1891 switch(FOLDER_TYPE(folder)) {
1893 if (folder_cur_num < cache_max_num)
1896 if (folder->account->max_articles == 0) {
1900 if (folder_max_num <= folder->account->max_articles) {
1902 } else if (folder_cur_num > (folder_max_num - folder->account->max_articles)) {
1912 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(folder_cur_num));
1913 debug_print("Remembered message %d for fetching\n", folder_cur_num);
1916 /* Move to next folder number */
1917 folder_list_cur = folder_list_cur->next;
1919 if (folder_list_cur != NULL)
1920 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1922 folder_cur_num = G_MAXINT;
1928 * Message only exists in the cache
1929 * Remove the message from the cache
1931 if (cache_cur_num < folder_cur_num) {
1932 msgcache_remove_msg(item->cache, cache_cur_num);
1933 debug_print("Removed message %d from cache.\n", cache_cur_num);
1935 /* Move to next cache number */
1936 cache_list_cur = cache_list_cur->next;
1938 if (cache_list_cur != NULL)
1939 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1941 cache_cur_num = G_MAXINT;
1943 update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
1949 * Message number exists in folder and cache!
1950 * Check if the message has been modified
1952 if (cache_cur_num == folder_cur_num) {
1955 msginfo = msgcache_get_msg(item->cache, folder_cur_num);
1956 if (folder->klass->is_msg_changed && folder->klass->is_msg_changed(folder, item, msginfo)) {
1957 msgcache_remove_msg(item->cache, msginfo->msgnum);
1958 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(msginfo->msgnum));
1959 procmsg_msginfo_free(msginfo);
1961 debug_print("Remembering message %d to update...\n", folder_cur_num);
1963 exists_list = g_slist_prepend(exists_list, msginfo);
1965 if(prefs_common.thread_by_subject &&
1966 MSG_IS_IGNORE_THREAD(msginfo->flags) &&
1967 !subject_table_lookup(subject_table, msginfo->subject)) {
1968 subject_table_insert(subject_table, msginfo->subject, msginfo);
1972 /* Move to next folder and cache number */
1973 cache_list_cur = cache_list_cur->next;
1974 folder_list_cur = folder_list_cur->next;
1976 if (cache_list_cur != NULL)
1977 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1979 cache_cur_num = G_MAXINT;
1981 if (folder_list_cur != NULL)
1982 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1984 folder_cur_num = G_MAXINT;
1990 for(cache_list_cur = cache_list; cache_list_cur != NULL; cache_list_cur = g_slist_next(cache_list_cur))
1991 procmsg_msginfo_free((MsgInfo *) cache_list_cur->data);
1993 g_slist_free(cache_list);
1994 g_slist_free(folder_list);
1996 if (new_list != NULL) {
1997 GSList *tmp_list = NULL;
1998 newmsg_list = get_msginfos(item, new_list);
1999 g_slist_free(new_list);
2000 tmp_list = g_slist_concat(g_slist_copy(exists_list), g_slist_copy(newmsg_list));
2001 syncronize_flags(item, tmp_list);
2002 g_slist_free(tmp_list);
2004 syncronize_flags(item, exists_list);
2007 folder_item_update_freeze();
2008 if (newmsg_list != NULL) {
2009 GSList *elem, *to_filter = NULL;
2010 gboolean do_filter = (filtering == TRUE) &&
2011 (item->stype == F_INBOX) &&
2012 (item->folder->account != NULL) &&
2013 (item->folder->account->filter_on_recv);
2015 for (elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) {
2016 MsgInfo *msginfo = (MsgInfo *) elem->data;
2018 msgcache_add_msg(item->cache, msginfo);
2020 exists_list = g_slist_prepend(exists_list, msginfo);
2022 if(prefs_common.thread_by_subject &&
2023 MSG_IS_IGNORE_THREAD(msginfo->flags) &&
2024 !subject_table_lookup(subject_table, msginfo->subject)) {
2025 subject_table_insert(subject_table, msginfo->subject, msginfo);
2032 procmsg_msglist_filter(newmsg_list, item->folder->account,
2033 &to_filter, &unfiltered,
2036 filtering_move_and_copy_msgs(newmsg_list);
2037 if (to_filter != NULL) {
2038 for (elem = to_filter; elem; elem = g_slist_next(elem)) {
2039 MsgInfo *msginfo = (MsgInfo *)elem->data;
2040 procmsg_msginfo_free(msginfo);
2042 g_slist_free(to_filter);
2044 if (unfiltered != NULL) {
2045 for (elem = unfiltered; elem; elem = g_slist_next(elem)) {
2046 MsgInfo *msginfo = (MsgInfo *)elem->data;
2047 exists_list = g_slist_prepend(exists_list, msginfo);
2049 if(prefs_common.thread_by_subject &&
2050 MSG_IS_IGNORE_THREAD(msginfo->flags) &&
2051 !subject_table_lookup(subject_table, msginfo->subject)) {
2052 subject_table_insert(subject_table, msginfo->subject, msginfo);
2055 g_slist_free(unfiltered);
2059 g_slist_free(newmsg_list);
2061 update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
2064 folder_item_set_batch(item, TRUE);
2065 for (elem = exists_list; elem != NULL; elem = g_slist_next(elem)) {
2066 MsgInfo *msginfo, *parent_msginfo;
2068 msginfo = elem->data;
2069 if (MSG_IS_IGNORE_THREAD(msginfo->flags) && (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
2070 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
2071 if (!MSG_IS_IGNORE_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD)) {
2072 procmsg_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0, MSG_NEW | MSG_UNREAD, 0);
2074 if(prefs_common.thread_by_subject && !msginfo->inreplyto &&
2075 !msginfo->references && !MSG_IS_IGNORE_THREAD(msginfo->flags) &&
2076 (parent_msginfo = subject_table_lookup(subject_table, msginfo->subject)))
2078 if(MSG_IS_IGNORE_THREAD(parent_msginfo->flags)) {
2079 procmsg_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0,
2080 MSG_NEW | MSG_UNREAD, 0);
2083 if ((folder_has_parent_of_type(item, F_OUTBOX) ||
2084 folder_has_parent_of_type(item, F_QUEUE) ||
2085 folder_has_parent_of_type(item, F_DRAFT) ||
2086 folder_has_parent_of_type(item, F_TRASH)) &&
2087 (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
2088 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
2089 if (MSG_IS_NEW(msginfo->flags))
2091 if (MSG_IS_UNREAD(msginfo->flags))
2093 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
2095 if (MSG_IS_MARKED(msginfo->flags))
2100 procmsg_msginfo_free(msginfo);
2102 folder_item_set_batch(item, FALSE);
2103 g_slist_free(exists_list);
2105 if(prefs_common.thread_by_subject) {
2106 g_hash_table_destroy(subject_table);
2109 if (item->new_msgs != newcnt || item->unread_msgs != unreadcnt
2110 || item->total_msgs != totalcnt || item->marked_msgs != markedcnt
2111 || item->unreadmarked_msgs != unreadmarkedcnt) {
2112 update_flags |= F_ITEM_UPDATE_CONTENT;
2115 item->new_msgs = newcnt;
2116 item->unread_msgs = unreadcnt;
2117 item->total_msgs = totalcnt;
2118 item->unreadmarked_msgs = unreadmarkedcnt;
2119 item->marked_msgs = markedcnt;
2121 update_flags |= F_ITEM_UPDATE_MSGCNT;
2123 folder_item_update(item, update_flags);
2124 folder_item_update_thaw();
2126 item->scanning = FALSE;
2131 gint folder_item_scan(FolderItem *item)
2133 return folder_item_scan_full(item, TRUE);
2136 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
2139 folder_item_scan(FOLDER_ITEM(key));
2142 void folder_item_scan_foreach(GHashTable *table)
2144 g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
2147 static void folder_count_total_cache_memusage(FolderItem *item, gpointer data)
2149 gint *memusage = (gint *)data;
2151 if (item->cache == NULL)
2154 *memusage += msgcache_get_memory_usage(item->cache);
2157 gint folder_item_syncronize_flags(FolderItem *item)
2159 MsgInfoList *msglist = NULL;
2163 g_return_val_if_fail(item != NULL, -1);
2164 g_return_val_if_fail(item->folder != NULL, -1);
2165 g_return_val_if_fail(item->folder->klass != NULL, -1);
2166 if (item->no_select)
2169 item->scanning = TRUE;
2171 if (item->cache == NULL)
2172 folder_item_read_cache(item);
2174 msglist = msgcache_get_msg_list(item->cache);
2176 ret = syncronize_flags(item, msglist);
2178 for (cur = msglist; cur != NULL; cur = g_slist_next(cur))
2179 procmsg_msginfo_free((MsgInfo *) cur->data);
2181 g_slist_free(msglist);
2183 item->scanning = FALSE;
2188 static gint folder_cache_time_compare_func(gconstpointer a, gconstpointer b)
2190 FolderItem *fa = (FolderItem *)a;
2191 FolderItem *fb = (FolderItem *)b;
2193 return (gint) (msgcache_get_last_access_time(fa->cache) - msgcache_get_last_access_time(fb->cache));
2196 static void folder_find_expired_caches(FolderItem *item, gpointer data)
2198 GSList **folder_item_list = (GSList **)data;
2199 gint difftime, expiretime;
2201 if (item->cache == NULL)
2204 if (item->opened > 0)
2207 difftime = (gint) (time(NULL) - msgcache_get_last_access_time(item->cache));
2208 expiretime = prefs_common.cache_min_keep_time * 60;
2209 debug_print("Cache unused time: %d (Expire time: %d)\n", difftime, expiretime);
2211 if (difftime > expiretime && !item->opened && !item->processing_pending) {
2212 *folder_item_list = g_slist_insert_sorted(*folder_item_list, item, folder_cache_time_compare_func);
2216 gboolean folder_item_free_cache(FolderItem *item, gboolean force)
2218 g_return_val_if_fail(item != NULL, TRUE);
2220 if (item->cache == NULL)
2223 if (item->opened > 0 && !force)
2226 folder_item_write_cache(item);
2227 msgcache_destroy(item->cache);
2232 void folder_clean_cache_memory_force(void)
2234 int old_cache_max_mem_usage = prefs_common.cache_max_mem_usage;
2235 int old_cache_min_keep_time = prefs_common.cache_min_keep_time;
2237 prefs_common.cache_max_mem_usage = 0;
2238 prefs_common.cache_min_keep_time = 0;
2240 folder_clean_cache_memory(NULL);
2242 prefs_common.cache_max_mem_usage = old_cache_max_mem_usage;
2243 prefs_common.cache_min_keep_time = old_cache_min_keep_time;
2246 void folder_clean_cache_memory(FolderItem *protected_item)
2250 folder_func_to_all_folders(folder_count_total_cache_memusage, &memusage);
2251 debug_print("Total cache memory usage: %d\n", memusage);
2253 if (memusage > (prefs_common.cache_max_mem_usage * 1024)) {
2254 GSList *folder_item_list = NULL, *listitem;
2256 debug_print("Trying to free cache memory\n");
2258 folder_func_to_all_folders(folder_find_expired_caches, &folder_item_list);
2259 listitem = folder_item_list;
2260 while((listitem != NULL) && (memusage > (prefs_common.cache_max_mem_usage * 1024))) {
2261 FolderItem *item = (FolderItem *)(listitem->data);
2262 gint cache_size = 0;
2263 if (item == protected_item) {
2264 listitem = listitem->next;
2267 debug_print("Freeing cache memory for %s\n", item->path ? item->path : item->name);
2268 cache_size = msgcache_get_memory_usage(item->cache);
2269 if (folder_item_free_cache(item, FALSE))
2270 memusage -= cache_size;
2272 listitem = listitem->next;
2274 g_slist_free(folder_item_list);
2278 static void folder_item_read_cache(FolderItem *item)
2280 gchar *cache_file, *mark_file;
2282 g_return_if_fail(item != NULL);
2284 if (item->path != NULL) {
2285 cache_file = folder_item_get_cache_file(item);
2286 mark_file = folder_item_get_mark_file(item);
2287 item->cache = msgcache_read_cache(item, cache_file);
2289 MsgInfoList *list, *cur;
2290 guint newcnt = 0, unreadcnt = 0;
2291 guint markedcnt = 0, unreadmarkedcnt = 0;
2294 item->cache = msgcache_new();
2295 folder_item_scan_full(item, TRUE);
2297 msgcache_read_mark(item->cache, mark_file);
2299 list = msgcache_get_msg_list(item->cache);
2300 for (cur = list; cur != NULL; cur = g_slist_next(cur)) {
2301 msginfo = cur->data;
2303 if (MSG_IS_NEW(msginfo->flags))
2305 if (MSG_IS_UNREAD(msginfo->flags))
2307 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
2309 if (MSG_IS_MARKED(msginfo->flags))
2312 item->new_msgs = newcnt;
2313 item->unread_msgs = unreadcnt;
2314 item->unreadmarked_msgs = unreadmarkedcnt;
2315 item->marked_msgs = markedcnt;
2316 procmsg_msg_list_free(list);
2318 msgcache_read_mark(item->cache, mark_file);
2323 item->cache = msgcache_new();
2326 folder_clean_cache_memory(item);
2329 void folder_item_write_cache(FolderItem *item)
2331 gchar *cache_file, *mark_file;
2332 FolderItemPrefs *prefs;
2335 time_t last_mtime = (time_t)0;
2336 gboolean need_scan = FALSE;
2338 if (!item || !item->path || !item->cache)
2341 if (FOLDER_TYPE(item->folder) == F_MH) {
2342 last_mtime = item->mtime;
2343 need_scan = item->folder->klass->scan_required(item->folder, item);
2346 id = folder_item_get_identifier(item);
2347 debug_print("Save cache for folder %s\n", id);
2350 cache_file = folder_item_get_cache_file(item);
2351 mark_file = folder_item_get_mark_file(item);
2352 if (msgcache_write(cache_file, mark_file, item->cache) < 0) {
2353 prefs = item->prefs;
2354 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
2355 /* for cache file */
2356 filemode = prefs->folder_chmod;
2357 if (filemode & S_IRGRP) filemode |= S_IWGRP;
2358 if (filemode & S_IROTH) filemode |= S_IWOTH;
2359 chmod(cache_file, filemode);
2363 if (!need_scan && FOLDER_TYPE(item->folder) == F_MH) {
2364 if (item->mtime == last_mtime) {
2373 MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
2376 MsgInfo *msginfo = NULL;
2378 g_return_val_if_fail(item != NULL, NULL);
2379 if (item->no_select)
2381 folder = item->folder;
2383 folder_item_read_cache(item);
2385 if ((msginfo = msgcache_get_msg(item->cache, num)) != NULL)
2388 msginfo = get_msginfo(item, num);
2389 if (msginfo != NULL) {
2390 msgcache_add_msg(item->cache, msginfo);
2397 MsgInfo *folder_item_get_msginfo_by_msgid(FolderItem *item, const gchar *msgid)
2402 g_return_val_if_fail(item != NULL, NULL);
2403 g_return_val_if_fail(msgid != NULL, NULL);
2404 if (item->no_select)
2407 folder = item->folder;
2409 folder_item_read_cache(item);
2411 if ((msginfo = msgcache_get_msg_by_id(item->cache, msgid)) != NULL)
2417 GSList *folder_item_get_msg_list(FolderItem *item)
2419 g_return_val_if_fail(item != NULL, NULL);
2420 if (item->no_select)
2423 if (item->cache == 0)
2424 folder_item_read_cache(item);
2426 g_return_val_if_fail(item->cache != NULL, NULL);
2428 return msgcache_get_msg_list(item->cache);
2431 static void msginfo_set_mime_flags(GNode *node, gpointer data)
2433 MsgInfo *msginfo = data;
2434 MimeInfo *mimeinfo = node->data;
2436 if (mimeinfo->disposition == DISPOSITIONTYPE_ATTACHMENT
2437 && (!mimeinfo->subtype || strcmp(mimeinfo->subtype, "pgp-signature"))) {
2438 procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT);
2439 } else if (mimeinfo->disposition == DISPOSITIONTYPE_UNKNOWN &&
2440 mimeinfo->type != MIMETYPE_TEXT &&
2441 mimeinfo->type != MIMETYPE_MULTIPART) {
2442 if (!mimeinfo->subtype
2443 || strcmp(mimeinfo->subtype, "pgp-signature"))
2444 procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT);
2445 } else if (mimeinfo->disposition == DISPOSITIONTYPE_INLINE &&
2446 strcmp(mimeinfo->subtype, "pgp-signature") &&
2447 (procmime_mimeinfo_get_parameter(mimeinfo, "name") != NULL ||
2448 procmime_mimeinfo_get_parameter(mimeinfo, "filename") != NULL)) {
2449 procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT);
2452 /* don't descend below top level message for signed and encrypted info */
2453 if (mimeinfo->type == MIMETYPE_MESSAGE)
2456 if (privacy_mimeinfo_is_signed(mimeinfo)) {
2457 procmsg_msginfo_set_flags(msginfo, 0, MSG_SIGNED);
2460 if (privacy_mimeinfo_is_encrypted(mimeinfo)) {
2461 procmsg_msginfo_set_flags(msginfo, 0, MSG_ENCRYPTED);
2463 /* searching inside encrypted parts doesn't really make sense */
2464 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2468 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
2474 g_return_val_if_fail(item != NULL, NULL);
2476 folder = item->folder;
2478 g_return_val_if_fail(folder->klass->fetch_msg != NULL, NULL);
2479 if (item->no_select)
2482 msgfile = folder->klass->fetch_msg(folder, item, num);
2484 if (msgfile != NULL) {
2485 msginfo = folder_item_get_msginfo(item, num);
2486 if ((msginfo != NULL) && !MSG_IS_SCANNED(msginfo->flags)) {
2489 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2490 !folder_has_parent_of_type(msginfo->folder, F_DRAFT))
2491 mimeinfo = procmime_scan_file(msgfile);
2493 mimeinfo = procmime_scan_queue_file(msgfile);
2494 /* check for attachments */
2495 if (mimeinfo != NULL) {
2496 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2497 procmime_mimeinfo_free_all(mimeinfo);
2499 procmsg_msginfo_set_flags(msginfo, 0, MSG_SCANNED);
2502 procmsg_msginfo_free(msginfo);
2508 gchar *folder_item_fetch_msg_full(FolderItem *item, gint num, gboolean headers,
2515 g_return_val_if_fail(item != NULL, NULL);
2516 if (item->no_select)
2519 folder = item->folder;
2521 if (folder->klass->fetch_msg_full == NULL)
2522 return folder_item_fetch_msg(item, num);
2524 msgfile = folder->klass->fetch_msg_full(folder, item, num,
2527 if (msgfile != NULL) {
2528 msginfo = folder_item_get_msginfo(item, num);
2529 if ((msginfo != NULL) && !MSG_IS_SCANNED(msginfo->flags)) {
2532 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2533 !folder_has_parent_of_type(msginfo->folder, F_DRAFT))
2534 mimeinfo = procmime_scan_file(msgfile);
2536 mimeinfo = procmime_scan_queue_file(msgfile);
2537 /* check for attachments */
2538 if (mimeinfo != NULL) {
2539 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2540 procmime_mimeinfo_free_all(mimeinfo);
2542 procmsg_msginfo_set_flags(msginfo, 0, MSG_SCANNED);
2545 procmsg_msginfo_free(msginfo);
2552 gint folder_item_fetch_all_msg(FolderItem *item)
2561 g_return_val_if_fail(item != NULL, -1);
2562 if (item->no_select)
2565 debug_print("fetching all messages in %s ...\n", item->path ? item->path : "(null)");
2566 statusbar_print_all(_("Fetching all messages in %s ...\n"), item->path ? item->path : "(null)");
2568 folder = item->folder;
2570 if (folder->ui_func)
2571 folder->ui_func(folder, item, folder->ui_func_data ?
2572 folder->ui_func_data : GINT_TO_POINTER(num));
2574 mlist = folder_item_get_msg_list(item);
2576 total = g_slist_length(mlist);
2578 for (cur = mlist; cur != NULL; cur = cur->next) {
2579 MsgInfo *msginfo = (MsgInfo *)cur->data;
2582 statusbar_progress_all(num++,total, 10);
2584 if (folder->ui_func)
2585 folder->ui_func(folder, item,
2586 folder->ui_func_data ?
2587 folder->ui_func_data :
2588 GINT_TO_POINTER(num));
2590 msg = folder_item_fetch_msg(item, msginfo->msgnum);
2592 g_warning("Can't fetch message %d. Aborting.\n",
2600 statusbar_progress_all(0,0,0);
2601 statusbar_pop_all();
2602 procmsg_msg_list_free(mlist);
2607 static gint folder_item_get_msg_num_by_file(FolderItem *dest, const gchar *file)
2609 static HeaderEntry hentry[] = {{"Message-ID:", NULL, TRUE},
2610 {NULL, NULL, FALSE}};
2614 gchar buf[BUFFSIZE];
2616 if ((fp = g_fopen(file, "rb")) == NULL)
2619 if ((folder_has_parent_of_type(dest, F_QUEUE)) ||
2620 (folder_has_parent_of_type(dest, F_DRAFT)))
2621 while (fgets(buf, sizeof(buf), fp) != NULL)
2622 if (buf[0] == '\r' || buf[0] == '\n') break;
2624 procheader_get_header_fields(fp, hentry);
2625 if (hentry[0].body) {
2626 extract_parenthesis(hentry[0].body, '<', '>');
2627 remove_space(hentry[0].body);
2628 if ((msginfo = msgcache_get_msg_by_id(dest->cache, hentry[0].body)) != NULL) {
2629 msgnum = msginfo->msgnum;
2630 procmsg_msginfo_free(msginfo);
2632 debug_print("found message as uid %d\n", msgnum);
2636 g_free(hentry[0].body);
2637 hentry[0].body = NULL;
2643 static void copy_msginfo_flags(MsgInfo *source, MsgInfo *dest)
2645 MsgPermFlags perm_flags = 0;
2646 MsgTmpFlags tmp_flags = 0;
2648 /* create new flags */
2649 if (source != NULL) {
2650 /* copy original flags */
2651 perm_flags = source->flags.perm_flags;
2652 tmp_flags = source->flags.tmp_flags;
2654 perm_flags = dest->flags.perm_flags;
2655 tmp_flags = dest->flags.tmp_flags;
2658 /* remove new, unread and deleted in special folders */
2659 if (folder_has_parent_of_type(dest->folder, F_OUTBOX) ||
2660 folder_has_parent_of_type(dest->folder, F_QUEUE) ||
2661 folder_has_parent_of_type(dest->folder, F_DRAFT) ||
2662 folder_has_parent_of_type(dest->folder, F_TRASH))
2663 perm_flags &= ~(MSG_NEW | MSG_UNREAD | MSG_DELETED);
2665 /* set ignore flag of ignored parent exists */
2666 if (procmsg_msg_has_flagged_parent(dest, MSG_IGNORE_THREAD))
2667 perm_flags |= MSG_IGNORE_THREAD;
2669 /* Unset tmp flags that should not be copied */
2670 tmp_flags &= ~(MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
2672 /* unset flags that are set but should not */
2673 /* and set new flags */
2674 procmsg_msginfo_change_flags(dest,
2675 ~dest->flags.perm_flags & perm_flags,
2676 ~dest->flags.tmp_flags & tmp_flags,
2677 dest->flags.perm_flags & ~perm_flags,
2678 dest->flags.tmp_flags & ~tmp_flags);
2681 static void add_msginfo_to_cache(FolderItem *item, MsgInfo *newmsginfo, MsgInfo *flagsource)
2683 /* update folder stats */
2684 if (MSG_IS_NEW(newmsginfo->flags))
2686 if (MSG_IS_UNREAD(newmsginfo->flags))
2687 item->unread_msgs++;
2688 if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
2689 item->unreadmarked_msgs++;
2690 if (MSG_IS_MARKED(newmsginfo->flags))
2691 item->marked_msgs++;
2694 folder_item_update_freeze();
2697 folder_item_read_cache(item);
2699 msgcache_add_msg(item->cache, newmsginfo);
2700 copy_msginfo_flags(flagsource, newmsginfo);
2701 folder_item_update_with_msg(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT | F_ITEM_UPDATE_ADDMSG, newmsginfo);
2702 folder_item_update_thaw();
2705 static void remove_msginfo_from_cache(FolderItem *item, MsgInfo *msginfo)
2707 MsgInfoUpdate msginfo_update;
2710 folder_item_read_cache(item);
2712 if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2713 msginfo->folder->new_msgs--;
2714 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2715 msginfo->folder->unread_msgs--;
2716 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
2717 msginfo->folder->unreadmarked_msgs--;
2718 if (MSG_IS_MARKED(msginfo->flags))
2719 item->marked_msgs--;
2721 msginfo->folder->total_msgs--;
2723 msginfo_update.msginfo = msginfo;
2724 msginfo_update.flags = MSGINFO_UPDATE_DELETED;
2725 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2727 msgcache_remove_msg(item->cache, msginfo->msgnum);
2728 folder_item_update_with_msg(msginfo->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT | F_ITEM_UPDATE_REMOVEMSG, msginfo);
2731 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
2732 MsgFlags *flags, gboolean remove_source)
2735 MsgFileInfo fileinfo;
2737 g_return_val_if_fail(dest != NULL, -1);
2738 g_return_val_if_fail(file != NULL, -1);
2740 fileinfo.msginfo = NULL;
2741 fileinfo.file = (gchar *)file;
2742 fileinfo.flags = flags;
2743 file_list.data = &fileinfo;
2744 file_list.next = NULL;
2746 return folder_item_add_msgs(dest, &file_list, remove_source);
2749 gint folder_item_add_msgs(FolderItem *dest, GSList *file_list,
2750 gboolean remove_source)
2753 gint ret, num, lastnum = -1;
2755 GRelation *relation;
2756 MsgFileInfo *fileinfo = NULL;
2757 gboolean folderscan = FALSE;
2759 g_return_val_if_fail(dest != NULL, -1);
2760 g_return_val_if_fail(file_list != NULL, -1);
2761 g_return_val_if_fail(dest->folder != NULL, -1);
2762 if (dest->no_select)
2765 folder = dest->folder;
2767 relation = g_relation_new(2);
2768 g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
2770 if (folder->klass->add_msgs != NULL) {
2771 ret = folder->klass->add_msgs(folder, dest, file_list, relation);
2773 g_relation_destroy(relation);
2777 for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
2778 fileinfo = (MsgFileInfo *) file_cur->data;
2780 ret = folder->klass->add_msg(folder, dest, fileinfo->file, fileinfo->flags);
2782 g_relation_destroy(relation);
2785 g_relation_insert(relation, fileinfo, GINT_TO_POINTER(ret));
2789 for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
2792 fileinfo = (MsgFileInfo *) file_cur->data;
2793 tuples = g_relation_select(relation, fileinfo, 0);
2794 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
2795 g_tuples_destroy(tuples);
2798 MsgInfo *newmsginfo;
2802 folder_item_scan_full(dest, FALSE);
2805 num = folder_item_get_msg_num_by_file(dest, fileinfo->file);
2811 if (num >= 0 && remove_source) {
2812 if (g_unlink(fileinfo->file) < 0)
2813 FILE_OP_ERROR(fileinfo->file, "unlink");
2820 ((newmsginfo = get_msginfo(dest, num)) != NULL)) {
2821 add_msginfo_to_cache(dest, newmsginfo, NULL);
2822 procmsg_msginfo_free(newmsginfo);
2823 } else if ((newmsginfo = msgcache_get_msg(dest->cache, num)) != NULL) {
2824 /* TODO: set default flags */
2825 procmsg_msginfo_free(newmsginfo);
2830 g_relation_destroy(relation);
2835 static FolderItem *folder_item_move_recursive(FolderItem *src, FolderItem *dest, gboolean copy)
2838 FolderItem *new_item;
2839 FolderItem *next_item;
2841 gchar *old_id, *new_id;
2844 debug_print("%s %s to %s\n", copy?"Copying":"Moving", src->path, dest->path);
2845 new_item = folder_create_folder(dest, src->name);
2846 if (new_item == NULL) {
2847 printf("Can't create folder\n");
2851 if (new_item->folder == NULL)
2852 new_item->folder = dest->folder;
2855 log_message(LOG_PROTOCOL, copy ?_("Copying %s to %s...\n"):_("Moving %s to %s...\n"),
2856 src->name, new_item->path);
2858 mlist = folder_item_get_msg_list(src);
2860 if (mlist != NULL) {
2862 folder_item_copy_msgs(new_item, mlist);
2864 folder_item_move_msgs(new_item, mlist);
2865 procmsg_msg_list_free(mlist);
2869 folder_item_prefs_copy_prefs(src, new_item);
2871 /* copy internal data */
2872 if (src->folder->klass == new_item->folder->klass &&
2873 src->folder->klass->copy_private_data != NULL)
2874 src->folder->klass->copy_private_data(src->folder,
2876 new_item->collapsed = src->collapsed;
2877 new_item->thread_collapsed = src->thread_collapsed;
2878 new_item->threaded = src->threaded;
2879 new_item->ret_rcpt = src->ret_rcpt;
2880 new_item->hide_read_msgs = src->hide_read_msgs;
2881 new_item->sort_key = src->sort_key;
2882 new_item->sort_type = src->sort_type;
2884 prefs_matcher_write_config();
2887 srcnode = src->folder->node;
2888 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, src);
2889 srcnode = srcnode->children;
2890 while (srcnode != NULL) {
2891 if (srcnode && srcnode->data) {
2892 next_item = (FolderItem*) srcnode->data;
2893 srcnode = srcnode->next;
2894 if (folder_item_move_recursive(next_item, new_item, copy) == NULL) {
2899 old_id = folder_item_get_identifier(src);
2900 new_id = folder_item_get_identifier(new_item);
2902 /* if src supports removing, otherwise only copy folder */
2903 if (src->folder->klass->remove_folder != NULL && !copy)
2904 src->folder->klass->remove_folder(src->folder, src);
2905 folder_write_list();
2908 debug_print("updating rules : %s => %s\n", old_id, new_id);
2909 if (old_id != NULL && new_id != NULL) {
2910 prefs_filtering_rename_path(old_id, new_id);
2911 account_rename_path(old_id, new_id);
2920 gint folder_item_move_to(FolderItem *src, FolderItem *dest, FolderItem **new_item, gboolean copy)
2922 FolderItem *tmp = folder_item_parent(dest);
2923 gchar * src_identifier, * dst_identifier;
2924 gchar * phys_srcpath, * phys_dstpath, *tmppath;
2928 return F_MOVE_FAILED_DEST_IS_CHILD;
2930 tmp = folder_item_parent(tmp);
2933 tmp = folder_item_parent(src);
2935 src_identifier = folder_item_get_identifier(src);
2936 dst_identifier = folder_item_get_identifier(dest);
2938 if(dst_identifier == NULL && dest->folder && folder_item_parent(dest) == NULL) {
2939 /* dest can be a root folder */
2940 dst_identifier = folder_get_identifier(dest->folder);
2942 if (src_identifier == NULL || dst_identifier == NULL) {
2943 debug_print("Can't get identifiers\n");
2944 return F_MOVE_FAILED;
2947 if (src->folder != dest->folder && !copy) {
2948 return F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX;
2951 phys_srcpath = folder_item_get_path(src);
2952 tmppath = folder_item_get_path(dest);
2953 phys_dstpath = g_strconcat(tmppath,
2955 g_path_get_basename(phys_srcpath),
2959 if (folder_item_parent(src) == dest || src == dest) {
2960 g_free(src_identifier);
2961 g_free(dst_identifier);
2962 g_free(phys_srcpath);
2963 g_free(phys_dstpath);
2964 return F_MOVE_FAILED_DEST_IS_PARENT;
2966 debug_print("moving \"%s\" to \"%s\"\n", phys_srcpath, phys_dstpath);
2967 if ((tmp = folder_item_move_recursive(src, dest, copy)) == NULL) {
2968 return F_MOVE_FAILED;
2971 g_free(src_identifier);
2972 g_free(dst_identifier);
2973 g_free(phys_srcpath);
2974 g_free(phys_dstpath);
2982 * Copy a list of message to a new folder and remove
2983 * source messages if wanted
2985 static gint do_copy_msgs(FolderItem *dest, GSList *msglist, gboolean remove_source)
2989 gint num, lastnum = -1;
2990 gboolean folderscan = FALSE;
2991 GRelation *relation;
2992 GSList *not_moved = NULL;
2993 gint total = 0, curmsg = 0;
2994 MsgInfo *msginfo = NULL;
2996 g_return_val_if_fail(dest != NULL, -1);
2997 g_return_val_if_fail(msglist != NULL, -1);
2999 folder = dest->folder;
3001 g_return_val_if_fail(folder->klass->copy_msg != NULL, -1);
3002 if (dest->no_select)
3005 msginfo = (MsgInfo *)msglist->data;
3010 if (!MSG_IS_QUEUED(msginfo->flags) &&
3011 MSG_IS_DRAFT(msginfo->flags) &&
3012 folder_has_parent_of_type(dest, F_QUEUE)) {
3013 GSList *cur = msglist;
3014 gboolean queue_err = FALSE;
3015 for (; cur; cur = cur->next) {
3016 Compose *compose = NULL;
3017 FolderItem *queue = dest;
3020 msginfo = (MsgInfo *)cur->data;
3021 compose = compose_reedit(msginfo, TRUE);
3022 if (compose == NULL) {
3026 val = compose_queue(compose, NULL, &queue, NULL,
3030 } else if (remove_source) {
3031 folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
3034 compose_close(compose);
3036 return queue_err ? -1:0;
3039 relation = g_relation_new(2);
3040 g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
3041 g_relation_index(relation, 1, g_direct_hash, g_direct_equal);
3043 for (l = msglist ; l != NULL ; l = g_slist_next(l)) {
3044 MsgInfo * msginfo = (MsgInfo *) l->data;
3046 if (msginfo->planned_download != 0) {
3047 int old_planned = msginfo->planned_download;
3048 partial_unmark(msginfo);
3049 /* little hack to reenable after */
3050 msginfo->planned_download = old_planned;
3055 * Copy messages to destination folder and
3056 * store new message numbers in newmsgnums
3058 if (folder->klass->copy_msgs != NULL) {
3059 if (folder->klass->copy_msgs(folder, dest, msglist, relation) < 0) {
3060 g_relation_destroy(relation);
3067 /* immediately stop if src and dest folders are identical */
3069 msginfo = (MsgInfo *) l->data;
3070 if (msginfo != NULL && msginfo->folder == dest) {
3071 g_relation_destroy(relation);
3076 for (; l != NULL ; l = g_slist_next(l)) {
3077 msginfo = (MsgInfo *) l->data;
3079 num = folder->klass->copy_msg(folder, dest, msginfo);
3081 g_relation_insert(relation, msginfo, GINT_TO_POINTER(num));
3083 not_moved = g_slist_prepend(not_moved, msginfo);
3087 if (remove_source) {
3088 MsgInfo *msginfo = (MsgInfo *) msglist->data;
3089 FolderItem *item = msginfo->folder;
3091 * Remove source messages from their folders if
3092 * copying was successfull and update folder
3095 if (not_moved == NULL && item->folder->klass->remove_msgs) {
3096 item->folder->klass->remove_msgs(item->folder,
3101 for (l = msglist; l != NULL; l = g_slist_next(l)) {
3103 msginfo = (MsgInfo *) l->data;
3104 item = msginfo->folder;
3106 tuples = g_relation_select(relation, msginfo, 0);
3109 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
3112 g_tuples_destroy(tuples);
3117 if (g_slist_find(not_moved, msginfo))
3120 if ((num >= 0) && (item->folder->klass->remove_msg != NULL)) {
3121 if (!item->folder->klass->remove_msgs)
3122 item->folder->klass->remove_msg(item->folder,
3125 remove_msginfo_from_cache(item, msginfo);
3130 /* Read cache for dest folder */
3131 if (!dest->cache) folder_item_read_cache(dest);
3134 * Fetch new MsgInfos for new messages in dest folder,
3135 * add them to the msgcache and update folder message counts
3137 if (g_relation_count(relation, GINT_TO_POINTER(0), 1) > 0) {
3138 folder_item_scan_full(dest, FALSE);
3142 statusbar_print_all(_("Updating cache for %s..."), dest->path ? dest->path : "(null)");
3143 total = g_slist_length(msglist);
3144 for (l = msglist; l != NULL; l = g_slist_next(l)) {
3145 MsgInfo *msginfo = (MsgInfo *) l->data;
3148 tuples = g_relation_select(relation, msginfo, 0);
3149 if (tuples->len > 0) {
3150 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
3151 g_tuples_destroy(tuples);
3155 g_tuples_destroy(tuples);
3157 statusbar_progress_all(curmsg++,total, 100);
3158 if (curmsg % 100 == 0)
3162 MsgInfo *newmsginfo = NULL;
3165 if (msginfo->msgid != NULL) {
3166 newmsginfo = folder_item_get_msginfo_by_msgid(dest, msginfo->msgid);
3167 if (newmsginfo != NULL) {
3168 copy_msginfo_flags(msginfo, newmsginfo);
3169 num = newmsginfo->msgnum;
3173 newmsginfo = get_msginfo(dest, num);
3174 if (newmsginfo != NULL) {
3175 add_msginfo_to_cache(dest, newmsginfo, msginfo);
3179 if (msginfo->planned_download
3180 == POP3_PARTIAL_DLOAD_DELE) {
3181 partial_mark_for_delete(newmsginfo);
3183 if (msginfo->planned_download
3184 == POP3_PARTIAL_DLOAD_DLOAD) {
3185 partial_mark_for_download(newmsginfo);
3187 if (!MSG_IS_POSTFILTERED (msginfo->flags)) {
3188 procmsg_msginfo_set_flags ( msginfo, MSG_POSTFILTERED, 0);
3189 procmsg_msginfo_set_flags (newmsginfo, MSG_POSTFILTERED, 0);
3190 hooks_invoke (MAIL_POSTFILTERING_HOOKLIST, newmsginfo);
3192 procmsg_msginfo_free(newmsginfo);
3199 statusbar_progress_all(0,0,0);
3200 statusbar_pop_all();
3202 g_relation_destroy(relation);
3203 if (not_moved != NULL) {
3204 g_slist_free(not_moved);
3211 * Move a message to a new folder.
3213 * \param dest Destination folder
3214 * \param msginfo The message
3216 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
3220 g_return_val_if_fail(dest != NULL, -1);
3221 g_return_val_if_fail(msginfo != NULL, -1);
3223 list.data = msginfo;
3226 return do_copy_msgs(dest, &list, TRUE);
3230 * Move a list of messages to a new folder.
3232 * \param dest Destination folder
3233 * \param msglist List of messages
3235 gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
3238 g_return_val_if_fail(dest != NULL, -1);
3239 g_return_val_if_fail(msglist != NULL, -1);
3241 result = do_copy_msgs(dest, msglist, TRUE);
3247 * Copy a message to a new folder.
3249 * \param dest Destination folder
3250 * \param msginfo The message
3252 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
3256 g_return_val_if_fail(dest != NULL, -1);
3257 g_return_val_if_fail(msginfo != NULL, -1);
3259 list.data = msginfo;
3262 return do_copy_msgs(dest, &list, FALSE);
3266 * Copy a list of messages to a new folder.
3268 * \param dest Destination folder
3269 * \param msglist List of messages
3271 gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
3274 g_return_val_if_fail(dest != NULL, -1);
3275 g_return_val_if_fail(msglist != NULL, -1);
3278 result = do_copy_msgs(dest, msglist, FALSE);
3284 gint folder_item_remove_msg(FolderItem *item, gint num)
3290 g_return_val_if_fail(item != NULL, -1);
3291 folder = item->folder;
3292 g_return_val_if_fail(folder->klass->remove_msg != NULL, -1);
3293 if (item->no_select)
3296 if (!item->cache) folder_item_read_cache(item);
3298 ret = folder->klass->remove_msg(folder, item, num);
3300 msginfo = msgcache_get_msg(item->cache, num);
3301 if (msginfo != NULL) {
3302 remove_msginfo_from_cache(item, msginfo);
3303 procmsg_msginfo_free(msginfo);
3309 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
3314 g_return_val_if_fail(item != NULL, -1);
3315 folder = item->folder;
3316 g_return_val_if_fail(folder != NULL, -1);
3317 if (item->no_select)
3320 if (!item->cache) folder_item_read_cache(item);
3322 folder_item_update_freeze();
3323 if (item->folder->klass->remove_msgs) {
3324 ret = item->folder->klass->remove_msgs(item->folder,
3329 while (ret == 0 && msglist != NULL) {
3330 MsgInfo *msginfo = (MsgInfo *)msglist->data;
3331 if (MSG_IS_LOCKED(msginfo->flags)) {
3332 msglist = msglist->next;
3335 if (!item->folder->klass->remove_msgs)
3336 ret = folder_item_remove_msg(item, msginfo->msgnum);
3337 if (ret != 0) break;
3338 msgcache_remove_msg(item->cache, msginfo->msgnum);
3339 msglist = msglist->next;
3341 folder_item_scan_full(item, FALSE);
3342 folder_item_update_thaw();
3347 gint folder_item_remove_all_msg(FolderItem *item)
3352 g_return_val_if_fail(item != NULL, -1);
3353 if (item->no_select)
3356 folder = item->folder;
3359 if (folder->klass->remove_all_msg != NULL) {
3360 result = folder->klass->remove_all_msg(folder, item);
3363 folder_item_free_cache(item, TRUE);
3364 item->cache = msgcache_new();
3367 MsgInfoList *msglist;
3369 msglist = folder_item_get_msg_list(item);
3370 result = folder_item_remove_msgs(item, msglist);
3371 procmsg_msg_list_free(msglist);
3376 item->unread_msgs = 0;
3377 item->unreadmarked_msgs = 0;
3378 item->marked_msgs = 0;
3379 item->total_msgs = 0;
3380 folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
3387 void folder_item_change_msg_flags(FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3389 g_return_if_fail(item != NULL);
3390 g_return_if_fail(msginfo != NULL);
3391 if (item->no_select)
3394 if (item->folder->klass->change_flags != NULL) {
3395 item->folder->klass->change_flags(item->folder, item, msginfo, newflags);
3397 msginfo->flags.perm_flags = newflags;
3401 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
3405 g_return_val_if_fail(item != NULL, FALSE);
3406 if (item->no_select)
3409 folder = item->folder;
3411 g_return_val_if_fail(folder->klass->is_msg_changed != NULL, -1);
3413 return folder->klass->is_msg_changed(folder, item, msginfo);
3416 static gchar *folder_item_get_cache_file(FolderItem *item)
3421 g_return_val_if_fail(item != NULL, NULL);
3422 g_return_val_if_fail(item->path != NULL, NULL);
3424 path = folder_item_get_path(item);
3425 g_return_val_if_fail(path != NULL, NULL);
3426 if (!is_dir_exist(path))
3427 make_dir_hier(path);
3428 file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
3434 static gchar *folder_item_get_mark_file(FolderItem *item)
3439 g_return_val_if_fail(item != NULL, NULL);
3440 g_return_val_if_fail(item->path != NULL, NULL);
3442 path = folder_item_get_path(item);
3443 g_return_val_if_fail(path != NULL, NULL);
3444 if (!is_dir_exist(path))
3445 make_dir_hier(path);
3446 file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
3452 static gpointer xml_to_folder_item(gpointer nodedata, gpointer data)
3454 XMLNode *xmlnode = (XMLNode *) nodedata;
3455 Folder *folder = (Folder *) data;
3458 g_return_val_if_fail(xmlnode != NULL, NULL);
3459 g_return_val_if_fail(folder != NULL, NULL);
3461 if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
3462 g_warning("tag name != \"folderitem\"\n");
3466 item = folder_item_new(folder, "", "");
3467 if (folder->klass->item_set_xml != NULL)
3468 folder->klass->item_set_xml(folder, item, xmlnode->tag);
3470 folder_item_set_xml(folder, item, xmlnode->tag);
3472 item->folder = folder;
3474 switch (item->stype) {
3475 case F_INBOX: folder->inbox = item; break;
3476 case F_OUTBOX: folder->outbox = item; break;
3477 case F_DRAFT: folder->draft = item; break;
3478 case F_QUEUE: folder->queue = item; break;
3479 case F_TRASH: folder->trash = item; break;
3482 folder_item_prefs_read_config(item);
3487 static gboolean folder_item_set_node(GNode *node, gpointer data)
3489 FolderItem *item = (FolderItem *) node->data;
3495 static Folder *folder_get_from_xml(GNode *node)
3500 FolderClass *klass = NULL;
3503 g_return_val_if_fail(node->data != NULL, NULL);
3505 xmlnode = node->data;
3506 if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
3507 g_warning("tag name != \"folder\"\n");
3510 list = xmlnode->tag->attr;
3511 for (; list != NULL; list = list->next) {
3512 XMLAttr *attr = list->data;
3514 if (!attr || !attr->name || !attr->value) continue;
3515 if (!strcmp(attr->name, "type"))
3516 klass = folder_get_class_from_string(attr->value);
3521 folder = folder_new(klass, "", "");
3522 g_return_val_if_fail(folder != NULL, NULL);
3525 klass->set_xml(folder, xmlnode->tag);
3527 folder_set_xml(folder, xmlnode->tag);
3529 cur = node->children;
3530 while (cur != NULL) {
3533 itemnode = g_node_map(cur, xml_to_folder_item, (gpointer) folder);
3534 g_node_append(folder->node, itemnode);
3537 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, folder_item_set_node, NULL);
3542 static gchar *folder_get_list_path(void)
3544 static gchar *filename = NULL;
3547 filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
3553 #define PUT_ESCAPE_STR(fp, attr, str) \
3555 fputs(" " attr "=\"", fp); \
3556 xml_file_put_escape_str(fp, str); \
3560 static gpointer folder_item_to_xml(gpointer nodedata, gpointer data)
3562 FolderItem *item = (FolderItem *) nodedata;
3565 g_return_val_if_fail(item != NULL, NULL);
3567 if (item->folder->klass->item_get_xml != NULL)
3568 tag = item->folder->klass->item_get_xml(item->folder, item);
3570 tag = folder_item_get_xml(item->folder, item);
3572 return xml_node_new(tag, NULL);
3575 static GNode *folder_get_xml_node(Folder *folder)
3581 g_return_val_if_fail(folder != NULL, NULL);
3583 if (folder->klass->get_xml != NULL)
3584 tag = folder->klass->get_xml(folder);
3586 tag = folder_get_xml(folder);
3588 xml_tag_add_attr(tag, xml_attr_new("type", folder->klass->idstr));
3590 xmlnode = xml_node_new(tag, NULL);
3592 node = g_node_new(xmlnode);
3594 g_return_val_if_fail (folder->node != NULL, NULL);
3596 if (folder->node->children) {
3599 cur = folder->node->children;
3603 xmlnode = g_node_map(cur, folder_item_to_xml, (gpointer) folder);
3604 g_node_append(node, xmlnode);
3612 static void folder_update_op_count_rec(GNode *node)
3614 FolderItem *fitem = FOLDER_ITEM(node->data);
3616 if (g_node_depth(node) > 0) {
3617 if (fitem->op_count > 0) {
3618 fitem->op_count = 0;
3619 folder_item_update(fitem, F_ITEM_UPDATE_MSGCNT);
3621 if (node->children) {
3624 child = node->children;
3630 folder_update_op_count_rec(cur);
3636 void folder_update_op_count(void)
3641 for (cur = folder_list; cur != NULL; cur = cur->next) {
3643 folder_update_op_count_rec(folder->node);
3647 typedef struct _type_str {
3654 static gchar * folder_item_get_tree_identifier(FolderItem * item)
3656 if (item->parent != NULL) {
3660 path = folder_item_get_tree_identifier(item->parent);
3664 id = g_strconcat(path, "/", item->name, NULL);
3670 return g_strconcat("/", item->name, NULL);
3675 /* CLAWS: temporary local folder for filtering */
3676 #define TEMP_FOLDER "TEMP_FOLDER"
3677 #define PROCESSING_FOLDER_ITEM "processing"
3679 static FolderItem *processing_folder_item;
3681 static void folder_create_processing_folder(void)
3683 Folder *processing_folder;
3686 if ((processing_folder = folder_find_from_name(TEMP_FOLDER, mh_get_class())) == NULL) {
3690 g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
3691 "tempfolder", NULL);
3693 folder_new(mh_get_class(), TEMP_FOLDER, tmppath);
3696 g_assert(processing_folder != NULL);
3698 debug_print("tmpparentroot %s\n", LOCAL_FOLDER(processing_folder)->rootpath);
3699 /* FIXME: [W32] The code below does not correctly merge
3700 relative filenames; there should be a function to handle
3702 if (!is_relative_filename(LOCAL_FOLDER(processing_folder)->rootpath))
3703 tmpname = g_strconcat(LOCAL_FOLDER(processing_folder)->rootpath,
3704 G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
3707 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
3708 LOCAL_FOLDER(processing_folder)->rootpath,
3709 G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
3712 if (!is_dir_exist(tmpname)) {
3713 debug_print("*TMP* creating %s\n", tmpname);
3714 processing_folder_item = processing_folder->klass->create_folder(processing_folder,
3715 processing_folder->node->data,
3716 PROCESSING_FOLDER_ITEM);
3718 debug_print("*TMP* already created\n");
3719 processing_folder_item = folder_item_new(processing_folder, PROCESSING_FOLDER_ITEM, PROCESSING_FOLDER_ITEM);
3720 g_assert(processing_folder_item);
3721 folder_item_append(processing_folder->node->data, processing_folder_item);
3723 g_assert(processing_folder_item != NULL);
3727 FolderItem *folder_get_default_processing(void)
3729 if (!processing_folder_item) {
3730 folder_create_processing_folder();
3732 return processing_folder_item;
3735 /* folder_persist_prefs_new() - return hash table with persistent
3736 * settings (and folder name as key).
3737 * (note that in claws other options are in the folder_item_prefs_RC
3738 * file, so those don't need to be included in PersistPref yet)
3740 static GHashTable *folder_persist_prefs_new(Folder *folder)
3742 GHashTable *pptable;
3744 g_return_val_if_fail(folder, NULL);
3745 pptable = g_hash_table_new(g_str_hash, g_str_equal);
3746 folder_get_persist_prefs_recursive(folder->node, pptable);
3750 static void folder_persist_prefs_free(GHashTable *pptable)
3752 g_return_if_fail(pptable);
3753 g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
3754 g_hash_table_destroy(pptable);
3757 static const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
3759 if (pptable == NULL || name == NULL) return NULL;
3760 return g_hash_table_lookup(pptable, name);
3763 static void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
3765 const PersistPrefs *pp;
3766 gchar *id = folder_item_get_identifier(item);
3768 pp = folder_get_persist_prefs(pptable, id);
3773 /* CLAWS: since not all folder properties have been migrated to
3774 * folderlist.xml, we need to call the old stuff first before
3775 * setting things that apply both to Main and Claws. */
3776 folder_item_prefs_read_config(item);
3778 item->collapsed = pp->collapsed;
3779 item->thread_collapsed = pp->thread_collapsed;
3780 item->threaded = pp->threaded;
3781 item->ret_rcpt = pp->ret_rcpt;
3782 item->hide_read_msgs = pp->hide_read_msgs;
3783 item->sort_key = pp->sort_key;
3784 item->sort_type = pp->sort_type;
3787 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
3789 FolderItem *item = FOLDER_ITEM(node->data);
3794 g_return_if_fail(node != NULL);
3795 g_return_if_fail(item != NULL);
3797 /* NOTE: item->path == NULL means top level folder; not interesting
3798 * to store preferences of that one. */
3800 id = folder_item_get_identifier(item);
3801 pp = g_new0(PersistPrefs, 1);
3802 g_return_if_fail(pp != NULL);
3803 pp->collapsed = item->collapsed;
3804 pp->thread_collapsed = item->thread_collapsed;
3805 pp->threaded = item->threaded;
3806 pp->ret_rcpt = item->ret_rcpt;
3807 pp->hide_read_msgs = item->hide_read_msgs;
3808 pp->sort_key = item->sort_key;
3809 pp->sort_type = item->sort_type;
3810 g_hash_table_insert(pptable, id, pp);
3813 if (node->children) {
3814 child = node->children;
3818 folder_get_persist_prefs_recursive(cur, pptable);
3823 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
3830 void folder_item_apply_processing(FolderItem *item)
3832 GSList *processing_list;
3833 GSList *mlist, *cur;
3834 guint total = 0, curmsg = 0;
3835 gint last_apply_per_account;
3837 g_return_if_fail(item != NULL);
3839 if (item->no_select)
3842 processing_list = item->prefs->processing;
3844 if (!pre_global_processing && !processing_list
3845 && !post_global_processing)
3848 debug_print("processing %s\n", item->name);
3849 folder_item_update_freeze();
3851 mlist = folder_item_get_msg_list(item);
3852 total = g_slist_length(mlist);
3853 statusbar_print_all(_("Processing messages..."));
3855 last_apply_per_account = prefs_common.apply_per_account_filtering_rules;
3856 prefs_common.apply_per_account_filtering_rules = FILTERING_ACCOUNT_RULES_SKIP;
3858 for (cur = mlist ; cur != NULL ; cur = cur->next) {
3861 msginfo = (MsgInfo *) cur->data;
3863 /* reset parameters that can be modified by processing */
3864 msginfo->hidden = 0;
3867 statusbar_progress_all(curmsg++,total, 10);
3869 /* apply pre global rules */
3870 filter_message_by_msginfo(pre_global_processing, msginfo, NULL,
3871 FILTERING_PRE_PROCESSING, NULL);
3873 /* apply rules of the folder */
3874 filter_message_by_msginfo(processing_list, msginfo, NULL,
3875 FILTERING_FOLDER_PROCESSING, item->name);
3877 /* apply post global rules */
3878 filter_message_by_msginfo(post_global_processing, msginfo, NULL,
3879 FILTERING_POST_PROCESSING, NULL);
3881 prefs_common.apply_per_account_filtering_rules = last_apply_per_account;
3883 if (pre_global_processing || processing_list
3884 || post_global_processing)
3885 filtering_move_and_copy_msgs(mlist);
3886 for (cur = mlist ; cur != NULL ; cur = cur->next) {
3887 MsgInfo * msginfo = (MsgInfo *)cur->data;
3888 procmsg_msginfo_free(msginfo);
3890 g_slist_free(mlist);
3892 statusbar_progress_all(0,0,0);
3893 statusbar_pop_all();
3895 folder_item_update_thaw();
3899 * functions for handling FolderItem content changes
3901 static gint folder_item_update_freeze_cnt = 0;
3903 static void folder_item_update_with_msg(FolderItem *item, FolderItemUpdateFlags update_flags, MsgInfo *msg)
3905 if (folder_item_update_freeze_cnt == 0 /* || (msg != NULL && item->opened) */) {
3906 FolderItemUpdateData source;
3909 source.update_flags = update_flags;
3911 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
3913 item->update_flags |= update_flags & ~(F_ITEM_UPDATE_ADDMSG | F_ITEM_UPDATE_REMOVEMSG);
3918 * Notify the folder system about changes to a folder. If the
3919 * update system is not frozen the FOLDER_ITEM_UPDATE_HOOKLIST will
3920 * be invoked, otherwise the changes will be remebered until
3921 * the folder system is thawed.
3923 * \param item The FolderItem that was changed
3924 * \param update_flags Type of changed that was made
3926 void folder_item_update(FolderItem *item, FolderItemUpdateFlags update_flags)
3928 folder_item_update_with_msg(item, update_flags, NULL);
3931 void folder_item_update_recursive(FolderItem *item, FolderItemUpdateFlags update_flags)
3933 GNode *node = item->folder->node;
3935 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
3936 node = node->children;
3938 folder_item_update(item, update_flags);
3939 while (node != NULL) {
3940 if (node && node->data) {
3941 FolderItem *next_item = (FolderItem*) node->data;
3943 folder_item_update(next_item, update_flags);
3949 void folder_item_update_freeze(void)
3951 folder_item_update_freeze_cnt++;
3954 static void folder_item_update_func(FolderItem *item, gpointer data)
3956 FolderItemUpdateData source;
3958 if (item->update_flags) {
3960 source.update_flags = item->update_flags;
3962 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
3963 item->update_flags = 0;
3967 void folder_item_update_thaw(void)
3969 if (folder_item_update_freeze_cnt > 0)
3970 folder_item_update_freeze_cnt--;
3971 if (folder_item_update_freeze_cnt == 0) {
3972 /* Update all folders */
3973 folder_func_to_all_folders(folder_item_update_func, NULL);
3977 static void folder_item_synchronise_func(FolderItem *item, gpointer data)
3979 Folder *folder = (Folder *)data;
3980 if (folder == NULL || item->folder == folder) {
3981 if(item->prefs->offlinesync && item->folder->klass->synchronise)
3982 item->folder->klass->synchronise(item);
3986 void folder_synchronise(Folder *folder)
3988 folder_func_to_all_folders(folder_item_synchronise_func, folder);
3991 typedef struct _WantSyncData {
3996 static void folder_item_want_synchronise_func(FolderItem *item, gpointer data)
3998 WantSyncData *want_sync_data = (WantSyncData *)data;
4000 if (want_sync_data->folder == NULL || item->folder == want_sync_data->folder) {
4001 if (item->prefs->offlinesync && item->folder->klass->synchronise)
4002 want_sync_data->want_sync |= TRUE;
4006 gboolean folder_want_synchronise(Folder *folder)
4008 WantSyncData *want_sync_data = g_new0(WantSyncData, 1);
4010 want_sync_data->folder = folder;
4011 want_sync_data->want_sync = FALSE;
4013 folder_func_to_all_folders(folder_item_want_synchronise_func, want_sync_data);
4014 result = want_sync_data->want_sync;
4015 g_free(want_sync_data);
4017 debug_print("Folder %s wants sync\n", folder->name);
4021 void folder_item_set_batch (FolderItem *item, gboolean batch)
4023 if (item->folder->klass->set_batch) {
4024 item->folder->klass->set_batch(item->folder, item, batch);
4028 gboolean folder_has_parent_of_type(FolderItem *item,
4029 SpecialFolderItemType type)
4031 FolderItem *cur = item;
4033 /* if we already know it, make it short */
4034 if (item->parent_stype != -1) {
4035 return (item->parent_stype == type);
4038 /* if we don't, find the type from the first possible parent,
4039 * and set our parent type to be faster next time */
4041 if (cur->stype == type || cur->parent_stype == type) {
4042 item->parent_stype = type;
4045 cur = folder_item_parent(cur);
4048 /* if we didn't match what was asked, we didn't return. If our
4049 * parent type is unknown, we may as well find it now to be faster
4051 if (item->parent_stype == -1) {
4054 /* here's an exception: Inbox subfolders are normal. */
4055 if (item->parent_stype == -1 && cur->stype == F_INBOX
4057 item->parent_stype = F_NORMAL;
4060 /* ah, we know this parent's parent's type, we may as
4061 * well copy it instead of going up the full way */
4062 if (cur->parent_stype != -1) {
4063 item->parent_stype = cur->parent_stype;
4066 /* we found a parent that has a special type. That's
4067 * our parent type. */
4068 if (cur->stype != F_NORMAL) {
4069 cur->parent_stype = cur->stype;
4070 item->parent_stype = cur->stype;
4073 /* if we didn't find anything, go up once more */
4074 cur = folder_item_parent(cur);
4076 /* as we still didn't find anything, our parents must all be
4078 if (item->parent_stype == -1) {
4079 item->parent_stype = F_NORMAL;
4085 gboolean folder_subscribe (const gchar *uri)
4088 for (cur = folder_get_list(); cur != NULL; cur = g_list_next(cur)) {
4089 Folder *folder = (Folder *) cur->data;
4091 if (folder->klass->subscribe
4092 && folder->klass->subscribe(folder, uri)) {
4100 gboolean folder_get_sort_type (Folder *folder,
4101 FolderSortKey *sort_key,
4102 FolderSortType *sort_type)
4104 if (!folder || !sort_key || !sort_type)
4106 if (folder->klass->get_sort_type == NULL)
4108 folder->klass->get_sort_type(folder, sort_key, sort_type);
4112 #undef PUT_ESCAPE_STR