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"
62 /* Dependecies to be removed ?! */
63 #include "prefs_common.h"
64 #include "prefs_account.h"
66 /* Define possible missing constants for Windows. */
78 static GList *folder_list = NULL;
79 static GSList *class_list = NULL;
80 static GSList *folder_unloaded_list = NULL;
82 void folder_init (Folder *folder,
85 static gchar *folder_item_get_cache_file (FolderItem *item);
86 static gchar *folder_item_get_mark_file (FolderItem *item);
87 static gchar *folder_get_list_path (void);
88 static GNode *folder_get_xml_node (Folder *folder);
89 static Folder *folder_get_from_xml (GNode *node);
90 static void folder_update_op_count_rec (GNode *node);
93 static void folder_get_persist_prefs_recursive
94 (GNode *node, GHashTable *pptable);
95 static gboolean persist_prefs_free (gpointer key, gpointer val, gpointer data);
96 static void folder_item_read_cache (FolderItem *item);
97 gint folder_item_scan_full (FolderItem *item, gboolean filtering);
98 static void folder_item_update_with_msg (FolderItem *item, FolderItemUpdateFlags update_flags,
100 static GHashTable *folder_persist_prefs_new (Folder *folder);
101 static void folder_persist_prefs_free (GHashTable *pptable);
102 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;
592 } else if (!strcmp(attr->name, "last_seen")) {
593 if (!claws_crashed())
594 item->last_seen = atoi(attr->value);
601 XMLTag *folder_item_get_xml(Folder *folder, FolderItem *item)
603 static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
604 "draft", "queue", "trash"};
605 static gchar *sort_key_str[] = {"none", "number", "size", "date",
606 "from", "subject", "score", "label",
607 "mark", "unread", "mime", "to",
612 tag = xml_tag_new("folderitem");
614 xml_tag_add_attr(tag, xml_attr_new("type", folder_item_stype_str[item->stype]));
616 xml_tag_add_attr(tag, xml_attr_new("name", item->name));
618 xml_tag_add_attr(tag, xml_attr_new("path", item->path));
620 xml_tag_add_attr(tag, xml_attr_new("no_sub", "1"));
622 xml_tag_add_attr(tag, xml_attr_new("no_select", "1"));
623 xml_tag_add_attr(tag, xml_attr_new("collapsed", item->collapsed && item->node->children ? "1" : "0"));
624 xml_tag_add_attr(tag, xml_attr_new("thread_collapsed", item->thread_collapsed ? "1" : "0"));
625 xml_tag_add_attr(tag, xml_attr_new("threaded", item->threaded ? "1" : "0"));
626 xml_tag_add_attr(tag, xml_attr_new("hidereadmsgs", item->hide_read_msgs ? "1" : "0"));
628 xml_tag_add_attr(tag, xml_attr_new("reqretrcpt", "1"));
630 if (item->sort_key != SORT_BY_NONE) {
631 xml_tag_add_attr(tag, xml_attr_new("sort_key", sort_key_str[item->sort_key]));
632 xml_tag_add_attr(tag, xml_attr_new("sort_type", item->sort_type == SORT_ASCENDING ? "ascending" : "descending"));
635 value = g_strdup_printf("%ld", (unsigned long int) item->mtime);
636 xml_tag_add_attr(tag, xml_attr_new("mtime", value));
638 xml_tag_add_attr(tag, xml_attr_new_int("new", item->new_msgs));
639 xml_tag_add_attr(tag, xml_attr_new_int("unread", item->unread_msgs));
640 xml_tag_add_attr(tag, xml_attr_new_int("unreadmarked", item->unreadmarked_msgs));
641 xml_tag_add_attr(tag, xml_attr_new_int("marked", item->marked_msgs));
642 xml_tag_add_attr(tag, xml_attr_new_int("total", item->total_msgs));
643 xml_tag_add_attr(tag, xml_attr_new_int("order", item->order));
646 xml_tag_add_attr(tag, xml_attr_new_int("account_id", item->account->account_id));
648 xml_tag_add_attr(tag, xml_attr_new("apply_sub", "1"));
650 xml_tag_add_attr(tag, xml_attr_new_int("last_seen", item->last_seen));
655 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
657 g_return_if_fail(folder != NULL);
659 folder->ui_func = func;
660 folder->ui_func_data = data;
663 void folder_set_name(Folder *folder, const gchar *name)
665 g_return_if_fail(folder != NULL);
667 g_free(folder->name);
668 folder->name = name ? g_strdup(name) : NULL;
669 if (folder->node && folder->node->data) {
670 FolderItem *item = (FolderItem *)folder->node->data;
673 item->name = name ? g_strdup(name) : NULL;
677 void folder_set_sort(Folder *folder, guint sort)
679 g_return_if_fail(folder != NULL);
681 if (folder->sort != sort) {
682 folder_remove(folder);
688 static gboolean folder_tree_destroy_func(GNode *node, gpointer data) {
689 FolderItem *item = (FolderItem *) node->data;
691 folder_item_destroy(item);
695 void folder_tree_destroy(Folder *folder)
699 g_return_if_fail(folder != NULL);
703 prefs_filtering_clear_folder(folder);
706 g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
707 folder_tree_destroy_func, NULL);
708 g_node_destroy(node);
713 void folder_add(Folder *folder)
718 FolderUpdateData hookdata;
720 g_return_if_fail(folder != NULL);
722 for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
723 cur_folder = FOLDER(cur->data);
724 if (cur_folder->sort < folder->sort)
728 folder_list = g_list_insert(folder_list, folder, i);
730 hookdata.folder = folder;
731 hookdata.update_flags = FOLDER_ADD_FOLDER;
732 hookdata.item = NULL;
733 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
736 void folder_remove(Folder *folder)
738 FolderUpdateData hookdata;
740 g_return_if_fail(folder != NULL);
742 folder_list = g_list_remove(folder_list, folder);
744 hookdata.folder = folder;
745 hookdata.update_flags = FOLDER_REMOVE_FOLDER;
746 hookdata.item = NULL;
747 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
750 GList *folder_get_list(void)
755 gint folder_read_list(void)
761 path = folder_get_list_path();
762 if (!is_file_exist(path)) return -1;
763 node = xml_parse_file(path);
764 if (!node) return -1;
766 xmlnode = node->data;
767 if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
768 g_warning("wrong folder list\n");
773 cur = node->children;
774 while (cur != NULL) {
777 folder = folder_get_from_xml(cur);
781 folder_unloaded_list = g_slist_append(folder_unloaded_list,
782 (gpointer) xml_copy_tree(cur));
787 if (folder_list || folder_unloaded_list)
793 void folder_write_list(void)
804 path = folder_get_list_path();
805 if ((pfile = prefs_write_open(path)) == NULL) return;
807 if (xml_file_put_xml_decl(pfile->fp) < 0) {
808 prefs_file_close_revert(pfile);
809 g_warning("failed to start write folder list.\n");
812 tag = xml_tag_new("folderlist");
814 xmlnode = xml_node_new(tag, NULL);
816 rootnode = g_node_new(xmlnode);
818 for (list = folder_list; list != NULL; list = list->next) {
822 node = folder_get_xml_node(folder);
824 g_node_append(rootnode, node);
827 for (slist = folder_unloaded_list; slist != NULL; slist = g_slist_next(slist)) {
828 GNode *node = (GNode *) slist->data;
830 g_node_append(rootnode, (gpointer) xml_copy_tree(node));
833 if (xml_write_tree(rootnode, pfile->fp) < 0) {
834 prefs_file_close_revert(pfile);
835 g_warning("failed to write folder list.\n");
836 } else if (prefs_file_close(pfile) < 0) {
837 g_warning("failed to write folder list.\n");
839 xml_free_tree(rootnode);
842 static gboolean folder_scan_tree_func(GNode *node, gpointer data)
844 GHashTable *pptable = (GHashTable *)data;
845 FolderItem *item = (FolderItem *)node->data;
847 folder_item_restore_persist_prefs(item, pptable);
848 folder_item_scan_full(item, FALSE);
853 void folder_scan_tree(Folder *folder, gboolean rebuild)
856 FolderUpdateData hookdata;
857 Folder *old_folder = folder;
859 if (!folder->klass->scan_tree)
862 pptable = folder_persist_prefs_new(folder);
865 folder_remove(folder);
867 if (folder->klass->scan_tree(folder) < 0) {
869 folder_add(old_folder);
874 hookdata.folder = folder;
875 hookdata.update_flags = FOLDER_TREE_CHANGED;
876 hookdata.item = NULL;
877 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
879 g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_scan_tree_func, pptable);
880 folder_persist_prefs_free(pptable);
882 prefs_matcher_read_config();
887 static gboolean folder_restore_prefs_func(GNode *node, gpointer data)
889 GHashTable *pptable = (GHashTable *)data;
890 FolderItem *item = (FolderItem *)node->data;
892 folder_item_restore_persist_prefs(item, pptable);
897 void folder_fast_scan_tree(Folder *folder)
900 FolderUpdateData hookdata;
902 if (!folder->klass->scan_tree)
905 pptable = folder_persist_prefs_new(folder);
907 if (folder->klass->scan_tree(folder) < 0) {
911 hookdata.folder = folder;
912 hookdata.update_flags = FOLDER_TREE_CHANGED;
913 hookdata.item = NULL;
914 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
916 g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_restore_prefs_func, pptable);
917 folder_persist_prefs_free(pptable);
919 prefs_matcher_read_config();
924 FolderItem *folder_create_folder(FolderItem *parent, const gchar *name)
926 FolderItem *new_item;
928 new_item = parent->folder->klass->create_folder(parent->folder, parent, name);
930 FolderUpdateData hookdata;
932 new_item->cache = msgcache_new();
934 hookdata.folder = new_item->folder;
935 hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDERITEM;
936 hookdata.item = new_item;
937 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
943 gint folder_item_rename(FolderItem *item, gchar *newname)
947 g_return_val_if_fail(item != NULL, -1);
948 g_return_val_if_fail(newname != NULL, -1);
950 retval = item->folder->klass->rename_folder(item->folder, item, newname);
953 FolderItemUpdateData hookdata;
954 FolderUpdateData hookdata2;
956 hookdata.item = item;
957 hookdata.update_flags = F_ITEM_UPDATE_NAME;
959 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
961 hookdata2.folder = item->folder;
962 hookdata2.item = item;
963 hookdata2.update_flags = FOLDER_RENAME_FOLDERITEM;
964 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata2);
974 guint unreadmarked_msgs;
979 struct FuncToAllFoldersData
981 FolderItemFunc function;
985 static gboolean folder_func_to_all_folders_func(GNode *node, gpointer data)
988 struct FuncToAllFoldersData *function_data = (struct FuncToAllFoldersData *) data;
990 g_return_val_if_fail(node->data != NULL, FALSE);
992 item = FOLDER_ITEM(node->data);
993 g_return_val_if_fail(item != NULL, FALSE);
995 function_data->function(item, function_data->data);
1000 void folder_func_to_all_folders(FolderItemFunc function, gpointer data)
1004 struct FuncToAllFoldersData function_data;
1006 function_data.function = function;
1007 function_data.data = data;
1009 for (list = folder_list; list != NULL; list = list->next) {
1010 folder = FOLDER(list->data);
1012 g_node_traverse(folder->node, G_PRE_ORDER,
1014 folder_func_to_all_folders_func,
1019 static void folder_count_total_msgs_func(FolderItem *item, gpointer data)
1021 struct TotalMsgCount *count = (struct TotalMsgCount *)data;
1023 count->new_msgs += item->new_msgs;
1024 count->unread_msgs += item->unread_msgs;
1025 count->unreadmarked_msgs += item->unreadmarked_msgs;
1026 count->marked_msgs += item->marked_msgs;
1027 count->total_msgs += item->total_msgs;
1030 struct TotalMsgStatus
1038 static gboolean folder_get_status_full_all_func(GNode *node, gpointer data)
1041 struct TotalMsgStatus *status = (struct TotalMsgStatus *)data;
1044 g_return_val_if_fail(node->data != NULL, FALSE);
1046 item = FOLDER_ITEM(node->data);
1048 if (!item->path) return FALSE;
1050 status->new += item->new_msgs;
1051 status->unread += item->unread_msgs;
1052 status->total += item->total_msgs;
1055 id = folder_item_get_identifier(item);
1056 g_string_append_printf(status->str, "%5d %5d %5d %s\n",
1057 item->new_msgs, item->unread_msgs,
1058 item->total_msgs, id);
1065 static void folder_get_status_full_all(GString *str, guint *new, guint *unread,
1070 struct TotalMsgStatus status;
1072 status.new = status.unread = status.total = 0;
1075 debug_print("Counting total number of messages...\n");
1077 for (list = folder_list; list != NULL; list = list->next) {
1078 folder = FOLDER(list->data);
1080 g_node_traverse(folder->node, G_PRE_ORDER,
1082 folder_get_status_full_all_func,
1087 *unread = status.unread;
1088 *total = status.total;
1091 gchar *folder_get_status(GPtrArray *folders, gboolean full)
1093 guint new, unread, total;
1098 new = unread = total = 0;
1100 str = g_string_new(NULL);
1103 for (i = 0; i < folders->len; i++) {
1106 item = g_ptr_array_index(folders, i);
1107 new += item->new_msgs;
1108 unread += item->unread_msgs;
1109 total += item->total_msgs;
1114 id = folder_item_get_identifier(item);
1115 g_string_append_printf(str, "%5d %5d %5d %s\n",
1116 item->new_msgs, item->unread_msgs,
1117 item->total_msgs, id);
1122 folder_get_status_full_all(full ? str : NULL,
1123 &new, &unread, &total);
1127 g_string_append_printf(str, "%5d %5d %5d\n", new, unread, total);
1129 g_string_append_printf(str, "%d %d %d\n", new, unread, total);
1132 g_string_free(str, FALSE);
1137 void folder_count_total_msgs(guint *new_msgs, guint *unread_msgs,
1138 guint *unreadmarked_msgs, guint *marked_msgs,
1141 struct TotalMsgCount count;
1143 count.new_msgs = count.unread_msgs = count.unreadmarked_msgs = count.total_msgs = 0;
1145 debug_print("Counting total number of messages...\n");
1147 folder_func_to_all_folders(folder_count_total_msgs_func, &count);
1149 *new_msgs = count.new_msgs;
1150 *unread_msgs = count.unread_msgs;
1151 *unreadmarked_msgs = count.unreadmarked_msgs;
1152 *marked_msgs = count.marked_msgs;
1153 *total_msgs = count.total_msgs;
1156 Folder *folder_find_from_path(const gchar *path)
1161 for (list = folder_list; list != NULL; list = list->next) {
1162 folder = list->data;
1163 if ((FOLDER_TYPE(folder) == F_MH ||
1164 FOLDER_TYPE(folder) == F_MBOX) &&
1165 !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
1172 Folder *folder_find_from_name(const gchar *name, FolderClass *klass)
1177 for (list = folder_list; list != NULL; list = list->next) {
1178 folder = list->data;
1179 if (folder->klass == klass &&
1180 strcmp2(name, folder->name) == 0)
1187 static gboolean folder_item_find_func(GNode *node, gpointer data)
1189 FolderItem *item = node->data;
1191 const gchar *path = d[0];
1193 if (path_cmp(path, item->path) != 0)
1201 FolderItem *folder_find_item_from_path(const gchar *path)
1205 GList *list = folder_get_list();
1207 folder = list ? list->data:NULL;
1209 g_return_val_if_fail(folder != NULL, NULL);
1211 d[0] = (gpointer)path;
1213 while (d[1] == NULL && list) {
1214 folder = FOLDER(list->data);
1215 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1216 folder_item_find_func, d);
1222 FolderItem *folder_find_child_item_by_name(FolderItem *item, const gchar *name)
1227 for (node = item->node->children; node != NULL; node = node->next) {
1229 child = FOLDER_ITEM(node->data);
1230 base = g_path_get_basename(child->path);
1231 if (strcmp2(base, name) == 0) {
1241 FolderClass *folder_get_class_from_string(const gchar *str)
1245 classlist = folder_get_class_list();
1246 for (; classlist != NULL; classlist = g_slist_next(classlist)) {
1247 FolderClass *class = (FolderClass *) classlist->data;
1248 if (g_ascii_strcasecmp(class->idstr, str) == 0)
1255 static gchar *folder_get_identifier(Folder *folder)
1259 g_return_val_if_fail(folder != NULL, NULL);
1261 type_str = folder->klass->idstr;
1262 return g_strconcat("#", type_str, "/", folder->name, NULL);
1265 gchar *folder_item_get_identifier(FolderItem *item)
1268 gchar *folder_id = NULL;
1270 g_return_val_if_fail(item != NULL, NULL);
1272 if (item->path == NULL)
1275 folder_id = folder_get_identifier(item->folder);
1276 id = g_strconcat(folder_id, "/", item->path, NULL);
1282 FolderItem *folder_find_item_from_identifier(const gchar *identifier)
1292 g_return_val_if_fail(identifier != NULL, NULL);
1294 if (*identifier != '#')
1295 return folder_find_item_from_path(identifier);
1297 Xstrdup_a(str, identifier, return NULL);
1299 p = strchr(str, '/');
1301 return folder_find_item_from_path(identifier);
1304 class = folder_get_class_from_string(&str[1]);
1306 return folder_find_item_from_path(identifier);
1311 return folder_find_item_from_path(identifier);
1315 folder = folder_find_from_name(name, class);
1317 return folder_find_item_from_path(identifier);
1321 d[0] = (gpointer)path;
1323 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1324 folder_item_find_func, d);
1329 * Get a displayable name for a FolderItem
1331 * \param item FolderItem for that a name should be created
1332 * \return Displayable name for item, returned string has to
1335 gchar *folder_item_get_name(FolderItem *item)
1339 g_return_val_if_fail(item != NULL, g_strdup(""));
1341 switch (item->stype) {
1343 name = g_strdup(!strcmp2(item->name, INBOX_DIR) ? _("Inbox") :
1347 name = g_strdup(!strcmp2(item->name, OUTBOX_DIR) ? _("Sent") :
1351 name = g_strdup(!strcmp2(item->name, QUEUE_DIR) ? _("Queue") :
1355 name = g_strdup(!strcmp2(item->name, TRASH_DIR) ? _("Trash") :
1359 name = g_strdup(!strcmp2(item->name, DRAFT_DIR) ? _("Drafts") :
1368 * should probably be done by a virtual function,
1369 * the folder knows the ui string and how to abbrev
1371 if (folder_item_parent(item) == NULL) {
1372 name = g_strconcat(item->name, " (", item->folder->klass->uistr, ")", NULL);
1374 if (FOLDER_CLASS(item->folder) == news_get_class() &&
1375 item->path && !strcmp2(item->name, item->path))
1376 name = get_abbrev_newsgroup_name
1378 prefs_common.ng_abbrev_len);
1380 name = g_strdup(item->name);
1385 name = g_strdup("");
1390 gboolean folder_have_mailbox (void)
1393 for (cur = folder_list; cur != NULL; cur = g_list_next(cur)) {
1394 Folder *folder = FOLDER(cur->data);
1395 if (folder->inbox && folder->outbox)
1401 FolderItem *folder_get_default_inbox(void)
1405 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1406 Folder * folder = FOLDER(flist->data);
1410 if (folder->inbox == NULL)
1412 if (folder->klass->type == F_UNKNOWN)
1415 return folder->inbox;
1421 FolderItem *folder_get_default_inbox_for_class(FolderType type)
1425 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1426 Folder * folder = FOLDER(flist->data);
1430 if (folder->inbox == NULL)
1432 if (folder->klass->type != type)
1435 return folder->inbox;
1441 FolderItem *folder_get_default_outbox(void)
1445 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1446 Folder * folder = FOLDER(flist->data);
1450 if (folder->outbox == NULL)
1452 if (folder->klass->type == F_UNKNOWN)
1455 return folder->outbox;
1461 FolderItem *folder_get_default_draft(void)
1465 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1466 Folder * folder = FOLDER(flist->data);
1470 if (folder->draft == NULL)
1472 if (folder->klass->type == F_UNKNOWN)
1475 return folder->draft;
1481 FolderItem *folder_get_default_queue(void)
1485 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1486 Folder * folder = FOLDER(flist->data);
1490 if (folder->queue == NULL)
1492 if (folder->klass->type == F_UNKNOWN)
1495 return folder->queue;
1501 FolderItem *folder_get_default_trash(void)
1505 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1506 Folder * folder = FOLDER(flist->data);
1510 if (folder->trash == NULL)
1512 if (folder->klass->type == F_UNKNOWN)
1515 return folder->trash;
1521 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type) \
1523 if (!folder->member) { \
1524 item = folder_item_new(folder, dir, dir); \
1525 item->stype = type; \
1526 folder_item_append(rootitem, item); \
1527 folder->member = item; \
1531 void folder_set_missing_folders(void)
1534 FolderItem *rootitem;
1538 for (list = folder_list; list != NULL; list = list->next) {
1539 folder = list->data;
1540 if (FOLDER_TYPE(folder) != F_MH) continue;
1541 rootitem = FOLDER_ITEM(folder->node->data);
1542 g_return_if_fail(rootitem != NULL);
1544 if (folder->inbox && folder->outbox && folder->draft &&
1545 folder->queue && folder->trash)
1548 if (folder->klass->create_tree(folder) < 0) {
1549 g_warning("%s: can't create the folder tree.\n",
1550 LOCAL_FOLDER(folder)->rootpath);
1554 CREATE_FOLDER_IF_NOT_EXIST(inbox, INBOX_DIR, F_INBOX);
1555 CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
1556 CREATE_FOLDER_IF_NOT_EXIST(draft, DRAFT_DIR, F_DRAFT);
1557 CREATE_FOLDER_IF_NOT_EXIST(queue, QUEUE_DIR, F_QUEUE);
1558 CREATE_FOLDER_IF_NOT_EXIST(trash, TRASH_DIR, F_TRASH);
1562 static gboolean folder_unref_account_func(GNode *node, gpointer data)
1564 FolderItem *item = node->data;
1565 PrefsAccount *account = data;
1567 if (item->account == account)
1568 item->account = NULL;
1573 void folder_unref_account_all(PrefsAccount *account)
1578 if (!account) return;
1580 for (list = folder_list; list != NULL; list = list->next) {
1581 folder = list->data;
1582 if (folder->account == account)
1583 folder->account = NULL;
1584 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1585 folder_unref_account_func, account);
1589 #undef CREATE_FOLDER_IF_NOT_EXIST
1591 gchar *folder_item_get_path(FolderItem *item)
1595 g_return_val_if_fail(item != NULL, NULL);
1596 folder = item->folder;
1597 g_return_val_if_fail(folder != NULL, NULL);
1599 return folder->klass->item_get_path(folder, item);
1602 void folder_item_set_default_flags(FolderItem *dest, MsgFlags *flags)
1604 if (!(folder_has_parent_of_type(dest, F_OUTBOX) ||
1605 folder_has_parent_of_type(dest, F_QUEUE) ||
1606 folder_has_parent_of_type(dest, F_DRAFT) ||
1607 folder_has_parent_of_type(dest, F_TRASH))) {
1608 flags->perm_flags = MSG_NEW|MSG_UNREAD;
1610 flags->perm_flags = 0;
1612 if (FOLDER_TYPE(dest->folder) == F_MH) {
1613 if (folder_has_parent_of_type(dest, F_QUEUE)) {
1614 MSG_SET_TMP_FLAGS(*flags, MSG_QUEUED);
1615 } else if (folder_has_parent_of_type(dest, F_DRAFT)) {
1616 MSG_SET_TMP_FLAGS(*flags, MSG_DRAFT);
1621 static gint folder_sort_cache_list_by_msgnum(gconstpointer a, gconstpointer b)
1623 MsgInfo *msginfo_a = (MsgInfo *) a;
1624 MsgInfo *msginfo_b = (MsgInfo *) b;
1626 return (msginfo_a->msgnum - msginfo_b->msgnum);
1629 static gint folder_sort_folder_list(gconstpointer a, gconstpointer b)
1631 guint gint_a = GPOINTER_TO_INT(a);
1632 guint gint_b = GPOINTER_TO_INT(b);
1634 return (gint_a - gint_b);
1637 void folder_item_process_open (FolderItem *item,
1638 void (*before_proc_func)(gpointer data),
1639 void (*after_proc_func)(gpointer data),
1645 if((item->folder->klass->scan_required != NULL) &&
1646 (item->folder->klass->scan_required(item->folder, item))) {
1647 folder_item_scan_full(item, TRUE);
1649 folder_item_syncronize_flags(item);
1653 buf = g_strdup_printf(_("Processing (%s)...\n"),
1654 item->path ? item->path : item->name);
1657 if (before_proc_func)
1658 before_proc_func(data);
1660 folder_item_apply_processing(item);
1662 if (after_proc_func)
1663 after_proc_func(data);
1665 item->processing_pending = FALSE;
1669 gint folder_item_open(FolderItem *item)
1672 if (item->no_select)
1675 if (item->scanning) {
1676 debug_print("%s is scanning... \n", item->path ? item->path : item->name);
1680 item->processing_pending = TRUE;
1681 folder_item_process_open (item, NULL, NULL, NULL);
1683 item->opened = TRUE;
1688 gint folder_item_close(FolderItem *item)
1690 GSList *mlist, *cur;
1693 g_return_val_if_fail(item != NULL, -1);
1695 if (item->no_select)
1698 if (item->new_msgs) {
1699 folder_item_update_freeze();
1700 mlist = folder_item_get_msg_list(item);
1701 for (cur = mlist ; cur != NULL ; cur = cur->next) {
1704 msginfo = (MsgInfo *) cur->data;
1705 if (MSG_IS_NEW(msginfo->flags))
1706 procmsg_msginfo_unset_flags(msginfo, MSG_NEW, 0);
1707 procmsg_msginfo_free(msginfo);
1709 g_slist_free(mlist);
1710 folder_item_update_thaw();
1713 folder_item_write_cache(item);
1715 folder_item_update(item, F_ITEM_UPDATE_MSGCNT);
1717 item->opened = FALSE;
1718 folder = item->folder;
1720 if (folder->klass->close == NULL)
1723 return folder->klass->close(folder, item);
1726 static MsgInfoList *get_msginfos(FolderItem *item, MsgNumberList *numlist)
1728 MsgInfoList *msglist = NULL;
1729 Folder *folder = item->folder;
1730 if (item->no_select)
1733 if (folder->klass->get_msginfos != NULL)
1734 msglist = folder->klass->get_msginfos(folder, item, numlist);
1736 MsgNumberList *elem;
1738 for (elem = numlist; elem != NULL; elem = g_slist_next(elem)) {
1742 num = GPOINTER_TO_INT(elem->data);
1743 msginfo = folder->klass->get_msginfo(folder, item, num);
1744 if (msginfo != NULL)
1745 msglist = g_slist_prepend(msglist, msginfo);
1752 static MsgInfo *get_msginfo(FolderItem *item, guint num)
1754 MsgNumberList numlist;
1755 MsgInfoList *msglist;
1756 MsgInfo *msginfo = NULL;
1758 numlist.data = GINT_TO_POINTER(num);
1759 numlist.next = NULL;
1760 msglist = get_msginfos(item, &numlist);
1761 if (msglist != NULL)
1762 msginfo = procmsg_msginfo_new_ref(msglist->data);
1763 procmsg_msg_list_free(msglist);
1768 static gint syncronize_flags(FolderItem *item, MsgInfoList *msglist)
1770 GRelation *relation;
1776 if(item->folder->klass->get_flags == NULL)
1778 if (item->no_select)
1781 relation = g_relation_new(2);
1782 g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
1783 folder_item_set_batch(item, TRUE);
1784 if ((ret = item->folder->klass->get_flags(
1785 item->folder, item, msglist, relation)) == 0) {
1788 MsgPermFlags permflags = 0;
1791 folder_item_update_freeze();
1792 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1793 msginfo = (MsgInfo *) cur->data;
1795 tuples = g_relation_select(relation, msginfo, 0);
1796 skip = tuples->len < 1;
1798 permflags = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1799 g_tuples_destroy(tuples);
1803 if (msginfo->flags.perm_flags != permflags) {
1804 procmsg_msginfo_change_flags(msginfo,
1805 permflags & ~msginfo->flags.perm_flags, 0,
1806 ~permflags & msginfo->flags.perm_flags, 0);
1809 folder_item_update_thaw();
1811 folder_item_set_batch(item, FALSE);
1812 g_relation_destroy(relation);
1817 gint folder_item_scan_full(FolderItem *item, gboolean filtering)
1820 GSList *folder_list = NULL, *cache_list = NULL;
1821 GSList *folder_list_cur, *cache_list_cur, *new_list = NULL;
1822 GSList *exists_list = NULL, *elem;
1823 GSList *newmsg_list = NULL;
1824 guint newcnt = 0, unreadcnt = 0, totalcnt = 0;
1825 guint markedcnt = 0, unreadmarkedcnt = 0;
1826 guint cache_max_num, folder_max_num, cache_cur_num, folder_cur_num;
1827 gboolean update_flags = 0, old_uids_valid = FALSE;
1828 GHashTable *subject_table = NULL;
1830 g_return_val_if_fail(item != NULL, -1);
1831 if (item->path == NULL) return -1;
1833 folder = item->folder;
1835 g_return_val_if_fail(folder != NULL, -1);
1836 g_return_val_if_fail(folder->klass->get_num_list != NULL, -1);
1838 item->scanning = TRUE;
1840 debug_print("Scanning folder %s for cache changes.\n", item->path ? item->path : "(null)");
1842 /* Get list of messages for folder and cache */
1843 if (folder->klass->get_num_list(item->folder, item, &folder_list, &old_uids_valid) < 0) {
1844 debug_print("Error fetching list of message numbers\n");
1845 item->scanning = FALSE;
1849 if(prefs_common.thread_by_subject) {
1850 subject_table = g_hash_table_new(g_str_hash, g_str_equal);
1853 if (old_uids_valid) {
1855 folder_item_read_cache(item);
1856 cache_list = msgcache_get_msg_list(item->cache);
1859 msgcache_destroy(item->cache);
1860 item->cache = msgcache_new();
1864 /* Sort both lists */
1865 cache_list = g_slist_sort(cache_list, folder_sort_cache_list_by_msgnum);
1866 folder_list = g_slist_sort(folder_list, folder_sort_folder_list);
1868 cache_list_cur = cache_list;
1869 folder_list_cur = folder_list;
1871 if (cache_list_cur != NULL) {
1872 GSList *cache_list_last;
1874 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1875 cache_list_last = g_slist_last(cache_list);
1876 cache_max_num = ((MsgInfo *)cache_list_last->data)->msgnum;
1878 cache_cur_num = G_MAXINT;
1882 if (folder_list_cur != NULL) {
1883 GSList *folder_list_last;
1885 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1886 folder_list_last = g_slist_last(folder_list);
1887 folder_max_num = GPOINTER_TO_INT(folder_list_last->data);
1889 folder_cur_num = G_MAXINT;
1893 while ((cache_cur_num != G_MAXINT) || (folder_cur_num != G_MAXINT)) {
1895 * Message only exists in the folder
1896 * Remember message for fetching
1898 if (folder_cur_num < cache_cur_num) {
1899 gboolean add = FALSE;
1901 switch(FOLDER_TYPE(folder)) {
1903 if (folder_cur_num < cache_max_num)
1906 if (folder->account->max_articles == 0) {
1910 if (folder_max_num <= folder->account->max_articles) {
1912 } else if (folder_cur_num > (folder_max_num - folder->account->max_articles)) {
1922 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(folder_cur_num));
1923 debug_print("Remembered message %d for fetching\n", folder_cur_num);
1926 /* Move to next folder number */
1927 folder_list_cur = folder_list_cur->next;
1929 if (folder_list_cur != NULL)
1930 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1932 folder_cur_num = G_MAXINT;
1938 * Message only exists in the cache
1939 * Remove the message from the cache
1941 if (cache_cur_num < folder_cur_num) {
1942 msgcache_remove_msg(item->cache, cache_cur_num);
1943 debug_print("Removed message %d from cache.\n", cache_cur_num);
1945 /* Move to next cache number */
1946 cache_list_cur = cache_list_cur->next;
1948 if (cache_list_cur != NULL)
1949 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1951 cache_cur_num = G_MAXINT;
1953 update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
1959 * Message number exists in folder and cache!
1960 * Check if the message has been modified
1962 if (cache_cur_num == folder_cur_num) {
1965 msginfo = msgcache_get_msg(item->cache, folder_cur_num);
1966 if (folder->klass->is_msg_changed && folder->klass->is_msg_changed(folder, item, msginfo)) {
1967 msgcache_remove_msg(item->cache, msginfo->msgnum);
1968 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(msginfo->msgnum));
1969 procmsg_msginfo_free(msginfo);
1971 debug_print("Remembering message %d to update...\n", folder_cur_num);
1973 exists_list = g_slist_prepend(exists_list, msginfo);
1975 if(prefs_common.thread_by_subject &&
1976 MSG_IS_IGNORE_THREAD(msginfo->flags) &&
1977 !subject_table_lookup(subject_table, msginfo->subject)) {
1978 subject_table_insert(subject_table, msginfo->subject, msginfo);
1982 /* Move to next folder and cache number */
1983 cache_list_cur = cache_list_cur->next;
1984 folder_list_cur = folder_list_cur->next;
1986 if (cache_list_cur != NULL)
1987 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1989 cache_cur_num = G_MAXINT;
1991 if (folder_list_cur != NULL)
1992 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1994 folder_cur_num = G_MAXINT;
2000 for(cache_list_cur = cache_list; cache_list_cur != NULL; cache_list_cur = g_slist_next(cache_list_cur))
2001 procmsg_msginfo_free((MsgInfo *) cache_list_cur->data);
2003 g_slist_free(cache_list);
2004 g_slist_free(folder_list);
2006 if (new_list != NULL) {
2007 GSList *tmp_list = NULL;
2008 newmsg_list = get_msginfos(item, new_list);
2009 g_slist_free(new_list);
2010 tmp_list = g_slist_concat(g_slist_copy(exists_list), g_slist_copy(newmsg_list));
2011 syncronize_flags(item, tmp_list);
2012 g_slist_free(tmp_list);
2014 syncronize_flags(item, exists_list);
2017 folder_item_update_freeze();
2018 if (newmsg_list != NULL) {
2019 GSList *elem, *to_filter = NULL;
2020 gboolean do_filter = (filtering == TRUE) &&
2021 (item->stype == F_INBOX) &&
2022 (item->folder->account != NULL) &&
2023 (item->folder->account->filter_on_recv);
2025 for (elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) {
2026 MsgInfo *msginfo = (MsgInfo *) elem->data;
2028 msgcache_add_msg(item->cache, msginfo);
2030 exists_list = g_slist_prepend(exists_list, msginfo);
2032 if(prefs_common.thread_by_subject &&
2033 MSG_IS_IGNORE_THREAD(msginfo->flags) &&
2034 !subject_table_lookup(subject_table, msginfo->subject)) {
2035 subject_table_insert(subject_table, msginfo->subject, msginfo);
2042 procmsg_msglist_filter(newmsg_list, item->folder->account,
2043 &to_filter, &unfiltered,
2046 filtering_move_and_copy_msgs(newmsg_list);
2047 if (to_filter != NULL) {
2048 for (elem = to_filter; elem; elem = g_slist_next(elem)) {
2049 MsgInfo *msginfo = (MsgInfo *)elem->data;
2050 procmsg_msginfo_free(msginfo);
2052 g_slist_free(to_filter);
2054 if (unfiltered != NULL) {
2055 for (elem = unfiltered; elem; elem = g_slist_next(elem)) {
2056 MsgInfo *msginfo = (MsgInfo *)elem->data;
2057 exists_list = g_slist_prepend(exists_list, msginfo);
2059 if(prefs_common.thread_by_subject &&
2060 MSG_IS_IGNORE_THREAD(msginfo->flags) &&
2061 !subject_table_lookup(subject_table, msginfo->subject)) {
2062 subject_table_insert(subject_table, msginfo->subject, msginfo);
2065 g_slist_free(unfiltered);
2067 if (prefs_common.real_time_sync)
2068 folder_item_synchronise(item);
2070 if (prefs_common.real_time_sync)
2071 folder_item_synchronise(item);
2074 g_slist_free(newmsg_list);
2076 update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
2079 folder_item_set_batch(item, TRUE);
2080 for (elem = exists_list; elem != NULL; elem = g_slist_next(elem)) {
2081 MsgInfo *msginfo, *parent_msginfo;
2083 msginfo = elem->data;
2084 if (MSG_IS_IGNORE_THREAD(msginfo->flags) && (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
2085 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
2086 if (!MSG_IS_IGNORE_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD)) {
2087 procmsg_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0, MSG_NEW | MSG_UNREAD, 0);
2089 if(prefs_common.thread_by_subject && !msginfo->inreplyto &&
2090 !msginfo->references && !MSG_IS_IGNORE_THREAD(msginfo->flags) &&
2091 (parent_msginfo = subject_table_lookup(subject_table, msginfo->subject)))
2093 if(MSG_IS_IGNORE_THREAD(parent_msginfo->flags)) {
2094 procmsg_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0,
2095 MSG_NEW | MSG_UNREAD, 0);
2098 if ((folder_has_parent_of_type(item, F_OUTBOX) ||
2099 folder_has_parent_of_type(item, F_QUEUE) ||
2100 folder_has_parent_of_type(item, F_DRAFT) ||
2101 folder_has_parent_of_type(item, F_TRASH)) &&
2102 (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
2103 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
2104 if (MSG_IS_NEW(msginfo->flags))
2106 if (MSG_IS_UNREAD(msginfo->flags))
2108 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
2110 if (MSG_IS_MARKED(msginfo->flags))
2115 procmsg_msginfo_free(msginfo);
2117 folder_item_set_batch(item, FALSE);
2118 g_slist_free(exists_list);
2120 if(prefs_common.thread_by_subject) {
2121 g_hash_table_destroy(subject_table);
2124 if (item->new_msgs != newcnt || item->unread_msgs != unreadcnt
2125 || item->total_msgs != totalcnt || item->marked_msgs != markedcnt
2126 || item->unreadmarked_msgs != unreadmarkedcnt) {
2127 update_flags |= F_ITEM_UPDATE_CONTENT;
2130 item->new_msgs = newcnt;
2131 item->unread_msgs = unreadcnt;
2132 item->total_msgs = totalcnt;
2133 item->unreadmarked_msgs = unreadmarkedcnt;
2134 item->marked_msgs = markedcnt;
2136 update_flags |= F_ITEM_UPDATE_MSGCNT;
2138 folder_item_update(item, update_flags);
2139 folder_item_update_thaw();
2141 item->scanning = FALSE;
2146 gint folder_item_scan(FolderItem *item)
2148 return folder_item_scan_full(item, TRUE);
2151 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
2154 folder_item_scan(FOLDER_ITEM(key));
2157 void folder_item_scan_foreach(GHashTable *table)
2159 g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
2162 static void folder_count_total_cache_memusage(FolderItem *item, gpointer data)
2164 gint *memusage = (gint *)data;
2166 if (item->cache == NULL)
2169 *memusage += msgcache_get_memory_usage(item->cache);
2172 gint folder_item_syncronize_flags(FolderItem *item)
2174 MsgInfoList *msglist = NULL;
2178 g_return_val_if_fail(item != NULL, -1);
2179 g_return_val_if_fail(item->folder != NULL, -1);
2180 g_return_val_if_fail(item->folder->klass != NULL, -1);
2181 if (item->no_select)
2184 item->scanning = TRUE;
2186 if (item->cache == NULL)
2187 folder_item_read_cache(item);
2189 msglist = msgcache_get_msg_list(item->cache);
2191 ret = syncronize_flags(item, msglist);
2193 for (cur = msglist; cur != NULL; cur = g_slist_next(cur))
2194 procmsg_msginfo_free((MsgInfo *) cur->data);
2196 g_slist_free(msglist);
2198 item->scanning = FALSE;
2203 static gint folder_cache_time_compare_func(gconstpointer a, gconstpointer b)
2205 FolderItem *fa = (FolderItem *)a;
2206 FolderItem *fb = (FolderItem *)b;
2208 return (gint) (msgcache_get_last_access_time(fa->cache) - msgcache_get_last_access_time(fb->cache));
2211 static void folder_find_expired_caches(FolderItem *item, gpointer data)
2213 GSList **folder_item_list = (GSList **)data;
2214 gint difftime, expiretime;
2216 if (item->cache == NULL)
2219 if (item->opened > 0)
2222 difftime = (gint) (time(NULL) - msgcache_get_last_access_time(item->cache));
2223 expiretime = prefs_common.cache_min_keep_time * 60;
2224 debug_print("Cache unused time: %d (Expire time: %d)\n", difftime, expiretime);
2226 if (difftime > expiretime && !item->opened && !item->processing_pending) {
2227 *folder_item_list = g_slist_insert_sorted(*folder_item_list, item, folder_cache_time_compare_func);
2231 gboolean folder_item_free_cache(FolderItem *item, gboolean force)
2233 g_return_val_if_fail(item != NULL, TRUE);
2235 if (item->cache == NULL)
2238 if (item->opened > 0 && !force)
2241 folder_item_write_cache(item);
2242 msgcache_destroy(item->cache);
2247 void folder_clean_cache_memory_force(void)
2249 int old_cache_max_mem_usage = prefs_common.cache_max_mem_usage;
2250 int old_cache_min_keep_time = prefs_common.cache_min_keep_time;
2252 prefs_common.cache_max_mem_usage = 0;
2253 prefs_common.cache_min_keep_time = 0;
2255 folder_clean_cache_memory(NULL);
2257 prefs_common.cache_max_mem_usage = old_cache_max_mem_usage;
2258 prefs_common.cache_min_keep_time = old_cache_min_keep_time;
2261 void folder_clean_cache_memory(FolderItem *protected_item)
2265 folder_func_to_all_folders(folder_count_total_cache_memusage, &memusage);
2266 debug_print("Total cache memory usage: %d\n", memusage);
2268 if (memusage > (prefs_common.cache_max_mem_usage * 1024)) {
2269 GSList *folder_item_list = NULL, *listitem;
2271 debug_print("Trying to free cache memory\n");
2273 folder_func_to_all_folders(folder_find_expired_caches, &folder_item_list);
2274 listitem = folder_item_list;
2275 while((listitem != NULL) && (memusage > (prefs_common.cache_max_mem_usage * 1024))) {
2276 FolderItem *item = (FolderItem *)(listitem->data);
2277 gint cache_size = 0;
2278 if (item == protected_item) {
2279 listitem = listitem->next;
2282 debug_print("Freeing cache memory for %s\n", item->path ? item->path : item->name);
2283 cache_size = msgcache_get_memory_usage(item->cache);
2284 if (folder_item_free_cache(item, FALSE))
2285 memusage -= cache_size;
2287 listitem = listitem->next;
2289 g_slist_free(folder_item_list);
2293 static void folder_item_read_cache(FolderItem *item)
2295 gchar *cache_file, *mark_file;
2297 g_return_if_fail(item != NULL);
2299 if (item->path != NULL) {
2300 cache_file = folder_item_get_cache_file(item);
2301 mark_file = folder_item_get_mark_file(item);
2302 item->cache = msgcache_read_cache(item, cache_file);
2304 MsgInfoList *list, *cur;
2305 guint newcnt = 0, unreadcnt = 0;
2306 guint markedcnt = 0, unreadmarkedcnt = 0;
2309 item->cache = msgcache_new();
2310 folder_item_scan_full(item, TRUE);
2312 msgcache_read_mark(item->cache, mark_file);
2314 list = msgcache_get_msg_list(item->cache);
2315 for (cur = list; cur != NULL; cur = g_slist_next(cur)) {
2316 msginfo = cur->data;
2318 if (MSG_IS_NEW(msginfo->flags))
2320 if (MSG_IS_UNREAD(msginfo->flags))
2322 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
2324 if (MSG_IS_MARKED(msginfo->flags))
2327 item->new_msgs = newcnt;
2328 item->unread_msgs = unreadcnt;
2329 item->unreadmarked_msgs = unreadmarkedcnt;
2330 item->marked_msgs = markedcnt;
2331 procmsg_msg_list_free(list);
2333 msgcache_read_mark(item->cache, mark_file);
2338 item->cache = msgcache_new();
2341 folder_clean_cache_memory(item);
2344 void folder_item_write_cache(FolderItem *item)
2346 gchar *cache_file, *mark_file;
2347 FolderItemPrefs *prefs;
2350 time_t last_mtime = (time_t)0;
2351 gboolean need_scan = FALSE;
2353 if (!item || !item->path || !item->cache)
2356 if (FOLDER_TYPE(item->folder) == F_MH) {
2357 last_mtime = item->mtime;
2358 need_scan = item->folder->klass->scan_required(item->folder, item);
2361 id = folder_item_get_identifier(item);
2362 debug_print("Save cache for folder %s\n", id);
2365 cache_file = folder_item_get_cache_file(item);
2366 mark_file = folder_item_get_mark_file(item);
2367 if (msgcache_write(cache_file, mark_file, item->cache) < 0) {
2368 prefs = item->prefs;
2369 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
2370 /* for cache file */
2371 filemode = prefs->folder_chmod;
2372 if (filemode & S_IRGRP) filemode |= S_IWGRP;
2373 if (filemode & S_IROTH) filemode |= S_IWOTH;
2374 chmod(cache_file, filemode);
2378 if (!need_scan && FOLDER_TYPE(item->folder) == F_MH) {
2379 if (item->mtime == last_mtime) {
2388 MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
2391 MsgInfo *msginfo = NULL;
2393 g_return_val_if_fail(item != NULL, NULL);
2394 if (item->no_select)
2396 folder = item->folder;
2398 folder_item_read_cache(item);
2400 if ((msginfo = msgcache_get_msg(item->cache, num)) != NULL)
2403 msginfo = get_msginfo(item, num);
2404 if (msginfo != NULL) {
2405 msgcache_add_msg(item->cache, msginfo);
2412 MsgInfo *folder_item_get_msginfo_by_msgid(FolderItem *item, const gchar *msgid)
2417 g_return_val_if_fail(item != NULL, NULL);
2418 g_return_val_if_fail(msgid != NULL, NULL);
2419 if (item->no_select)
2422 folder = item->folder;
2424 folder_item_read_cache(item);
2426 if ((msginfo = msgcache_get_msg_by_id(item->cache, msgid)) != NULL)
2432 GSList *folder_item_get_msg_list(FolderItem *item)
2434 g_return_val_if_fail(item != NULL, NULL);
2435 if (item->no_select)
2438 if (item->cache == 0)
2439 folder_item_read_cache(item);
2441 g_return_val_if_fail(item->cache != NULL, NULL);
2443 return msgcache_get_msg_list(item->cache);
2446 static void msginfo_set_mime_flags(GNode *node, gpointer data)
2448 MsgInfo *msginfo = data;
2449 MimeInfo *mimeinfo = node->data;
2451 if (mimeinfo->disposition == DISPOSITIONTYPE_ATTACHMENT
2452 && (!mimeinfo->subtype || strcmp(mimeinfo->subtype, "pgp-signature"))) {
2453 procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT);
2454 } else if (mimeinfo->disposition == DISPOSITIONTYPE_UNKNOWN &&
2455 mimeinfo->type != MIMETYPE_TEXT &&
2456 mimeinfo->type != MIMETYPE_MULTIPART) {
2457 if (!mimeinfo->subtype
2458 || strcmp(mimeinfo->subtype, "pgp-signature"))
2459 procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT);
2460 } else if (mimeinfo->disposition == DISPOSITIONTYPE_INLINE &&
2461 strcmp(mimeinfo->subtype, "pgp-signature") &&
2462 (procmime_mimeinfo_get_parameter(mimeinfo, "name") != NULL ||
2463 procmime_mimeinfo_get_parameter(mimeinfo, "filename") != NULL)) {
2464 procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT);
2467 /* don't descend below top level message for signed and encrypted info */
2468 if (mimeinfo->type == MIMETYPE_MESSAGE)
2471 if (privacy_mimeinfo_is_signed(mimeinfo)) {
2472 procmsg_msginfo_set_flags(msginfo, 0, MSG_SIGNED);
2475 if (privacy_mimeinfo_is_encrypted(mimeinfo)) {
2476 procmsg_msginfo_set_flags(msginfo, 0, MSG_ENCRYPTED);
2478 /* searching inside encrypted parts doesn't really make sense */
2479 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2483 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
2489 g_return_val_if_fail(item != NULL, NULL);
2491 folder = item->folder;
2493 g_return_val_if_fail(folder->klass->fetch_msg != NULL, NULL);
2494 if (item->no_select)
2497 msgfile = folder->klass->fetch_msg(folder, item, num);
2499 if (msgfile != NULL) {
2500 msginfo = folder_item_get_msginfo(item, num);
2501 if ((msginfo != NULL) && !MSG_IS_SCANNED(msginfo->flags)) {
2504 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2505 !folder_has_parent_of_type(msginfo->folder, F_DRAFT))
2506 mimeinfo = procmime_scan_file(msgfile);
2508 mimeinfo = procmime_scan_queue_file(msgfile);
2509 /* check for attachments */
2510 if (mimeinfo != NULL) {
2511 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2512 procmime_mimeinfo_free_all(mimeinfo);
2514 procmsg_msginfo_set_flags(msginfo, 0, MSG_SCANNED);
2517 procmsg_msginfo_free(msginfo);
2523 gchar *folder_item_fetch_msg_full(FolderItem *item, gint num, gboolean headers,
2530 g_return_val_if_fail(item != NULL, NULL);
2531 if (item->no_select)
2534 folder = item->folder;
2536 if (folder->klass->fetch_msg_full == NULL)
2537 return folder_item_fetch_msg(item, num);
2539 if (item->prefs->offlinesync && prefs_common.real_time_sync)
2540 msgfile = folder->klass->fetch_msg_full(folder, item, num,
2543 msgfile = folder->klass->fetch_msg_full(folder, item, num,
2546 if (msgfile != NULL) {
2547 msginfo = folder_item_get_msginfo(item, num);
2548 if ((msginfo != NULL) && !MSG_IS_SCANNED(msginfo->flags)) {
2551 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2552 !folder_has_parent_of_type(msginfo->folder, F_DRAFT))
2553 mimeinfo = procmime_scan_file(msgfile);
2555 mimeinfo = procmime_scan_queue_file(msgfile);
2556 /* check for attachments */
2557 if (mimeinfo != NULL) {
2558 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2559 procmime_mimeinfo_free_all(mimeinfo);
2561 procmsg_msginfo_set_flags(msginfo, 0, MSG_SCANNED);
2564 procmsg_msginfo_free(msginfo);
2571 gint folder_item_fetch_all_msg(FolderItem *item)
2580 g_return_val_if_fail(item != NULL, -1);
2581 if (item->no_select)
2584 debug_print("fetching all messages in %s ...\n", item->path ? item->path : "(null)");
2585 statusbar_print_all(_("Fetching all messages in %s ...\n"), item->path ? item->path : "(null)");
2587 folder = item->folder;
2589 if (folder->ui_func)
2590 folder->ui_func(folder, item, folder->ui_func_data ?
2591 folder->ui_func_data : GINT_TO_POINTER(num));
2593 mlist = folder_item_get_msg_list(item);
2595 total = g_slist_length(mlist);
2597 for (cur = mlist; cur != NULL; cur = cur->next) {
2598 MsgInfo *msginfo = (MsgInfo *)cur->data;
2601 statusbar_progress_all(num++,total, 10);
2603 if (folder->ui_func)
2604 folder->ui_func(folder, item,
2605 folder->ui_func_data ?
2606 folder->ui_func_data :
2607 GINT_TO_POINTER(num));
2611 msg = folder_item_fetch_msg(item, msginfo->msgnum);
2613 g_warning("Can't fetch message %d. Aborting.\n",
2621 statusbar_progress_all(0,0,0);
2622 statusbar_pop_all();
2623 procmsg_msg_list_free(mlist);
2628 static gint folder_item_get_msg_num_by_file(FolderItem *dest, const gchar *file)
2630 static HeaderEntry hentry[] = {{"Message-ID:", NULL, TRUE},
2631 {NULL, NULL, FALSE}};
2635 gchar buf[BUFFSIZE];
2637 if ((fp = g_fopen(file, "rb")) == NULL)
2640 if ((folder_has_parent_of_type(dest, F_QUEUE)) ||
2641 (folder_has_parent_of_type(dest, F_DRAFT)))
2642 while (fgets(buf, sizeof(buf), fp) != NULL)
2643 if (buf[0] == '\r' || buf[0] == '\n') break;
2645 procheader_get_header_fields(fp, hentry);
2646 if (hentry[0].body) {
2647 extract_parenthesis(hentry[0].body, '<', '>');
2648 remove_space(hentry[0].body);
2649 if ((msginfo = msgcache_get_msg_by_id(dest->cache, hentry[0].body)) != NULL) {
2650 msgnum = msginfo->msgnum;
2651 procmsg_msginfo_free(msginfo);
2653 debug_print("found message as uid %d\n", msgnum);
2657 g_free(hentry[0].body);
2658 hentry[0].body = NULL;
2664 static void copy_msginfo_flags(MsgInfo *source, MsgInfo *dest)
2666 MsgPermFlags perm_flags = 0;
2667 MsgTmpFlags tmp_flags = 0;
2669 /* create new flags */
2670 if (source != NULL) {
2671 /* copy original flags */
2672 perm_flags = source->flags.perm_flags;
2673 tmp_flags = source->flags.tmp_flags;
2675 perm_flags = dest->flags.perm_flags;
2676 tmp_flags = dest->flags.tmp_flags;
2679 /* remove new, unread and deleted in special folders */
2680 if (folder_has_parent_of_type(dest->folder, F_OUTBOX) ||
2681 folder_has_parent_of_type(dest->folder, F_QUEUE) ||
2682 folder_has_parent_of_type(dest->folder, F_DRAFT) ||
2683 folder_has_parent_of_type(dest->folder, F_TRASH))
2684 perm_flags &= ~(MSG_NEW | MSG_UNREAD | MSG_DELETED);
2686 /* set ignore flag of ignored parent exists */
2687 if (procmsg_msg_has_flagged_parent(dest, MSG_IGNORE_THREAD))
2688 perm_flags |= MSG_IGNORE_THREAD;
2690 /* Unset tmp flags that should not be copied */
2691 tmp_flags &= ~(MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
2693 /* unset flags that are set but should not */
2694 /* and set new flags */
2695 procmsg_msginfo_change_flags(dest,
2696 ~dest->flags.perm_flags & perm_flags,
2697 ~dest->flags.tmp_flags & tmp_flags,
2698 dest->flags.perm_flags & ~perm_flags,
2699 dest->flags.tmp_flags & ~tmp_flags);
2702 static void add_msginfo_to_cache(FolderItem *item, MsgInfo *newmsginfo, MsgInfo *flagsource)
2704 /* update folder stats */
2705 if (MSG_IS_NEW(newmsginfo->flags))
2707 if (MSG_IS_UNREAD(newmsginfo->flags))
2708 item->unread_msgs++;
2709 if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
2710 item->unreadmarked_msgs++;
2711 if (MSG_IS_MARKED(newmsginfo->flags))
2712 item->marked_msgs++;
2715 folder_item_update_freeze();
2718 folder_item_read_cache(item);
2720 msgcache_add_msg(item->cache, newmsginfo);
2721 copy_msginfo_flags(flagsource, newmsginfo);
2722 folder_item_update_with_msg(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT | F_ITEM_UPDATE_ADDMSG, newmsginfo);
2723 folder_item_update_thaw();
2726 static void remove_msginfo_from_cache(FolderItem *item, MsgInfo *msginfo)
2728 MsgInfoUpdate msginfo_update;
2731 folder_item_read_cache(item);
2733 if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2734 msginfo->folder->new_msgs--;
2735 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2736 msginfo->folder->unread_msgs--;
2737 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
2738 msginfo->folder->unreadmarked_msgs--;
2739 if (MSG_IS_MARKED(msginfo->flags))
2740 item->marked_msgs--;
2742 msginfo->folder->total_msgs--;
2744 msginfo_update.msginfo = msginfo;
2745 msginfo_update.flags = MSGINFO_UPDATE_DELETED;
2746 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2748 msgcache_remove_msg(item->cache, msginfo->msgnum);
2749 folder_item_update_with_msg(msginfo->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT | F_ITEM_UPDATE_REMOVEMSG, msginfo);
2752 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
2753 MsgFlags *flags, gboolean remove_source)
2756 MsgFileInfo fileinfo;
2758 g_return_val_if_fail(dest != NULL, -1);
2759 g_return_val_if_fail(file != NULL, -1);
2761 fileinfo.msginfo = NULL;
2762 fileinfo.file = (gchar *)file;
2763 fileinfo.flags = flags;
2764 file_list.data = &fileinfo;
2765 file_list.next = NULL;
2767 return folder_item_add_msgs(dest, &file_list, remove_source);
2770 gint folder_item_add_msgs(FolderItem *dest, GSList *file_list,
2771 gboolean remove_source)
2774 gint ret, num, lastnum = -1;
2776 GRelation *relation;
2777 MsgFileInfo *fileinfo = NULL;
2778 gboolean folderscan = FALSE;
2780 g_return_val_if_fail(dest != NULL, -1);
2781 g_return_val_if_fail(file_list != NULL, -1);
2782 g_return_val_if_fail(dest->folder != NULL, -1);
2783 if (dest->no_select)
2786 folder = dest->folder;
2788 relation = g_relation_new(2);
2789 g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
2791 if (folder->klass->add_msgs != NULL) {
2792 ret = folder->klass->add_msgs(folder, dest, file_list, relation);
2794 g_relation_destroy(relation);
2798 for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
2799 fileinfo = (MsgFileInfo *) file_cur->data;
2801 ret = folder->klass->add_msg(folder, dest, fileinfo->file, fileinfo->flags);
2803 g_relation_destroy(relation);
2806 g_relation_insert(relation, fileinfo, GINT_TO_POINTER(ret));
2810 for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
2813 fileinfo = (MsgFileInfo *) file_cur->data;
2814 tuples = g_relation_select(relation, fileinfo, 0);
2815 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
2816 g_tuples_destroy(tuples);
2819 MsgInfo *newmsginfo;
2823 folder_item_scan_full(dest, FALSE);
2826 num = folder_item_get_msg_num_by_file(dest, fileinfo->file);
2832 if (num >= 0 && remove_source) {
2833 if (g_unlink(fileinfo->file) < 0)
2834 FILE_OP_ERROR(fileinfo->file, "unlink");
2841 ((newmsginfo = get_msginfo(dest, num)) != NULL)) {
2842 add_msginfo_to_cache(dest, newmsginfo, NULL);
2843 procmsg_msginfo_free(newmsginfo);
2844 } else if ((newmsginfo = msgcache_get_msg(dest->cache, num)) != NULL) {
2845 /* TODO: set default flags */
2846 procmsg_msginfo_free(newmsginfo);
2851 g_relation_destroy(relation);
2856 static FolderItem *folder_item_move_recursive(FolderItem *src, FolderItem *dest, gboolean copy)
2859 FolderItem *new_item;
2860 FolderItem *next_item;
2862 gchar *old_id, *new_id;
2865 debug_print("%s %s to %s\n", copy?"Copying":"Moving", src->path, dest->path);
2866 new_item = folder_create_folder(dest, src->name);
2867 if (new_item == NULL) {
2868 printf("Can't create folder\n");
2872 if (new_item->folder == NULL)
2873 new_item->folder = dest->folder;
2876 log_message(LOG_PROTOCOL, copy ?_("Copying %s to %s...\n"):_("Moving %s to %s...\n"),
2877 src->name, new_item->path);
2879 mlist = folder_item_get_msg_list(src);
2881 if (mlist != NULL) {
2883 folder_item_copy_msgs(new_item, mlist);
2885 folder_item_move_msgs(new_item, mlist);
2886 procmsg_msg_list_free(mlist);
2890 folder_item_prefs_copy_prefs(src, new_item);
2892 /* copy internal data */
2893 if (src->folder->klass == new_item->folder->klass &&
2894 src->folder->klass->copy_private_data != NULL)
2895 src->folder->klass->copy_private_data(src->folder,
2897 new_item->collapsed = src->collapsed;
2898 new_item->thread_collapsed = src->thread_collapsed;
2899 new_item->threaded = src->threaded;
2900 new_item->ret_rcpt = src->ret_rcpt;
2901 new_item->hide_read_msgs = src->hide_read_msgs;
2902 new_item->sort_key = src->sort_key;
2903 new_item->sort_type = src->sort_type;
2905 prefs_matcher_write_config();
2908 srcnode = src->folder->node;
2909 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, src);
2910 srcnode = srcnode->children;
2911 while (srcnode != NULL) {
2912 if (srcnode && srcnode->data) {
2913 next_item = (FolderItem*) srcnode->data;
2914 srcnode = srcnode->next;
2915 if (folder_item_move_recursive(next_item, new_item, copy) == NULL) {
2920 old_id = folder_item_get_identifier(src);
2921 new_id = folder_item_get_identifier(new_item);
2923 /* if src supports removing, otherwise only copy folder */
2924 if (src->folder->klass->remove_folder != NULL && !copy)
2925 src->folder->klass->remove_folder(src->folder, src);
2926 folder_write_list();
2929 debug_print("updating rules : %s => %s\n", old_id, new_id);
2930 if (old_id != NULL && new_id != NULL) {
2931 prefs_filtering_rename_path(old_id, new_id);
2932 account_rename_path(old_id, new_id);
2941 gint folder_item_move_to(FolderItem *src, FolderItem *dest, FolderItem **new_item, gboolean copy)
2943 FolderItem *tmp = folder_item_parent(dest);
2944 gchar * src_identifier, * dst_identifier;
2945 gchar * phys_srcpath, * phys_dstpath, *tmppath;
2949 return F_MOVE_FAILED_DEST_IS_CHILD;
2951 tmp = folder_item_parent(tmp);
2954 tmp = folder_item_parent(src);
2956 src_identifier = folder_item_get_identifier(src);
2957 dst_identifier = folder_item_get_identifier(dest);
2959 if(dst_identifier == NULL && dest->folder && folder_item_parent(dest) == NULL) {
2960 /* dest can be a root folder */
2961 dst_identifier = folder_get_identifier(dest->folder);
2963 if (src_identifier == NULL || dst_identifier == NULL) {
2964 debug_print("Can't get identifiers\n");
2965 return F_MOVE_FAILED;
2968 if (src->folder != dest->folder && !copy) {
2969 return F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX;
2972 phys_srcpath = folder_item_get_path(src);
2973 tmppath = folder_item_get_path(dest);
2974 phys_dstpath = g_strconcat(tmppath,
2976 g_path_get_basename(phys_srcpath),
2980 if (folder_item_parent(src) == dest || src == dest) {
2981 g_free(src_identifier);
2982 g_free(dst_identifier);
2983 g_free(phys_srcpath);
2984 g_free(phys_dstpath);
2985 return F_MOVE_FAILED_DEST_IS_PARENT;
2987 debug_print("moving \"%s\" to \"%s\"\n", phys_srcpath, phys_dstpath);
2988 if ((tmp = folder_item_move_recursive(src, dest, copy)) == NULL) {
2989 return F_MOVE_FAILED;
2992 g_free(src_identifier);
2993 g_free(dst_identifier);
2994 g_free(phys_srcpath);
2995 g_free(phys_dstpath);
3003 * Copy a list of message to a new folder and remove
3004 * source messages if wanted
3006 static gint do_copy_msgs(FolderItem *dest, GSList *msglist, gboolean remove_source)
3010 gint num, lastnum = -1;
3011 gboolean folderscan = FALSE;
3012 GRelation *relation;
3013 GSList *not_moved = NULL;
3014 gint total = 0, curmsg = 0;
3015 MsgInfo *msginfo = NULL;
3017 g_return_val_if_fail(dest != NULL, -1);
3018 g_return_val_if_fail(msglist != NULL, -1);
3020 folder = dest->folder;
3022 g_return_val_if_fail(folder->klass->copy_msg != NULL, -1);
3023 if (dest->no_select)
3026 msginfo = (MsgInfo *)msglist->data;
3031 if (!MSG_IS_QUEUED(msginfo->flags) &&
3032 MSG_IS_DRAFT(msginfo->flags) &&
3033 folder_has_parent_of_type(dest, F_QUEUE)) {
3034 GSList *cur = msglist;
3035 gboolean queue_err = FALSE;
3036 for (; cur; cur = cur->next) {
3037 Compose *compose = NULL;
3038 FolderItem *queue = dest;
3041 msginfo = (MsgInfo *)cur->data;
3042 compose = compose_reedit(msginfo, TRUE);
3043 if (compose == NULL) {
3047 val = compose_queue(compose, NULL, &queue, NULL,
3051 } else if (remove_source) {
3052 folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
3055 compose_close(compose);
3057 return queue_err ? -1:0;
3060 relation = g_relation_new(2);
3061 g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
3062 g_relation_index(relation, 1, g_direct_hash, g_direct_equal);
3064 for (l = msglist ; l != NULL ; l = g_slist_next(l)) {
3065 MsgInfo * msginfo = (MsgInfo *) l->data;
3067 if (msginfo->planned_download != 0) {
3068 int old_planned = msginfo->planned_download;
3069 partial_unmark(msginfo);
3070 /* little hack to reenable after */
3071 msginfo->planned_download = old_planned;
3076 * Copy messages to destination folder and
3077 * store new message numbers in newmsgnums
3079 if (folder->klass->copy_msgs != NULL) {
3080 if (folder->klass->copy_msgs(folder, dest, msglist, relation) < 0) {
3081 g_relation_destroy(relation);
3088 /* immediately stop if src and dest folders are identical */
3090 msginfo = (MsgInfo *) l->data;
3091 if (msginfo != NULL && msginfo->folder == dest) {
3092 g_relation_destroy(relation);
3097 for (; l != NULL ; l = g_slist_next(l)) {
3098 msginfo = (MsgInfo *) l->data;
3100 num = folder->klass->copy_msg(folder, dest, msginfo);
3102 g_relation_insert(relation, msginfo, GINT_TO_POINTER(num));
3104 not_moved = g_slist_prepend(not_moved, msginfo);
3108 if (remove_source) {
3109 MsgInfo *msginfo = (MsgInfo *) msglist->data;
3110 FolderItem *item = msginfo->folder;
3112 * Remove source messages from their folders if
3113 * copying was successfull and update folder
3116 if (not_moved == NULL && item->folder->klass->remove_msgs) {
3117 item->folder->klass->remove_msgs(item->folder,
3122 for (l = msglist; l != NULL; l = g_slist_next(l)) {
3124 msginfo = (MsgInfo *) l->data;
3125 item = msginfo->folder;
3127 tuples = g_relation_select(relation, msginfo, 0);
3130 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
3133 g_tuples_destroy(tuples);
3138 if (g_slist_find(not_moved, msginfo))
3141 if ((num >= 0) && (item->folder->klass->remove_msg != NULL)) {
3142 if (!item->folder->klass->remove_msgs)
3143 item->folder->klass->remove_msg(item->folder,
3146 remove_msginfo_from_cache(item, msginfo);
3151 /* Read cache for dest folder */
3152 if (!dest->cache) folder_item_read_cache(dest);
3155 * Fetch new MsgInfos for new messages in dest folder,
3156 * add them to the msgcache and update folder message counts
3158 if (g_relation_count(relation, GINT_TO_POINTER(0), 1) > 0) {
3159 folder_item_scan_full(dest, FALSE);
3163 statusbar_print_all(_("Updating cache for %s..."), dest->path ? dest->path : "(null)");
3164 total = g_slist_length(msglist);
3165 for (l = msglist; l != NULL; l = g_slist_next(l)) {
3166 MsgInfo *msginfo = (MsgInfo *) l->data;
3169 tuples = g_relation_select(relation, msginfo, 0);
3170 if (tuples->len > 0) {
3171 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
3172 g_tuples_destroy(tuples);
3176 g_tuples_destroy(tuples);
3178 statusbar_progress_all(curmsg++,total, 100);
3179 if (curmsg % 100 == 0)
3183 MsgInfo *newmsginfo = NULL;
3185 if (!folderscan && num > 0) {
3186 newmsginfo = get_msginfo(dest, num);
3187 if (newmsginfo != NULL) {
3188 add_msginfo_to_cache(dest, newmsginfo, msginfo);
3191 if (newmsginfo == NULL) {
3193 folder_item_scan_full(dest, FALSE);
3196 if (msginfo->msgid != NULL) {
3197 newmsginfo = folder_item_get_msginfo_by_msgid(dest, msginfo->msgid);
3198 if (newmsginfo != NULL) {
3199 copy_msginfo_flags(msginfo, newmsginfo);
3200 num = newmsginfo->msgnum;
3205 if (msginfo->planned_download
3206 == POP3_PARTIAL_DLOAD_DELE) {
3207 partial_mark_for_delete(newmsginfo);
3209 if (msginfo->planned_download
3210 == POP3_PARTIAL_DLOAD_DLOAD) {
3211 partial_mark_for_download(newmsginfo);
3213 if (!MSG_IS_POSTFILTERED (msginfo->flags)) {
3214 procmsg_msginfo_set_flags ( msginfo, MSG_POSTFILTERED, 0);
3215 procmsg_msginfo_set_flags (newmsginfo, MSG_POSTFILTERED, 0);
3216 hooks_invoke (MAIL_POSTFILTERING_HOOKLIST, newmsginfo);
3218 procmsg_msginfo_free(newmsginfo);
3225 statusbar_progress_all(0,0,0);
3226 statusbar_pop_all();
3228 g_relation_destroy(relation);
3229 if (not_moved != NULL) {
3230 g_slist_free(not_moved);
3237 * Move a message to a new folder.
3239 * \param dest Destination folder
3240 * \param msginfo The message
3242 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
3246 g_return_val_if_fail(dest != NULL, -1);
3247 g_return_val_if_fail(msginfo != NULL, -1);
3249 list.data = msginfo;
3252 return do_copy_msgs(dest, &list, TRUE);
3256 * Move a list of messages to a new folder.
3258 * \param dest Destination folder
3259 * \param msglist List of messages
3261 gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
3264 g_return_val_if_fail(dest != NULL, -1);
3265 g_return_val_if_fail(msglist != NULL, -1);
3267 result = do_copy_msgs(dest, msglist, TRUE);
3273 * Copy a message to a new folder.
3275 * \param dest Destination folder
3276 * \param msginfo The message
3278 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
3282 g_return_val_if_fail(dest != NULL, -1);
3283 g_return_val_if_fail(msginfo != NULL, -1);
3285 list.data = msginfo;
3288 return do_copy_msgs(dest, &list, FALSE);
3292 * Copy a list of messages to a new folder.
3294 * \param dest Destination folder
3295 * \param msglist List of messages
3297 gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
3300 g_return_val_if_fail(dest != NULL, -1);
3301 g_return_val_if_fail(msglist != NULL, -1);
3304 result = do_copy_msgs(dest, msglist, FALSE);
3310 gint folder_item_remove_msg(FolderItem *item, gint num)
3316 g_return_val_if_fail(item != NULL, -1);
3317 folder = item->folder;
3318 g_return_val_if_fail(folder->klass->remove_msg != NULL, -1);
3319 if (item->no_select)
3322 if (!item->cache) folder_item_read_cache(item);
3324 ret = folder->klass->remove_msg(folder, item, num);
3326 msginfo = msgcache_get_msg(item->cache, num);
3327 if (msginfo != NULL) {
3328 remove_msginfo_from_cache(item, msginfo);
3329 procmsg_msginfo_free(msginfo);
3335 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
3340 g_return_val_if_fail(item != NULL, -1);
3341 folder = item->folder;
3342 g_return_val_if_fail(folder != NULL, -1);
3343 if (item->no_select)
3346 if (!item->cache) folder_item_read_cache(item);
3348 folder_item_update_freeze();
3349 if (item->folder->klass->remove_msgs) {
3350 ret = item->folder->klass->remove_msgs(item->folder,
3355 while (ret == 0 && msglist != NULL) {
3356 MsgInfo *msginfo = (MsgInfo *)msglist->data;
3357 if (MSG_IS_LOCKED(msginfo->flags)) {
3358 msglist = msglist->next;
3361 if (!item->folder->klass->remove_msgs)
3362 ret = folder_item_remove_msg(item, msginfo->msgnum);
3363 if (ret != 0) break;
3364 msgcache_remove_msg(item->cache, msginfo->msgnum);
3365 msglist = msglist->next;
3367 folder_item_scan_full(item, FALSE);
3368 folder_item_update_thaw();
3373 gint folder_item_remove_all_msg(FolderItem *item)
3378 g_return_val_if_fail(item != NULL, -1);
3379 if (item->no_select)
3382 folder = item->folder;
3385 if (folder->klass->remove_all_msg != NULL) {
3386 result = folder->klass->remove_all_msg(folder, item);
3389 folder_item_free_cache(item, TRUE);
3390 item->cache = msgcache_new();
3393 MsgInfoList *msglist;
3395 msglist = folder_item_get_msg_list(item);
3396 result = folder_item_remove_msgs(item, msglist);
3397 procmsg_msg_list_free(msglist);
3402 item->unread_msgs = 0;
3403 item->unreadmarked_msgs = 0;
3404 item->marked_msgs = 0;
3405 item->total_msgs = 0;
3406 folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
3413 void folder_item_change_msg_flags(FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3415 g_return_if_fail(item != NULL);
3416 g_return_if_fail(msginfo != NULL);
3417 if (item->no_select)
3420 if (item->folder->klass->change_flags != NULL) {
3421 item->folder->klass->change_flags(item->folder, item, msginfo, newflags);
3423 msginfo->flags.perm_flags = newflags;
3427 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
3431 g_return_val_if_fail(item != NULL, FALSE);
3432 if (item->no_select)
3435 folder = item->folder;
3437 g_return_val_if_fail(folder->klass->is_msg_changed != NULL, -1);
3439 return folder->klass->is_msg_changed(folder, item, msginfo);
3442 static gchar *folder_item_get_cache_file(FolderItem *item)
3448 g_return_val_if_fail(item != NULL, NULL);
3449 g_return_val_if_fail(item->path != NULL, NULL);
3451 path = folder_item_get_path(item);
3452 g_return_val_if_fail(path != NULL, NULL);
3453 if (!is_dir_exist(path))
3454 make_dir_hier(path);
3455 file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
3456 old_file = g_strconcat(path, G_DIR_SEPARATOR_S, OLD_CACHE_FILE, NULL);
3458 if (!is_file_exist(file) && is_file_exist(old_file))
3459 move_file(old_file, file, FALSE);
3466 static gchar *folder_item_get_mark_file(FolderItem *item)
3472 g_return_val_if_fail(item != NULL, NULL);
3473 g_return_val_if_fail(item->path != NULL, NULL);
3475 path = folder_item_get_path(item);
3476 g_return_val_if_fail(path != NULL, NULL);
3477 if (!is_dir_exist(path))
3478 make_dir_hier(path);
3479 file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
3480 old_file = g_strconcat(path, G_DIR_SEPARATOR_S, OLD_MARK_FILE, NULL);
3482 if (!is_file_exist(file) && is_file_exist(old_file))
3483 move_file(old_file, file, FALSE);
3490 static gpointer xml_to_folder_item(gpointer nodedata, gpointer data)
3492 XMLNode *xmlnode = (XMLNode *) nodedata;
3493 Folder *folder = (Folder *) data;
3496 g_return_val_if_fail(xmlnode != NULL, NULL);
3497 g_return_val_if_fail(folder != NULL, NULL);
3499 if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
3500 g_warning("tag name != \"folderitem\"\n");
3504 item = folder_item_new(folder, "", "");
3505 if (folder->klass->item_set_xml != NULL)
3506 folder->klass->item_set_xml(folder, item, xmlnode->tag);
3508 folder_item_set_xml(folder, item, xmlnode->tag);
3510 item->folder = folder;
3512 switch (item->stype) {
3513 case F_INBOX: folder->inbox = item; break;
3514 case F_OUTBOX: folder->outbox = item; break;
3515 case F_DRAFT: folder->draft = item; break;
3516 case F_QUEUE: folder->queue = item; break;
3517 case F_TRASH: folder->trash = item; break;
3520 folder_item_prefs_read_config(item);
3525 static gboolean folder_item_set_node(GNode *node, gpointer data)
3527 FolderItem *item = (FolderItem *) node->data;
3533 static Folder *folder_get_from_xml(GNode *node)
3538 FolderClass *klass = NULL;
3541 g_return_val_if_fail(node->data != NULL, NULL);
3543 xmlnode = node->data;
3544 if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
3545 g_warning("tag name != \"folder\"\n");
3548 list = xmlnode->tag->attr;
3549 for (; list != NULL; list = list->next) {
3550 XMLAttr *attr = list->data;
3552 if (!attr || !attr->name || !attr->value) continue;
3553 if (!strcmp(attr->name, "type"))
3554 klass = folder_get_class_from_string(attr->value);
3559 folder = folder_new(klass, "", "");
3560 g_return_val_if_fail(folder != NULL, NULL);
3563 klass->set_xml(folder, xmlnode->tag);
3565 folder_set_xml(folder, xmlnode->tag);
3567 cur = node->children;
3568 while (cur != NULL) {
3571 itemnode = g_node_map(cur, xml_to_folder_item, (gpointer) folder);
3572 g_node_append(folder->node, itemnode);
3575 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, folder_item_set_node, NULL);
3580 static gchar *folder_get_list_path(void)
3582 static gchar *filename = NULL;
3585 filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
3591 #define PUT_ESCAPE_STR(fp, attr, str) \
3593 fputs(" " attr "=\"", fp); \
3594 xml_file_put_escape_str(fp, str); \
3598 static gpointer folder_item_to_xml(gpointer nodedata, gpointer data)
3600 FolderItem *item = (FolderItem *) nodedata;
3603 g_return_val_if_fail(item != NULL, NULL);
3605 if (item->folder->klass->item_get_xml != NULL)
3606 tag = item->folder->klass->item_get_xml(item->folder, item);
3608 tag = folder_item_get_xml(item->folder, item);
3610 return xml_node_new(tag, NULL);
3613 static GNode *folder_get_xml_node(Folder *folder)
3619 g_return_val_if_fail(folder != NULL, NULL);
3621 if (folder->klass->get_xml != NULL)
3622 tag = folder->klass->get_xml(folder);
3624 tag = folder_get_xml(folder);
3626 xml_tag_add_attr(tag, xml_attr_new("type", folder->klass->idstr));
3628 xmlnode = xml_node_new(tag, NULL);
3630 node = g_node_new(xmlnode);
3632 g_return_val_if_fail (folder->node != NULL, NULL);
3634 if (folder->node->children) {
3637 cur = folder->node->children;
3641 xmlnode = g_node_map(cur, folder_item_to_xml, (gpointer) folder);
3642 g_node_append(node, xmlnode);
3650 static void folder_update_op_count_rec(GNode *node)
3652 FolderItem *fitem = FOLDER_ITEM(node->data);
3654 if (g_node_depth(node) > 0) {
3655 if (fitem->op_count > 0) {
3656 fitem->op_count = 0;
3657 folder_item_update(fitem, F_ITEM_UPDATE_MSGCNT);
3659 if (node->children) {
3662 child = node->children;
3668 folder_update_op_count_rec(cur);
3674 void folder_update_op_count(void)
3679 for (cur = folder_list; cur != NULL; cur = cur->next) {
3681 folder_update_op_count_rec(folder->node);
3685 typedef struct _type_str {
3692 static gchar * folder_item_get_tree_identifier(FolderItem * item)
3694 if (item->parent != NULL) {
3698 path = folder_item_get_tree_identifier(item->parent);
3702 id = g_strconcat(path, "/", item->name, NULL);
3708 return g_strconcat("/", item->name, NULL);
3713 /* CLAWS: temporary local folder for filtering */
3714 #define TEMP_FOLDER "TEMP_FOLDER"
3715 #define PROCESSING_FOLDER_ITEM "processing"
3717 static FolderItem *processing_folder_item;
3719 static void folder_create_processing_folder(void)
3721 Folder *processing_folder;
3724 if ((processing_folder = folder_find_from_name(TEMP_FOLDER, mh_get_class())) == NULL) {
3728 g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
3729 "tempfolder", NULL);
3731 folder_new(mh_get_class(), TEMP_FOLDER, tmppath);
3734 g_assert(processing_folder != NULL);
3736 debug_print("tmpparentroot %s\n", LOCAL_FOLDER(processing_folder)->rootpath);
3737 /* FIXME: [W32] The code below does not correctly merge
3738 relative filenames; there should be a function to handle
3740 if (!is_relative_filename(LOCAL_FOLDER(processing_folder)->rootpath))
3741 tmpname = g_strconcat(LOCAL_FOLDER(processing_folder)->rootpath,
3742 G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
3745 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
3746 LOCAL_FOLDER(processing_folder)->rootpath,
3747 G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
3750 if (!is_dir_exist(tmpname)) {
3751 debug_print("*TMP* creating %s\n", tmpname);
3752 processing_folder_item = processing_folder->klass->create_folder(processing_folder,
3753 processing_folder->node->data,
3754 PROCESSING_FOLDER_ITEM);
3756 debug_print("*TMP* already created\n");
3757 processing_folder_item = folder_item_new(processing_folder, PROCESSING_FOLDER_ITEM, PROCESSING_FOLDER_ITEM);
3758 g_assert(processing_folder_item);
3759 folder_item_append(processing_folder->node->data, processing_folder_item);
3761 g_assert(processing_folder_item != NULL);
3765 FolderItem *folder_get_default_processing(void)
3767 if (!processing_folder_item) {
3768 folder_create_processing_folder();
3770 return processing_folder_item;
3773 /* folder_persist_prefs_new() - return hash table with persistent
3774 * settings (and folder name as key).
3775 * (note that in claws other options are in the folder_item_prefs_RC
3776 * file, so those don't need to be included in PersistPref yet)
3778 static GHashTable *folder_persist_prefs_new(Folder *folder)
3780 GHashTable *pptable;
3782 g_return_val_if_fail(folder, NULL);
3783 pptable = g_hash_table_new(g_str_hash, g_str_equal);
3784 folder_get_persist_prefs_recursive(folder->node, pptable);
3788 static void folder_persist_prefs_free(GHashTable *pptable)
3790 g_return_if_fail(pptable);
3791 g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
3792 g_hash_table_destroy(pptable);
3795 static const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
3797 if (pptable == NULL || name == NULL) return NULL;
3798 return g_hash_table_lookup(pptable, name);
3801 static void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
3803 const PersistPrefs *pp;
3804 gchar *id = folder_item_get_identifier(item);
3806 pp = folder_get_persist_prefs(pptable, id);
3811 /* CLAWS: since not all folder properties have been migrated to
3812 * folderlist.xml, we need to call the old stuff first before
3813 * setting things that apply both to Main and Claws. */
3814 folder_item_prefs_read_config(item);
3816 item->collapsed = pp->collapsed;
3817 item->thread_collapsed = pp->thread_collapsed;
3818 item->threaded = pp->threaded;
3819 item->ret_rcpt = pp->ret_rcpt;
3820 item->hide_read_msgs = pp->hide_read_msgs;
3821 item->sort_key = pp->sort_key;
3822 item->sort_type = pp->sort_type;
3825 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
3827 FolderItem *item = FOLDER_ITEM(node->data);
3832 g_return_if_fail(node != NULL);
3833 g_return_if_fail(item != NULL);
3835 /* NOTE: item->path == NULL means top level folder; not interesting
3836 * to store preferences of that one. */
3838 id = folder_item_get_identifier(item);
3839 pp = g_new0(PersistPrefs, 1);
3840 g_return_if_fail(pp != NULL);
3841 pp->collapsed = item->collapsed;
3842 pp->thread_collapsed = item->thread_collapsed;
3843 pp->threaded = item->threaded;
3844 pp->ret_rcpt = item->ret_rcpt;
3845 pp->hide_read_msgs = item->hide_read_msgs;
3846 pp->sort_key = item->sort_key;
3847 pp->sort_type = item->sort_type;
3848 g_hash_table_insert(pptable, id, pp);
3851 if (node->children) {
3852 child = node->children;
3856 folder_get_persist_prefs_recursive(cur, pptable);
3861 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
3868 void folder_item_apply_processing(FolderItem *item)
3870 GSList *processing_list;
3871 GSList *mlist, *cur;
3872 guint total = 0, curmsg = 0;
3873 gint last_apply_per_account;
3875 g_return_if_fail(item != NULL);
3877 if (item->no_select)
3880 processing_list = item->prefs->processing;
3882 if (!pre_global_processing && !processing_list
3883 && !post_global_processing)
3886 debug_print("processing %s\n", item->name);
3887 folder_item_update_freeze();
3889 mlist = folder_item_get_msg_list(item);
3890 total = g_slist_length(mlist);
3891 statusbar_print_all(_("Processing messages..."));
3893 last_apply_per_account = prefs_common.apply_per_account_filtering_rules;
3894 prefs_common.apply_per_account_filtering_rules = FILTERING_ACCOUNT_RULES_SKIP;
3896 for (cur = mlist ; cur != NULL ; cur = cur->next) {
3899 msginfo = (MsgInfo *) cur->data;
3901 /* reset parameters that can be modified by processing */
3902 msginfo->hidden = 0;
3905 statusbar_progress_all(curmsg++,total, 10);
3907 /* apply pre global rules */
3908 filter_message_by_msginfo(pre_global_processing, msginfo, NULL,
3909 FILTERING_PRE_PROCESSING, NULL);
3911 /* apply rules of the folder */
3912 filter_message_by_msginfo(processing_list, msginfo, NULL,
3913 FILTERING_FOLDER_PROCESSING, item->name);
3915 /* apply post global rules */
3916 filter_message_by_msginfo(post_global_processing, msginfo, NULL,
3917 FILTERING_POST_PROCESSING, NULL);
3919 prefs_common.apply_per_account_filtering_rules = last_apply_per_account;
3921 if (pre_global_processing || processing_list
3922 || post_global_processing)
3923 filtering_move_and_copy_msgs(mlist);
3924 for (cur = mlist ; cur != NULL ; cur = cur->next) {
3925 MsgInfo * msginfo = (MsgInfo *)cur->data;
3926 procmsg_msginfo_free(msginfo);
3928 g_slist_free(mlist);
3930 statusbar_progress_all(0,0,0);
3931 statusbar_pop_all();
3933 folder_item_update_thaw();
3937 * functions for handling FolderItem content changes
3939 static gint folder_item_update_freeze_cnt = 0;
3941 static void folder_item_update_with_msg(FolderItem *item, FolderItemUpdateFlags update_flags, MsgInfo *msg)
3943 if (folder_item_update_freeze_cnt == 0 /* || (msg != NULL && item->opened) */) {
3944 FolderItemUpdateData source;
3947 source.update_flags = update_flags;
3949 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
3951 item->update_flags |= update_flags & ~(F_ITEM_UPDATE_ADDMSG | F_ITEM_UPDATE_REMOVEMSG);
3956 * Notify the folder system about changes to a folder. If the
3957 * update system is not frozen the FOLDER_ITEM_UPDATE_HOOKLIST will
3958 * be invoked, otherwise the changes will be remebered until
3959 * the folder system is thawed.
3961 * \param item The FolderItem that was changed
3962 * \param update_flags Type of changed that was made
3964 void folder_item_update(FolderItem *item, FolderItemUpdateFlags update_flags)
3966 folder_item_update_with_msg(item, update_flags, NULL);
3969 void folder_item_update_recursive(FolderItem *item, FolderItemUpdateFlags update_flags)
3971 GNode *node = item->folder->node;
3973 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
3974 node = node->children;
3976 folder_item_update(item, update_flags);
3977 while (node != NULL) {
3978 if (node && node->data) {
3979 FolderItem *next_item = (FolderItem*) node->data;
3981 folder_item_update(next_item, update_flags);
3987 void folder_item_update_freeze(void)
3989 folder_item_update_freeze_cnt++;
3992 static void folder_item_update_func(FolderItem *item, gpointer data)
3994 FolderItemUpdateData source;
3996 if (item->update_flags) {
3998 source.update_flags = item->update_flags;
4000 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
4001 item->update_flags = 0;
4005 void folder_item_update_thaw(void)
4007 if (folder_item_update_freeze_cnt > 0)
4008 folder_item_update_freeze_cnt--;
4009 if (folder_item_update_freeze_cnt == 0) {
4010 /* Update all folders */
4011 folder_func_to_all_folders(folder_item_update_func, NULL);
4015 void folder_item_synchronise(FolderItem *item)
4019 if (item->prefs->offlinesync && item->folder->klass->synchronise) {
4020 statusbar_print_all(_("Synchronising %s for offline use...\n"), item->path ? item->path : "(null)");
4021 item->folder->klass->synchronise(item);
4022 statusbar_pop_all();
4026 static void folder_item_synchronise_func(FolderItem *item, gpointer data)
4028 Folder *folder = (Folder *)data;
4029 if (folder == NULL || item->folder == folder) {
4030 folder_item_synchronise(item);
4034 void folder_synchronise(Folder *folder)
4036 folder_func_to_all_folders(folder_item_synchronise_func, folder);
4039 typedef struct _WantSyncData {
4044 static void folder_item_want_synchronise_func(FolderItem *item, gpointer data)
4046 WantSyncData *want_sync_data = (WantSyncData *)data;
4048 if (want_sync_data->folder == NULL || item->folder == want_sync_data->folder) {
4049 if (item->prefs->offlinesync && item->folder->klass->synchronise)
4050 want_sync_data->want_sync |= TRUE;
4054 gboolean folder_want_synchronise(Folder *folder)
4056 WantSyncData *want_sync_data = g_new0(WantSyncData, 1);
4058 want_sync_data->folder = folder;
4059 want_sync_data->want_sync = FALSE;
4061 folder_func_to_all_folders(folder_item_want_synchronise_func, want_sync_data);
4062 result = want_sync_data->want_sync;
4063 g_free(want_sync_data);
4065 debug_print("Folder %s wants sync\n", folder->name);
4069 void folder_item_set_batch (FolderItem *item, gboolean batch)
4071 if (item->folder->klass->set_batch) {
4072 item->folder->klass->set_batch(item->folder, item, batch);
4076 gboolean folder_has_parent_of_type(FolderItem *item,
4077 SpecialFolderItemType type)
4079 FolderItem *cur = item;
4081 /* if we already know it, make it short */
4082 if (item->parent_stype != -1) {
4083 return (item->parent_stype == type);
4086 /* if we don't, find the type from the first possible parent,
4087 * and set our parent type to be faster next time */
4089 if (cur->stype == type || cur->parent_stype == type) {
4090 item->parent_stype = type;
4093 cur = folder_item_parent(cur);
4096 /* if we didn't match what was asked, we didn't return. If our
4097 * parent type is unknown, we may as well find it now to be faster
4099 if (item->parent_stype == -1) {
4102 /* here's an exception: Inbox subfolders are normal. */
4103 if (item->parent_stype == -1 && cur->stype == F_INBOX
4105 item->parent_stype = F_NORMAL;
4108 /* ah, we know this parent's parent's type, we may as
4109 * well copy it instead of going up the full way */
4110 if (cur->parent_stype != -1) {
4111 item->parent_stype = cur->parent_stype;
4114 /* we found a parent that has a special type. That's
4115 * our parent type. */
4116 if (cur->stype != F_NORMAL) {
4117 cur->parent_stype = cur->stype;
4118 item->parent_stype = cur->stype;
4121 /* if we didn't find anything, go up once more */
4122 cur = folder_item_parent(cur);
4124 /* as we still didn't find anything, our parents must all be
4126 if (item->parent_stype == -1) {
4127 item->parent_stype = F_NORMAL;
4133 gboolean folder_subscribe (const gchar *uri)
4136 for (cur = folder_get_list(); cur != NULL; cur = g_list_next(cur)) {
4137 Folder *folder = (Folder *) cur->data;
4139 if (folder->klass->subscribe
4140 && folder->klass->subscribe(folder, uri)) {
4148 gboolean folder_get_sort_type (Folder *folder,
4149 FolderSortKey *sort_key,
4150 FolderSortType *sort_type)
4152 if (!folder || !sort_key || !sort_type)
4154 if (folder->klass->get_sort_type == NULL)
4156 folder->klass->get_sort_type(folder, sort_key, sort_type);
4160 #undef PUT_ESCAPE_STR