2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2003 Hiroyuki Yamamoto
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #include <sys/types.h>
43 #include "prefs_gtk.h"
45 #include "filtering.h"
47 #include "procheader.h"
50 #include "folder_item_prefs.h"
51 #include "remotefolder.h"
53 /* Dependecies to be removed ?! */
54 #include "prefs_common.h"
55 #include "prefs_account.h"
57 static GList *folder_list = NULL;
59 void folder_init (Folder *folder,
62 static gboolean folder_read_folder_func (GNode *node,
64 static gchar *folder_get_list_path (void);
65 static void folder_write_list_recursive (GNode *node,
67 static void folder_update_op_count_rec (GNode *node);
70 static void folder_get_persist_prefs_recursive
71 (GNode *node, GHashTable *pptable);
72 static gboolean persist_prefs_free (gpointer key, gpointer val, gpointer data);
73 void folder_item_read_cache (FolderItem *item);
74 void folder_item_free_cache (FolderItem *item);
75 gint folder_item_scan_full (FolderItem *item, gboolean filtering);
77 static GSList *classlist;
79 void folder_system_init(void)
81 folder_register_class(mh_get_class());
82 folder_register_class(imap_get_class());
83 folder_register_class(news_get_class());
86 GSList *folder_get_class_list(void)
91 void folder_register_class(FolderClass *klass)
93 debug_print("registering folder class %s\n", klass->idstr);
94 classlist = g_slist_append(classlist, klass);
97 Folder *folder_new(FolderClass *klass, const gchar *name, const gchar *path)
99 Folder *folder = NULL;
102 g_return_val_if_fail(klass != NULL, NULL);
104 name = name ? name : path;
105 folder = klass->new_folder(name, path);
107 /* Create root folder item */
108 item = folder_item_new(folder, name, NULL);
109 item->folder = folder;
110 folder->node = item->node = g_node_new(item);
116 void folder_init(Folder *folder, const gchar *name)
118 g_return_if_fail(folder != NULL);
120 folder_set_name(folder, name);
122 /* Init folder data */
123 folder->account = NULL;
124 folder->inbox = NULL;
125 folder->outbox = NULL;
126 folder->draft = NULL;
127 folder->queue = NULL;
128 folder->trash = NULL;
131 void folder_destroy(Folder *folder)
133 g_return_if_fail(folder != NULL);
134 g_return_if_fail(folder->klass->destroy_folder != NULL);
136 folder_list = g_list_remove(folder_list, folder);
138 folder_tree_destroy(folder);
140 folder->klass->destroy_folder(folder);
142 g_free(folder->name);
146 void folder_set_xml(Folder *folder, XMLTag *tag)
150 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
151 XMLAttr *attr = (XMLAttr *) cur->data;
153 if (!attr || !attr->name || !attr->value) continue;
154 if (!strcmp(attr->name, "name")) {
155 if (folder->name != NULL)
156 g_free(folder->name);
157 folder->name = g_strdup(attr->value);
158 } else if (!strcmp(attr->name, "account_id")) {
159 PrefsAccount *account;
161 account = account_find_from_id(atoi(attr->value));
163 g_warning("account_id: %s not found\n", attr->value);
165 folder->account = account;
166 account->folder = folder;
172 XMLTag *folder_get_xml(Folder *folder)
176 tag = g_new0(XMLTag, 1);
177 tag->tag = g_strdup("folder");
180 xml_tag_add_attr(tag, "name", g_strdup(folder->name));
182 xml_tag_add_attr(tag, "account_id", g_strdup_printf("%d", folder->account->account_id));
187 FolderItem *folder_item_new(Folder *folder, const gchar *name, const gchar *path)
189 FolderItem *item = NULL;
191 if (folder->klass->item_new) {
192 item = folder->klass->item_new(folder);
194 item = g_new0(FolderItem, 1);
197 g_return_val_if_fail(item != NULL, NULL);
199 item->stype = F_NORMAL;
200 item->name = g_strdup(name);
201 item->path = g_strdup(path);
204 item->unread_msgs = 0;
205 item->unreadmarked_msgs = 0;
206 item->total_msgs = 0;
209 item->no_sub = FALSE;
210 item->no_select = FALSE;
211 item->collapsed = FALSE;
212 item->thread_collapsed = FALSE;
213 item->threaded = TRUE;
214 item->ret_rcpt = FALSE;
215 item->opened = FALSE;
219 item->account = NULL;
220 item->apply_sub = FALSE;
221 item->mark_queue = NULL;
224 item->prefs = folder_item_prefs_new();
229 void folder_item_append(FolderItem *parent, FolderItem *item)
231 g_return_if_fail(parent != NULL);
232 g_return_if_fail(parent->folder != NULL);
233 g_return_if_fail(parent->node != NULL);
234 g_return_if_fail(item != NULL);
236 item->parent = parent;
237 item->folder = parent->folder;
238 item->node = g_node_append_data(parent->node, item);
241 static gboolean folder_item_remove_func(GNode *node, gpointer data)
243 FolderItem *item = FOLDER_ITEM(node->data);
244 FolderUpdateData hookdata;
246 if (item->cache != NULL) {
247 msgcache_destroy(item->cache);
251 hookdata.folder = item->folder;
252 hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_REMOVE_FOLDERITEM;
253 hookdata.item = item;
254 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
256 folder_item_destroy(item);
261 void folder_item_remove(FolderItem *item)
265 g_return_if_fail(item != NULL);
266 g_return_if_fail(item->folder != NULL);
267 g_return_if_fail(item->node != NULL);
271 if (item->folder->node == node)
272 item->folder->node = NULL;
274 g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
275 folder_item_remove_func, NULL);
276 g_node_destroy(node);
279 void folder_item_remove_children(FolderItem *item)
283 g_return_if_fail(item != NULL);
284 g_return_if_fail(item->folder != NULL);
285 g_return_if_fail(item->node != NULL);
287 node = item->node->children;
288 while (node != NULL) {
290 folder_item_remove(FOLDER_ITEM(node->data));
295 void folder_item_destroy(FolderItem *item)
299 g_return_if_fail(item != NULL);
301 folder = item->folder;
303 if (folder->inbox == item)
304 folder->inbox = NULL;
305 else if (folder->outbox == item)
306 folder->outbox = NULL;
307 else if (folder->draft == item)
308 folder->draft = NULL;
309 else if (folder->queue == item)
310 folder->queue = NULL;
311 else if (folder->trash == item)
312 folder->trash = NULL;
316 folder_item_free_cache(item);
318 folder_item_prefs_free(item->prefs);
322 if (item->folder != NULL) {
323 if(item->folder->klass->item_destroy) {
324 item->folder->klass->item_destroy(item->folder, item);
331 void folder_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
335 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
336 XMLAttr *attr = (XMLAttr *) cur->data;
338 if (!attr || !attr->name || !attr->value) continue;
339 if (!strcmp(attr->name, "type")) {
340 if (!strcasecmp(attr->value, "normal"))
341 item->stype = F_NORMAL;
342 else if (!strcasecmp(attr->value, "inbox"))
343 item->stype = F_INBOX;
344 else if (!strcasecmp(attr->value, "outbox"))
345 item->stype = F_OUTBOX;
346 else if (!strcasecmp(attr->value, "draft"))
347 item->stype = F_DRAFT;
348 else if (!strcasecmp(attr->value, "queue"))
349 item->stype = F_QUEUE;
350 else if (!strcasecmp(attr->value, "trash"))
351 item->stype = F_TRASH;
352 } else if (!strcmp(attr->name, "name")) {
353 if (item->name != NULL)
355 item->name = g_strdup(attr->value);
356 } else if (!strcmp(attr->name, "path")) {
357 if (item->path != NULL)
359 item->path = g_strdup(attr->value);
360 } else if (!strcmp(attr->name, "mtime"))
361 item->mtime = strtoul(attr->value, NULL, 10);
362 else if (!strcmp(attr->name, "new"))
363 item->new_msgs = atoi(attr->value);
364 else if (!strcmp(attr->name, "unread"))
365 item->unread_msgs = atoi(attr->value);
366 else if (!strcmp(attr->name, "unreadmarked"))
367 item->unreadmarked_msgs = atoi(attr->value);
368 else if (!strcmp(attr->name, "total"))
369 item->total_msgs = atoi(attr->value);
370 else if (!strcmp(attr->name, "no_sub"))
371 item->no_sub = *attr->value == '1' ? TRUE : FALSE;
372 else if (!strcmp(attr->name, "no_select"))
373 item->no_select = *attr->value == '1' ? TRUE : FALSE;
374 else if (!strcmp(attr->name, "collapsed"))
375 item->collapsed = *attr->value == '1' ? TRUE : FALSE;
376 else if (!strcmp(attr->name, "thread_collapsed"))
377 item->thread_collapsed = *attr->value == '1' ? TRUE : FALSE;
378 else if (!strcmp(attr->name, "threaded"))
379 item->threaded = *attr->value == '1' ? TRUE : FALSE;
380 else if (!strcmp(attr->name, "hidereadmsgs"))
381 item->hide_read_msgs = *attr->value == '1' ? TRUE : FALSE;
382 else if (!strcmp(attr->name, "reqretrcpt"))
383 item->ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
384 else if (!strcmp(attr->name, "sort_key")) {
385 if (!strcmp(attr->value, "none"))
386 item->sort_key = SORT_BY_NONE;
387 else if (!strcmp(attr->value, "number"))
388 item->sort_key = SORT_BY_NUMBER;
389 else if (!strcmp(attr->value, "size"))
390 item->sort_key = SORT_BY_SIZE;
391 else if (!strcmp(attr->value, "date"))
392 item->sort_key = SORT_BY_DATE;
393 else if (!strcmp(attr->value, "from"))
394 item->sort_key = SORT_BY_FROM;
395 else if (!strcmp(attr->value, "subject"))
396 item->sort_key = SORT_BY_SUBJECT;
397 else if (!strcmp(attr->value, "score"))
398 item->sort_key = SORT_BY_SCORE;
399 else if (!strcmp(attr->value, "label"))
400 item->sort_key = SORT_BY_LABEL;
401 else if (!strcmp(attr->value, "mark"))
402 item->sort_key = SORT_BY_MARK;
403 else if (!strcmp(attr->value, "unread"))
404 item->sort_key = SORT_BY_STATUS;
405 else if (!strcmp(attr->value, "mime"))
406 item->sort_key = SORT_BY_MIME;
407 else if (!strcmp(attr->value, "to"))
408 item->sort_key = SORT_BY_TO;
409 else if (!strcmp(attr->value, "locked"))
410 item->sort_key = SORT_BY_LOCKED;
411 } else if (!strcmp(attr->name, "sort_type")) {
412 if (!strcmp(attr->value, "ascending"))
413 item->sort_type = SORT_ASCENDING;
415 item->sort_type = SORT_DESCENDING;
416 } else if (!strcmp(attr->name, "account_id")) {
417 PrefsAccount *account;
419 account = account_find_from_id(atoi(attr->value));
421 g_warning("account_id: %s not found\n", attr->value);
423 item->account = account;
424 } else if (!strcmp(attr->name, "apply_sub"))
425 item->apply_sub = *attr->value == '1' ? TRUE : FALSE;
429 XMLTag *folder_item_get_xml(Folder *folder, FolderItem *item)
431 static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
432 "draft", "queue", "trash"};
433 static gchar *sort_key_str[] = {"none", "number", "size", "date",
434 "from", "subject", "score", "label",
435 "mark", "unread", "mime", "to",
439 tag = g_new0(XMLTag, 1);
440 tag->tag = g_strdup("folderitem");
442 xml_tag_add_attr(tag, "type", g_strdup(folder_item_stype_str[item->stype]));
444 xml_tag_add_attr(tag, "name", g_strdup(item->name));
446 xml_tag_add_attr(tag, "path", g_strdup(item->path));
448 xml_tag_add_attr(tag, "no_sub", g_strdup("1"));
450 xml_tag_add_attr(tag, "no_select", g_strdup("1"));
451 xml_tag_add_attr(tag, "collapsed", g_strdup(item->collapsed && item->node->children ? "1" : "0"));
452 xml_tag_add_attr(tag, "thread_collapsed", g_strdup(item->thread_collapsed ? "1" : "0"));
453 xml_tag_add_attr(tag, "threaded", g_strdup(item->threaded ? "1" : "0"));
454 xml_tag_add_attr(tag, "hidereadmsgs", g_strdup(item->hide_read_msgs ? "1" : "0"));
456 xml_tag_add_attr(tag, "reqretrcpt", g_strdup("1"));
458 if (item->sort_key != SORT_BY_NONE) {
459 xml_tag_add_attr(tag, "sort_key", g_strdup(sort_key_str[item->sort_key]));
460 xml_tag_add_attr(tag, "sort_type", g_strdup(item->sort_type == SORT_ASCENDING ? "ascending" : "descending"));
463 xml_tag_add_attr(tag, "mtime", g_strdup_printf("%ld", (unsigned long int) item->mtime));
464 xml_tag_add_attr(tag, "new", g_strdup_printf("%d", item->new_msgs));
465 xml_tag_add_attr(tag, "unread", g_strdup_printf("%d", item->unread_msgs));
466 xml_tag_add_attr(tag, "unreadmarked", g_strdup_printf("%d", item->unreadmarked_msgs));
467 xml_tag_add_attr(tag, "total", g_strdup_printf("%d", item->total_msgs));
470 xml_tag_add_attr(tag, "account_id", g_strdup_printf("%d", item->account->account_id));
472 xml_tag_add_attr(tag, "apply_sub", g_strdup("1"));
477 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
479 g_return_if_fail(folder != NULL);
481 folder->ui_func = func;
482 folder->ui_func_data = data;
485 void folder_set_name(Folder *folder, const gchar *name)
487 g_return_if_fail(folder != NULL);
489 g_free(folder->name);
490 folder->name = name ? g_strdup(name) : NULL;
491 if (folder->node && folder->node->data) {
492 FolderItem *item = (FolderItem *)folder->node->data;
495 item->name = name ? g_strdup(name) : NULL;
499 gboolean folder_tree_destroy_func(GNode *node, gpointer data) {
500 FolderItem *item = (FolderItem *) node->data;
502 folder_item_destroy(item);
506 void folder_tree_destroy(Folder *folder)
510 g_return_if_fail(folder != NULL);
514 prefs_scoring_clear_folder(folder);
515 prefs_filtering_clear_folder(folder);
518 g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
519 folder_tree_destroy_func, NULL);
520 g_node_destroy(node);
525 void folder_add(Folder *folder)
531 g_return_if_fail(folder != NULL);
533 for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
534 cur_folder = FOLDER(cur->data);
535 if (FOLDER_TYPE(folder) == F_MH) {
536 if (FOLDER_TYPE(cur_folder) != F_MH) break;
537 } else if (FOLDER_TYPE(folder) == F_MBOX) {
538 if (FOLDER_TYPE(cur_folder) != F_MH &&
539 FOLDER_TYPE(cur_folder) != F_MBOX) break;
540 } else if (FOLDER_TYPE(folder) == F_IMAP) {
541 if (FOLDER_TYPE(cur_folder) != F_MH &&
542 FOLDER_TYPE(cur_folder) != F_MBOX &&
543 FOLDER_TYPE(cur_folder) != F_IMAP) break;
544 } else if (FOLDER_TYPE(folder) == F_NEWS) {
545 if (FOLDER_TYPE(cur_folder) != F_MH &&
546 FOLDER_TYPE(cur_folder) != F_MBOX &&
547 FOLDER_TYPE(cur_folder) != F_IMAP &&
548 FOLDER_TYPE(cur_folder) != F_NEWS) break;
552 folder_list = g_list_insert(folder_list, folder, i);
555 GList *folder_get_list(void)
560 gint folder_read_list(void)
566 path = folder_get_list_path();
567 if (!is_file_exist(path)) return -1;
568 node = xml_parse_file(path);
569 if (!node) return -1;
571 xmlnode = node->data;
572 if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
573 g_warning("wrong folder list\n");
578 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
579 folder_read_folder_func, NULL);
588 void folder_write_list(void)
595 path = folder_get_list_path();
596 if ((pfile = prefs_write_open(path)) == NULL) return;
598 fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
599 conv_get_current_charset_str());
600 fputs("\n<folderlist>\n", pfile->fp);
602 for (list = folder_list; list != NULL; list = list->next) {
604 folder_write_list_recursive(folder->node, pfile->fp);
607 fputs("</folderlist>\n", pfile->fp);
609 if (prefs_file_close(pfile) < 0)
610 g_warning("failed to write folder list.\n");
613 gboolean folder_scan_tree_func(GNode *node, gpointer data)
615 GHashTable *pptable = (GHashTable *)data;
616 FolderItem *item = (FolderItem *)node->data;
618 folder_item_restore_persist_prefs(item, pptable);
619 folder_item_scan_full(item, FALSE);
624 void folder_scan_tree(Folder *folder)
627 FolderUpdateData hookdata;
629 if (!folder->klass->scan_tree)
632 pptable = folder_persist_prefs_new(folder);
635 * should be changed and tree update should be done without
636 * destroying the tree first
638 folder_tree_destroy(folder);
639 folder->klass->scan_tree(folder);
641 hookdata.folder = folder;
642 hookdata.update_flags = FOLDER_TREE_CHANGED;
643 hookdata.item = NULL;
644 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
646 g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_scan_tree_func, pptable);
647 folder_persist_prefs_free(pptable);
649 prefs_matcher_read_config();
654 FolderItem *folder_create_folder(FolderItem *parent, const gchar *name)
656 FolderItem *new_item;
657 FolderUpdateData hookdata;
659 new_item = parent->folder->klass->create_folder(parent->folder, parent, name);
661 new_item->cache = msgcache_new();
663 hookdata.folder = new_item->folder;
664 hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_NEW_FOLDERITEM;
665 hookdata.item = new_item;
666 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
676 guint unreadmarked_msgs;
680 struct FuncToAllFoldersData
682 FolderItemFunc function;
686 static gboolean folder_func_to_all_folders_func(GNode *node, gpointer data)
689 struct FuncToAllFoldersData *function_data = (struct FuncToAllFoldersData *) data;
691 g_return_val_if_fail(node->data != NULL, FALSE);
693 item = FOLDER_ITEM(node->data);
694 g_return_val_if_fail(item != NULL, FALSE);
696 function_data->function(item, function_data->data);
701 void folder_func_to_all_folders(FolderItemFunc function, gpointer data)
705 struct FuncToAllFoldersData function_data;
707 function_data.function = function;
708 function_data.data = data;
710 for (list = folder_list; list != NULL; list = list->next) {
711 folder = FOLDER(list->data);
713 g_node_traverse(folder->node, G_PRE_ORDER,
715 folder_func_to_all_folders_func,
720 static void folder_count_total_msgs_func(FolderItem *item, gpointer data)
722 struct TotalMsgCount *count = (struct TotalMsgCount *)data;
724 count->new_msgs += item->new_msgs;
725 count->unread_msgs += item->unread_msgs;
726 count->unreadmarked_msgs += item->unreadmarked_msgs;
727 count->total_msgs += item->total_msgs;
730 struct TotalMsgStatus
738 static gboolean folder_get_status_full_all_func(GNode *node, gpointer data)
741 struct TotalMsgStatus *status = (struct TotalMsgStatus *)data;
744 g_return_val_if_fail(node->data != NULL, FALSE);
746 item = FOLDER_ITEM(node->data);
748 if (!item->path) return FALSE;
750 status->new += item->new_msgs;
751 status->unread += item->unread_msgs;
752 status->total += item->total_msgs;
755 id = folder_item_get_identifier(item);
756 g_string_sprintfa(status->str, "%5d %5d %5d %s\n",
757 item->new_msgs, item->unread_msgs,
758 item->total_msgs, id);
765 static void folder_get_status_full_all(GString *str, guint *new, guint *unread,
770 struct TotalMsgStatus status;
772 status.new = status.unread = status.total = 0;
775 debug_print("Counting total number of messages...\n");
777 for (list = folder_list; list != NULL; list = list->next) {
778 folder = FOLDER(list->data);
780 g_node_traverse(folder->node, G_PRE_ORDER,
782 folder_get_status_full_all_func,
787 *unread = status.unread;
788 *total = status.total;
791 gchar *folder_get_status(GPtrArray *folders, gboolean full)
793 guint new, unread, total;
798 new = unread = total = 0;
800 str = g_string_new(NULL);
803 for (i = 0; i < folders->len; i++) {
806 item = g_ptr_array_index(folders, i);
807 new += item->new_msgs;
808 unread += item->unread_msgs;
809 total += item->total_msgs;
814 id = folder_item_get_identifier(item);
815 g_string_sprintfa(str, "%5d %5d %5d %s\n",
816 item->new_msgs, item->unread_msgs,
817 item->total_msgs, id);
822 folder_get_status_full_all(full ? str : NULL,
823 &new, &unread, &total);
827 g_string_sprintfa(str, "%5d %5d %5d\n", new, unread, total);
829 g_string_sprintfa(str, "%d %d %d\n", new, unread, total);
832 g_string_free(str, FALSE);
837 void folder_count_total_msgs(guint *new_msgs, guint *unread_msgs, guint *unreadmarked_msgs, guint *total_msgs)
839 struct TotalMsgCount count;
841 count.new_msgs = count.unread_msgs = count.unreadmarked_msgs = count.total_msgs = 0;
843 debug_print("Counting total number of messages...\n");
845 folder_func_to_all_folders(folder_count_total_msgs_func, &count);
847 *new_msgs = count.new_msgs;
848 *unread_msgs = count.unread_msgs;
849 *unreadmarked_msgs = count.unreadmarked_msgs;
850 *total_msgs = count.total_msgs;
853 Folder *folder_find_from_path(const gchar *path)
858 for (list = folder_list; list != NULL; list = list->next) {
860 if ((FOLDER_TYPE(folder) == F_MH ||
861 FOLDER_TYPE(folder) == F_MBOX) &&
862 !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
869 Folder *folder_find_from_name(const gchar *name, FolderClass *klass)
874 for (list = folder_list; list != NULL; list = list->next) {
876 if (folder->klass == klass &&
877 strcmp2(name, folder->name) == 0)
884 static gboolean folder_item_find_func(GNode *node, gpointer data)
886 FolderItem *item = node->data;
888 const gchar *path = d[0];
890 if (path_cmp(path, item->path) != 0)
898 FolderItem *folder_find_item_from_path(const gchar *path)
903 folder = folder_get_default_folder();
904 g_return_val_if_fail(folder != NULL, NULL);
906 d[0] = (gpointer)path;
908 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
909 folder_item_find_func, d);
913 FolderClass *folder_get_class_from_string(const gchar *str)
917 classlist = folder_get_class_list();
918 for (; classlist != NULL; classlist = g_slist_next(classlist)) {
919 FolderClass *class = (FolderClass *) classlist->data;
920 if (g_strcasecmp(class->idstr, str) == 0)
927 gchar *folder_get_identifier(Folder *folder)
931 g_return_val_if_fail(folder != NULL, NULL);
933 type_str = folder->klass->idstr;
934 return g_strconcat("#", type_str, "/", folder->name, NULL);
937 gchar *folder_item_get_identifier(FolderItem *item)
942 g_return_val_if_fail(item != NULL, NULL);
943 g_return_val_if_fail(item->path != NULL, NULL);
945 folder_id = folder_get_identifier(item->folder);
946 id = g_strconcat(folder_id, "/", item->path, NULL);
952 FolderItem *folder_find_item_from_identifier(const gchar *identifier)
962 g_return_val_if_fail(identifier != NULL, NULL);
964 if (*identifier != '#')
965 return folder_find_item_from_path(identifier);
967 Xstrdup_a(str, identifier, return NULL);
969 p = strchr(str, '/');
971 return folder_find_item_from_path(identifier);
974 class = folder_get_class_from_string(&str[1]);
976 return folder_find_item_from_path(identifier);
981 return folder_find_item_from_path(identifier);
985 folder = folder_find_from_name(name, class);
987 return folder_find_item_from_path(identifier);
991 d[0] = (gpointer)path;
993 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
994 folder_item_find_func, d);
999 * Get a displayable name for a FolderItem
1001 * \param item FolderItem for that a name should be created
1002 * \return Displayable name for item, returned string has to
1005 gchar *folder_item_get_name(FolderItem *item)
1009 g_return_val_if_fail(item != NULL, g_strdup(""));
1011 switch (item->stype) {
1013 name = g_strdup(!strcmp2(item->name, INBOX_DIR) ? _("Inbox") :
1017 name = g_strdup(!strcmp2(item->name, OUTBOX_DIR) ? _("Sent") :
1021 name = g_strdup(!strcmp2(item->name, QUEUE_DIR) ? _("Queue") :
1025 name = g_strdup(!strcmp2(item->name, TRASH_DIR) ? _("Trash") :
1029 name = g_strdup(!strcmp2(item->name, DRAFT_DIR) ? _("Drafts") :
1038 * should probably be done by a virtual function,
1039 * the folder knows the ui string and how to abbrev
1041 if (!item->parent) {
1042 name = g_strconcat(item->name, " (", item->folder->klass->uistr, ")", NULL);
1044 if (FOLDER_CLASS(item->folder) == news_get_class() &&
1045 item->path && !strcmp2(item->name, item->path))
1046 name = get_abbrev_newsgroup_name
1048 prefs_common.ng_abbrev_len);
1050 name = g_strdup(item->name);
1055 name = g_strdup("");
1060 Folder *folder_get_default_folder(void)
1062 return folder_list ? FOLDER(folder_list->data) : NULL;
1065 FolderItem *folder_get_default_inbox(void)
1069 if (!folder_list) return NULL;
1070 folder = FOLDER(folder_list->data);
1071 g_return_val_if_fail(folder != NULL, NULL);
1072 return folder->inbox;
1075 FolderItem *folder_get_default_outbox(void)
1079 if (!folder_list) return NULL;
1080 folder = FOLDER(folder_list->data);
1081 g_return_val_if_fail(folder != NULL, NULL);
1082 return folder->outbox;
1085 FolderItem *folder_get_default_draft(void)
1089 if (!folder_list) return NULL;
1090 folder = FOLDER(folder_list->data);
1091 g_return_val_if_fail(folder != NULL, NULL);
1092 return folder->draft;
1095 FolderItem *folder_get_default_queue(void)
1099 if (!folder_list) return NULL;
1100 folder = FOLDER(folder_list->data);
1101 g_return_val_if_fail(folder != NULL, NULL);
1102 return folder->queue;
1105 FolderItem *folder_get_default_trash(void)
1109 if (!folder_list) return NULL;
1110 folder = FOLDER(folder_list->data);
1111 g_return_val_if_fail(folder != NULL, NULL);
1112 return folder->trash;
1115 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type) \
1117 if (!folder->member) { \
1118 item = folder_item_new(folder, dir, dir); \
1119 item->stype = type; \
1120 folder_item_append(rootitem, item); \
1121 folder->member = item; \
1125 void folder_set_missing_folders(void)
1128 FolderItem *rootitem;
1132 for (list = folder_list; list != NULL; list = list->next) {
1133 folder = list->data;
1134 if (FOLDER_TYPE(folder) != F_MH) continue;
1135 rootitem = FOLDER_ITEM(folder->node->data);
1136 g_return_if_fail(rootitem != NULL);
1138 if (folder->inbox && folder->outbox && folder->draft &&
1139 folder->queue && folder->trash)
1142 if (folder->klass->create_tree(folder) < 0) {
1143 g_warning("%s: can't create the folder tree.\n",
1144 LOCAL_FOLDER(folder)->rootpath);
1148 CREATE_FOLDER_IF_NOT_EXIST(inbox, INBOX_DIR, F_INBOX);
1149 CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
1150 CREATE_FOLDER_IF_NOT_EXIST(draft, DRAFT_DIR, F_DRAFT);
1151 CREATE_FOLDER_IF_NOT_EXIST(queue, QUEUE_DIR, F_QUEUE);
1152 CREATE_FOLDER_IF_NOT_EXIST(trash, TRASH_DIR, F_TRASH);
1156 static gboolean folder_unref_account_func(GNode *node, gpointer data)
1158 FolderItem *item = node->data;
1159 PrefsAccount *account = data;
1161 if (item->account == account)
1162 item->account = NULL;
1167 void folder_unref_account_all(PrefsAccount *account)
1172 if (!account) return;
1174 for (list = folder_list; list != NULL; list = list->next) {
1175 folder = list->data;
1176 if (folder->account == account)
1177 folder->account = NULL;
1178 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1179 folder_unref_account_func, account);
1183 #undef CREATE_FOLDER_IF_NOT_EXIST
1185 gchar *folder_item_get_path(FolderItem *item)
1189 g_return_val_if_fail(item != NULL, NULL);
1190 folder = item->folder;
1191 g_return_val_if_fail(folder != NULL, NULL);
1193 return folder->klass->item_get_path(folder, item);
1196 void folder_item_set_default_flags(FolderItem *dest, MsgFlags *flags)
1198 if (!(dest->stype == F_OUTBOX ||
1199 dest->stype == F_QUEUE ||
1200 dest->stype == F_DRAFT ||
1201 dest->stype == F_TRASH)) {
1202 flags->perm_flags = MSG_NEW|MSG_UNREAD;
1204 flags->perm_flags = 0;
1206 flags->tmp_flags = MSG_CACHED;
1207 if (FOLDER_TYPE(dest->folder) == F_MH) {
1208 if (dest->stype == F_QUEUE) {
1209 MSG_SET_TMP_FLAGS(*flags, MSG_QUEUED);
1210 } else if (dest->stype == F_DRAFT) {
1211 MSG_SET_TMP_FLAGS(*flags, MSG_DRAFT);
1216 static gint folder_sort_cache_list_by_msgnum(gconstpointer a, gconstpointer b)
1218 MsgInfo *msginfo_a = (MsgInfo *) a;
1219 MsgInfo *msginfo_b = (MsgInfo *) b;
1221 return (msginfo_a->msgnum - msginfo_b->msgnum);
1224 static gint folder_sort_folder_list(gconstpointer a, gconstpointer b)
1226 guint gint_a = GPOINTER_TO_INT(a);
1227 guint gint_b = GPOINTER_TO_INT(b);
1229 return (gint_a - gint_b);
1232 gint folder_item_open(FolderItem *item)
1234 if((item->folder->klass->scan_required != NULL) && (item->folder->klass->scan_required(item->folder, item))) {
1235 folder_item_scan_full(item, TRUE);
1239 if(item->prefs->processing != NULL) {
1242 buf = g_strdup_printf(_("Processing (%s)...\n"), item->path);
1243 debug_print("%s\n", buf);
1246 folder_item_apply_processing(item);
1248 debug_print("done.\n");
1254 gint folder_item_close(FolderItem *item)
1256 GSList *mlist, *cur;
1259 g_return_val_if_fail(item != NULL, -1);
1261 if (item->new_msgs) {
1262 folder_item_update_freeze();
1263 mlist = folder_item_get_msg_list(item);
1264 for (cur = mlist ; cur != NULL ; cur = cur->next) {
1267 msginfo = (MsgInfo *) cur->data;
1268 if (MSG_IS_NEW(msginfo->flags))
1269 procmsg_msginfo_unset_flags(msginfo, MSG_NEW, 0);
1270 procmsg_msginfo_free(msginfo);
1272 g_slist_free(mlist);
1273 folder_item_update_thaw();
1276 folder_item_write_cache(item);
1278 folder_item_update(item, F_ITEM_UPDATE_MSGCNT);
1280 item->opened = FALSE;
1281 folder = item->folder;
1283 if (folder->klass->close == NULL)
1286 return folder->klass->close(folder, item);
1289 gint folder_item_scan_full(FolderItem *item, gboolean filtering)
1292 GSList *folder_list = NULL, *cache_list = NULL;
1293 GSList *folder_list_cur, *cache_list_cur, *new_list = NULL;
1294 GSList *exists_list = NULL, *elem;
1295 GSList *newmsg_list = NULL;
1296 guint newcnt = 0, unreadcnt = 0, totalcnt = 0, unreadmarkedcnt = 0;
1297 guint cache_max_num, folder_max_num, cache_cur_num, folder_cur_num;
1298 gboolean update_flags = 0, old_uids_valid = FALSE;
1300 g_return_val_if_fail(item != NULL, -1);
1301 if (item->path == NULL) return -1;
1303 folder = item->folder;
1305 g_return_val_if_fail(folder != NULL, -1);
1306 g_return_val_if_fail(folder->klass->get_num_list != NULL, -1);
1308 debug_print("Scanning folder %s for cache changes.\n", item->path);
1310 /* Get list of messages for folder and cache */
1311 if (folder->klass->get_num_list(item->folder, item, &folder_list, &old_uids_valid) < 0) {
1312 debug_print("Error fetching list of message numbers\n");
1316 if (old_uids_valid) {
1318 folder_item_read_cache(item);
1319 cache_list = msgcache_get_msg_list(item->cache);
1322 msgcache_destroy(item->cache);
1323 item->cache = msgcache_new();
1327 /* Sort both lists */
1328 cache_list = g_slist_sort(cache_list, folder_sort_cache_list_by_msgnum);
1329 folder_list = g_slist_sort(folder_list, folder_sort_folder_list);
1331 cache_list_cur = cache_list;
1332 folder_list_cur = folder_list;
1334 if (cache_list_cur != NULL) {
1335 GSList *cache_list_last;
1337 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1338 cache_list_last = g_slist_last(cache_list);
1339 cache_max_num = ((MsgInfo *)cache_list_last->data)->msgnum;
1341 cache_cur_num = G_MAXINT;
1345 if (folder_list_cur != NULL) {
1346 GSList *folder_list_last;
1348 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1349 folder_list_last = g_slist_last(folder_list);
1350 folder_max_num = GPOINTER_TO_INT(folder_list_last->data);
1352 folder_cur_num = G_MAXINT;
1356 while ((cache_cur_num != G_MAXINT) || (folder_cur_num != G_MAXINT)) {
1358 * Message only exists in the folder
1359 * Remember message for fetching
1361 if (folder_cur_num < cache_cur_num) {
1362 gboolean add = FALSE;
1364 switch(FOLDER_TYPE(folder)) {
1366 if (folder_cur_num < cache_max_num)
1369 if (folder->account->max_articles == 0) {
1373 if (folder_max_num <= folder->account->max_articles) {
1375 } else if (folder_cur_num > (folder_max_num - folder->account->max_articles)) {
1385 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(folder_cur_num));
1386 debug_print("Remembered message %d for fetching\n", folder_cur_num);
1389 /* Move to next folder number */
1390 folder_list_cur = folder_list_cur->next;
1392 if (folder_list_cur != NULL)
1393 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1395 folder_cur_num = G_MAXINT;
1401 * Message only exists in the cache
1402 * Remove the message from the cache
1404 if (cache_cur_num < folder_cur_num) {
1405 msgcache_remove_msg(item->cache, cache_cur_num);
1406 debug_print("Removed message %d from cache.\n", cache_cur_num);
1408 /* Move to next cache number */
1409 cache_list_cur = cache_list_cur->next;
1411 if (cache_list_cur != NULL)
1412 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1414 cache_cur_num = G_MAXINT;
1416 update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
1422 * Message number exists in folder and cache!
1423 * Check if the message has been modified
1425 if (cache_cur_num == folder_cur_num) {
1428 msginfo = msgcache_get_msg(item->cache, folder_cur_num);
1429 if (folder->klass->is_msg_changed && folder->klass->is_msg_changed(folder, item, msginfo)) {
1430 msgcache_remove_msg(item->cache, msginfo->msgnum);
1431 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(msginfo->msgnum));
1432 procmsg_msginfo_free(msginfo);
1434 debug_print("Remembering message %d to update...\n", folder_cur_num);
1436 exists_list = g_slist_prepend(exists_list, msginfo);
1438 /* Move to next folder and cache number */
1439 cache_list_cur = cache_list_cur->next;
1440 folder_list_cur = folder_list_cur->next;
1442 if (cache_list_cur != NULL)
1443 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1445 cache_cur_num = G_MAXINT;
1447 if (folder_list_cur != NULL)
1448 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1450 folder_cur_num = G_MAXINT;
1456 for(cache_list_cur = cache_list; cache_list_cur != NULL; cache_list_cur = g_slist_next(cache_list_cur))
1457 procmsg_msginfo_free((MsgInfo *) cache_list_cur->data);
1459 g_slist_free(cache_list);
1460 g_slist_free(folder_list);
1462 if (new_list != NULL) {
1463 if (folder->klass->get_msginfos) {
1464 newmsg_list = folder->klass->get_msginfos(folder, item, new_list);
1465 } else if (folder->klass->get_msginfo) {
1468 for (elem = new_list; elem != NULL; elem = g_slist_next(elem)) {
1472 num = GPOINTER_TO_INT(elem->data);
1473 msginfo = folder->klass->get_msginfo(folder, item, num);
1474 if (msginfo != NULL) {
1475 newmsg_list = g_slist_prepend(newmsg_list, msginfo);
1476 debug_print("Added newly found message %d to cache.\n", num);
1480 g_slist_free(new_list);
1483 folder_item_update_freeze();
1484 if (newmsg_list != NULL) {
1487 for (elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) {
1488 MsgInfo *msginfo = (MsgInfo *) elem->data;
1490 msgcache_add_msg(item->cache, msginfo);
1491 if ((filtering == TRUE) &&
1492 (item->stype == F_INBOX) &&
1493 (item->folder->account != NULL) &&
1494 (item->folder->account->filter_on_recv) &&
1495 procmsg_msginfo_filter(msginfo))
1496 procmsg_msginfo_free(msginfo);
1498 exists_list = g_slist_prepend(exists_list, msginfo);
1500 g_slist_free(newmsg_list);
1502 update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
1505 for (elem = exists_list; elem != NULL; elem = g_slist_next(elem)) {
1508 msginfo = elem->data;
1509 if (MSG_IS_IGNORE_THREAD(msginfo->flags) && (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
1510 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1511 if (!MSG_IS_IGNORE_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD)) {
1512 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1513 procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
1515 if ((item->stype == F_OUTBOX ||
1516 item->stype == F_QUEUE ||
1517 item->stype == F_DRAFT ||
1518 item->stype == F_TRASH) &&
1519 (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
1520 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1521 if (MSG_IS_NEW(msginfo->flags))
1523 if (MSG_IS_UNREAD(msginfo->flags))
1525 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
1529 procmsg_msginfo_free(msginfo);
1531 g_slist_free(exists_list);
1533 item->new_msgs = newcnt;
1534 item->unread_msgs = unreadcnt;
1535 item->total_msgs = totalcnt;
1536 item->unreadmarked_msgs = unreadmarkedcnt;
1538 update_flags |= F_ITEM_UPDATE_MSGCNT;
1540 folder_item_update(item, update_flags);
1541 folder_item_update_thaw();
1546 gint folder_item_scan(FolderItem *item)
1548 return folder_item_scan_full(item, TRUE);
1551 static gboolean folder_scan_all_items_func(GNode *node, gpointer data)
1553 FolderItem *item = node->data;
1555 folder_item_scan(item);
1560 void folder_scan_all_items(Folder * folder)
1562 g_node_traverse(folder->node, G_PRE_ORDER,
1563 G_TRAVERSE_ALL, -1, folder_scan_all_items_func, NULL);
1566 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
1569 folder_item_scan(FOLDER_ITEM(key));
1572 void folder_item_scan_foreach(GHashTable *table)
1574 g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
1577 void folder_count_total_cache_memusage(FolderItem *item, gpointer data)
1579 gint *memusage = (gint *)data;
1581 if (item->cache == NULL)
1584 *memusage += msgcache_get_memory_usage(item->cache);
1587 gint folder_cache_time_compare_func(gconstpointer a, gconstpointer b)
1589 FolderItem *fa = (FolderItem *)a;
1590 FolderItem *fb = (FolderItem *)b;
1592 return (gint) (msgcache_get_last_access_time(fa->cache) - msgcache_get_last_access_time(fb->cache));
1595 void folder_find_expired_caches(FolderItem *item, gpointer data)
1597 GSList **folder_item_list = (GSList **)data;
1598 gint difftime, expiretime;
1600 if (item->cache == NULL)
1603 if (item->opened > 0)
1606 difftime = (gint) (time(NULL) - msgcache_get_last_access_time(item->cache));
1607 expiretime = prefs_common.cache_min_keep_time * 60;
1608 debug_print("Cache unused time: %d (Expire time: %d)\n", difftime, expiretime);
1609 if (difftime > expiretime) {
1610 *folder_item_list = g_slist_insert_sorted(*folder_item_list, item, folder_cache_time_compare_func);
1614 void folder_item_free_cache(FolderItem *item)
1616 g_return_if_fail(item != NULL);
1618 if (item->cache == NULL)
1621 if (item->opened > 0)
1624 folder_item_write_cache(item);
1625 msgcache_destroy(item->cache);
1629 void folder_clean_cache_memory(void)
1633 folder_func_to_all_folders(folder_count_total_cache_memusage, &memusage);
1634 debug_print("Total cache memory usage: %d\n", memusage);
1636 if (memusage > (prefs_common.cache_max_mem_usage * 1024)) {
1637 GSList *folder_item_list = NULL, *listitem;
1639 debug_print("Trying to free cache memory\n");
1641 folder_func_to_all_folders(folder_find_expired_caches, &folder_item_list);
1642 listitem = folder_item_list;
1643 while((listitem != NULL) && (memusage > (prefs_common.cache_max_mem_usage * 1024))) {
1644 FolderItem *item = (FolderItem *)(listitem->data);
1646 debug_print("Freeing cache memory for %s\n", item->path);
1647 memusage -= msgcache_get_memory_usage(item->cache);
1648 folder_item_free_cache(item);
1649 listitem = listitem->next;
1651 g_slist_free(folder_item_list);
1655 void folder_item_read_cache(FolderItem *item)
1657 gchar *cache_file, *mark_file;
1659 g_return_if_fail(item != NULL);
1661 cache_file = folder_item_get_cache_file(item);
1662 mark_file = folder_item_get_mark_file(item);
1663 item->cache = msgcache_read_cache(item, cache_file);
1665 item->cache = msgcache_new();
1666 folder_item_scan_full(item, TRUE);
1668 msgcache_read_mark(item->cache, mark_file);
1672 folder_clean_cache_memory();
1675 void folder_item_write_cache(FolderItem *item)
1677 gchar *cache_file, *mark_file;
1678 FolderItemPrefs *prefs;
1682 if (!item || !item->path || !item->cache)
1685 id = folder_item_get_identifier(item);
1686 debug_print("Save cache for folder %s\n", id);
1689 cache_file = folder_item_get_cache_file(item);
1690 mark_file = folder_item_get_mark_file(item);
1691 if (msgcache_write(cache_file, mark_file, item->cache) < 0) {
1692 prefs = item->prefs;
1693 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
1694 /* for cache file */
1695 filemode = prefs->folder_chmod;
1696 if (filemode & S_IRGRP) filemode |= S_IWGRP;
1697 if (filemode & S_IROTH) filemode |= S_IWOTH;
1698 chmod(cache_file, filemode);
1706 MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
1711 g_return_val_if_fail(item != NULL, NULL);
1713 folder = item->folder;
1715 folder_item_read_cache(item);
1717 if ((msginfo = msgcache_get_msg(item->cache, num)) != NULL)
1720 g_return_val_if_fail(folder->klass->get_msginfo, NULL);
1721 if ((msginfo = folder->klass->get_msginfo(folder, item, num)) != NULL) {
1722 msgcache_add_msg(item->cache, msginfo);
1729 MsgInfo *folder_item_get_msginfo_by_msgid(FolderItem *item, const gchar *msgid)
1734 g_return_val_if_fail(item != NULL, NULL);
1735 g_return_val_if_fail(msgid != NULL, NULL);
1737 folder = item->folder;
1739 folder_item_read_cache(item);
1741 if ((msginfo = msgcache_get_msg_by_id(item->cache, msgid)) != NULL)
1747 GSList *folder_item_get_msg_list(FolderItem *item)
1749 g_return_val_if_fail(item != NULL, NULL);
1751 if (item->cache == 0)
1752 folder_item_read_cache(item);
1754 g_return_val_if_fail(item->cache != NULL, NULL);
1756 return msgcache_get_msg_list(item->cache);
1759 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
1763 g_return_val_if_fail(item != NULL, NULL);
1765 folder = item->folder;
1767 g_return_val_if_fail(folder->klass->fetch_msg != NULL, NULL);
1769 return folder->klass->fetch_msg(folder, item, num);
1772 static gint folder_item_get_msg_num_by_file(FolderItem *dest, const gchar *file)
1774 static HeaderEntry hentry[] = {{"Message-ID:", NULL, TRUE},
1775 {NULL, NULL, FALSE}};
1779 gchar buf[BUFFSIZE];
1781 if ((fp = fopen(file, "rb")) == NULL)
1784 if ((dest->stype == F_QUEUE) || (dest->stype == F_DRAFT))
1785 while (fgets(buf, sizeof(buf), fp) != NULL)
1786 if (buf[0] == '\r' || buf[0] == '\n') break;
1788 procheader_get_header_fields(fp, hentry);
1789 if (hentry[0].body) {
1790 extract_parenthesis(hentry[0].body, '<', '>');
1791 remove_space(hentry[0].body);
1792 if ((msginfo = msgcache_get_msg_by_id(dest->cache, hentry[0].body)) != NULL) {
1793 msgnum = msginfo->msgnum;
1794 procmsg_msginfo_free(msginfo);
1796 debug_print("found message as uid %d\n", msgnum);
1800 g_free(hentry[0].body);
1801 hentry[0].body = NULL;
1807 static void copy_msginfo_flags(MsgInfo *source, MsgInfo *dest)
1809 MsgPermFlags perm_flags = 0;
1810 MsgTmpFlags tmp_flags = 0;
1812 /* create new flags */
1813 if (source != NULL) {
1814 /* copy original flags */
1815 perm_flags = source->flags.perm_flags;
1816 tmp_flags = source->flags.tmp_flags;
1818 perm_flags = dest->flags.perm_flags;
1819 tmp_flags = dest->flags.tmp_flags;
1822 /* remove new, unread and deleted in special folders */
1823 if (dest->folder->stype == F_OUTBOX ||
1824 dest->folder->stype == F_QUEUE ||
1825 dest->folder->stype == F_DRAFT ||
1826 dest->folder->stype == F_TRASH)
1827 perm_flags &= ~(MSG_NEW | MSG_UNREAD | MSG_DELETED);
1829 /* set ignore flag of ignored parent exists */
1830 if (procmsg_msg_has_flagged_parent(dest, MSG_IGNORE_THREAD))
1831 perm_flags |= MSG_IGNORE_THREAD;
1833 /* Unset tmp flags that should not be copied */
1834 tmp_flags &= ~(MSG_MOVE | MSG_COPY);
1836 /* unset flags that are set but should not */
1837 procmsg_msginfo_unset_flags(dest,
1838 dest->flags.perm_flags & ~perm_flags,
1839 dest->flags.tmp_flags & ~tmp_flags);
1841 procmsg_msginfo_set_flags(dest,
1842 ~dest->flags.perm_flags & perm_flags,
1843 ~dest->flags.tmp_flags & tmp_flags);
1845 folder_item_update(dest->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
1848 static void add_msginfo_to_cache(FolderItem *item, MsgInfo *newmsginfo, MsgInfo *flagsource)
1850 /* update folder stats */
1851 if (MSG_IS_NEW(newmsginfo->flags))
1853 if (MSG_IS_UNREAD(newmsginfo->flags))
1854 item->unread_msgs++;
1855 if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
1856 item->unreadmarked_msgs++;
1859 copy_msginfo_flags(flagsource, newmsginfo);
1861 msgcache_add_msg(item->cache, newmsginfo);
1864 static void remove_msginfo_from_cache(FolderItem *item, MsgInfo *msginfo)
1866 MsgInfoUpdate msginfo_update;
1869 folder_item_read_cache(item);
1871 if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1872 msginfo->folder->new_msgs--;
1873 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1874 msginfo->folder->unread_msgs--;
1875 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
1876 msginfo->folder->unreadmarked_msgs--;
1877 msginfo->folder->total_msgs--;
1879 msginfo_update.msginfo = msginfo;
1880 msginfo_update.flags = MSGINFO_UPDATE_DELETED;
1881 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1883 msgcache_remove_msg(item->cache, msginfo->msgnum);
1884 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
1887 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
1888 MsgFlags *flags, gboolean remove_source)
1891 MsgFileInfo fileinfo;
1893 g_return_val_if_fail(dest != NULL, -1);
1894 g_return_val_if_fail(file != NULL, -1);
1896 fileinfo.msginfo = NULL;
1897 fileinfo.file = (gchar *)file;
1898 fileinfo.flags = flags;
1899 file_list.data = &fileinfo;
1900 file_list.next = NULL;
1902 return folder_item_add_msgs(dest, &file_list, remove_source);
1905 gint folder_item_add_msgs(FolderItem *dest, GSList *file_list,
1906 gboolean remove_source)
1909 gint ret, num, lastnum = -1;
1911 GRelation *relation;
1912 MsgFileInfo *fileinfo = NULL;
1913 gboolean folderscan = FALSE;
1915 g_return_val_if_fail(dest != NULL, -1);
1916 g_return_val_if_fail(file_list != NULL, -1);
1917 g_return_val_if_fail(dest->folder != NULL, -1);
1919 folder = dest->folder;
1921 relation = g_relation_new(2);
1922 g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
1924 if (folder->klass->add_msgs != NULL) {
1925 ret = folder->klass->add_msgs(folder, dest, file_list, relation);
1927 g_relation_destroy(relation);
1931 for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
1932 fileinfo = (MsgFileInfo *) file_cur->data;
1934 ret = folder->klass->add_msg(folder, dest, fileinfo->file, fileinfo->flags);
1936 g_relation_destroy(relation);
1939 g_relation_insert(relation, fileinfo, GINT_TO_POINTER(ret));
1943 for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
1946 fileinfo = (MsgFileInfo *) file_cur->data;
1947 tuples = g_relation_select(relation, fileinfo, 0);
1948 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1949 g_tuples_destroy(tuples);
1952 MsgInfo *newmsginfo;
1956 folder_item_scan_full(dest, FALSE);
1959 num = folder_item_get_msg_num_by_file(dest, fileinfo->file);
1965 if (num >= 0 && remove_source) {
1966 if (unlink(fileinfo->file) < 0)
1967 FILE_OP_ERROR(fileinfo->file, "unlink");
1974 ((newmsginfo = folder->klass->get_msginfo(folder, dest, num)) != NULL)) {
1975 add_msginfo_to_cache(dest, newmsginfo, NULL);
1976 procmsg_msginfo_free(newmsginfo);
1977 } else if ((newmsginfo = msgcache_get_msg(dest->cache, num)) != NULL) {
1978 /* TODO: set default flags */
1979 procmsg_msginfo_free(newmsginfo);
1984 g_relation_destroy(relation);
1990 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1995 g_return_val_if_fail(dest != NULL, -1);
1996 g_return_val_if_fail(msginfo != NULL, -1);
1998 folder = dest->folder;
1999 if (dest->last_num < 0) folder->scan(folder, dest);
2001 num = folder->move_msg(folder, dest, msginfo);
2002 if (num > 0) dest->last_num = num;
2008 FolderItem *folder_item_move_recursive(FolderItem *src, FolderItem *dest)
2011 FolderItem *new_item;
2012 FolderItem *next_item;
2014 gchar *old_id, *new_id;
2016 mlist = folder_item_get_msg_list(src);
2019 debug_print("Moving %s to %s\n", src->path, dest->path);
2020 new_item = folder_create_folder(dest, g_basename(src->path));
2021 if (new_item == NULL) {
2022 printf("Can't create folder\n");
2026 if (new_item->folder == NULL)
2027 new_item->folder = dest->folder;
2030 log_message(_("Moving %s to %s...\n"),
2031 src->name, new_item->path);
2032 folder_item_move_msgs(new_item, mlist);
2035 folder_item_prefs_copy_prefs(src, new_item);
2036 new_item->collapsed = src->collapsed;
2037 new_item->thread_collapsed = src->thread_collapsed;
2038 new_item->threaded = src->threaded;
2039 new_item->ret_rcpt = src->ret_rcpt;
2040 new_item->hide_read_msgs = src->hide_read_msgs;
2041 new_item->sort_key = src->sort_key;
2042 new_item->sort_type = src->sort_type;
2044 prefs_matcher_write_config();
2047 srcnode = src->folder->node;
2048 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, src);
2049 srcnode = srcnode->children;
2050 while (srcnode != NULL) {
2051 if (srcnode && srcnode->data) {
2052 next_item = (FolderItem*) srcnode->data;
2053 srcnode = srcnode->next;
2054 if (folder_item_move_recursive(next_item, new_item) == NULL)
2058 old_id = folder_item_get_identifier(src);
2059 new_id = folder_item_get_identifier(new_item);
2060 debug_print("updating rules : %s => %s\n", old_id, new_id);
2062 src->folder->klass->remove_folder(src->folder, src);
2063 folder_write_list();
2065 if (old_id != NULL && new_id != NULL)
2066 prefs_filtering_rename_path(old_id, new_id);
2073 gint folder_item_move_to(FolderItem *src, FolderItem *dest, FolderItem **new_item)
2075 FolderItem *tmp = dest->parent;
2076 gchar * src_identifier, * dst_identifier;
2077 gchar * phys_srcpath, * phys_dstpath;
2081 return F_MOVE_FAILED_DEST_IS_CHILD;
2088 src_identifier = folder_item_get_identifier(src);
2089 dst_identifier = folder_item_get_identifier(dest);
2091 if(dst_identifier == NULL && dest->folder && dest->parent == NULL) {
2092 /* dest can be a root folder */
2093 dst_identifier = folder_get_identifier(dest->folder);
2095 if (src_identifier == NULL || dst_identifier == NULL) {
2096 debug_print("Can't get identifiers\n");
2097 return F_MOVE_FAILED;
2100 if (src->folder != dest->folder) {
2101 return F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX;
2104 phys_srcpath = folder_item_get_path(src);
2105 phys_dstpath = g_strconcat(folder_item_get_path(dest),G_DIR_SEPARATOR_S,g_basename(phys_srcpath),NULL);
2107 if (src->parent == dest || src == dest) {
2108 g_free(src_identifier);
2109 g_free(dst_identifier);
2110 g_free(phys_srcpath);
2111 g_free(phys_dstpath);
2112 return F_MOVE_FAILED_DEST_IS_PARENT;
2114 debug_print("moving \"%s\" to \"%s\"\n", phys_srcpath, phys_dstpath);
2115 if ((tmp = folder_item_move_recursive(src, dest)) == NULL) {
2116 return F_MOVE_FAILED;
2119 g_free(src_identifier);
2120 g_free(dst_identifier);
2121 g_free(phys_srcpath);
2122 g_free(phys_dstpath);
2130 * Copy a list of message to a new folder and remove
2131 * source messages if wanted
2133 static gint do_copy_msgs(FolderItem *dest, GSList *msglist, gboolean remove_source)
2137 gint num, lastnum = -1;
2138 gboolean folderscan = FALSE;
2139 GRelation *relation;
2141 g_return_val_if_fail(dest != NULL, -1);
2142 g_return_val_if_fail(msglist != NULL, -1);
2144 folder = dest->folder;
2146 g_return_val_if_fail(folder->klass->copy_msg != NULL, -1);
2148 relation = g_relation_new(2);
2149 g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
2150 g_relation_index(relation, 1, g_direct_hash, g_direct_equal);
2153 * Copy messages to destination folder and
2154 * store new message numbers in newmsgnums
2156 if (folder->klass->copy_msgs != NULL) {
2157 if (folder->klass->copy_msgs(folder, dest, msglist, relation) < 0) {
2158 g_relation_destroy(relation);
2162 for (l = msglist ; l != NULL ; l = g_slist_next(l)) {
2163 MsgInfo * msginfo = (MsgInfo *) l->data;
2165 num = folder->klass->copy_msg(folder, dest, msginfo);
2166 g_relation_insert(relation, msginfo, GINT_TO_POINTER(num));
2170 /* Read cache for dest folder */
2171 if (!dest->cache) folder_item_read_cache(dest);
2174 * Fetch new MsgInfos for new messages in dest folder,
2175 * add them to the msgcache and update folder message counts
2177 if (g_relation_count(relation, GINT_TO_POINTER(0), 1) > 0) {
2178 folder_item_scan_full(dest, FALSE);
2182 for (l = msglist; l != NULL; l = g_slist_next(l)) {
2183 MsgInfo *msginfo = (MsgInfo *) l->data;
2186 tuples = g_relation_select(relation, msginfo, 0);
2187 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
2188 g_tuples_destroy(tuples);
2191 MsgInfo *newmsginfo;
2194 if (msginfo->msgid != NULL) {
2195 newmsginfo = folder_item_get_msginfo_by_msgid(dest, msginfo->msgid);
2196 if (newmsginfo != NULL) {
2197 copy_msginfo_flags(msginfo, newmsginfo);
2198 num = newmsginfo->msgnum;
2199 procmsg_msginfo_free(newmsginfo);
2203 newmsginfo = folder->klass->get_msginfo(folder, dest, num);
2204 if (newmsginfo != NULL) {
2205 add_msginfo_to_cache(dest, newmsginfo, msginfo);
2206 procmsg_msginfo_free(newmsginfo);
2215 if (remove_source) {
2217 * Remove source messages from their folders if
2218 * copying was successfull and update folder
2221 for (l = msglist; l != NULL; l = g_slist_next(l)) {
2222 MsgInfo *msginfo = (MsgInfo *) l->data;
2223 FolderItem *item = msginfo->folder;
2226 tuples = g_relation_select(relation, msginfo, 0);
2227 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
2228 g_tuples_destroy(tuples);
2230 if ((num >= 0) && (item->folder->klass->remove_msg != NULL)) {
2231 item->folder->klass->remove_msg(item->folder,
2234 remove_msginfo_from_cache(item, msginfo);
2239 if (folder->klass->finished_copy)
2240 folder->klass->finished_copy(folder, dest);
2242 g_relation_destroy(relation);
2247 * Move a message to a new folder.
2249 * \param dest Destination folder
2250 * \param msginfo The message
2252 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
2256 g_return_val_if_fail(dest != NULL, -1);
2257 g_return_val_if_fail(msginfo != NULL, -1);
2259 list.data = msginfo;
2262 return do_copy_msgs(dest, &list, TRUE);
2266 * Move a list of messages to a new folder.
2268 * \param dest Destination folder
2269 * \param msglist List of messages
2271 gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
2273 g_return_val_if_fail(dest != NULL, -1);
2274 g_return_val_if_fail(msglist != NULL, -1);
2276 return do_copy_msgs(dest, msglist, TRUE);
2280 * Copy a message to a new folder.
2282 * \param dest Destination folder
2283 * \param msginfo The message
2285 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
2289 g_return_val_if_fail(dest != NULL, -1);
2290 g_return_val_if_fail(msginfo != NULL, -1);
2292 list.data = msginfo;
2295 return do_copy_msgs(dest, &list, FALSE);
2299 * Copy a list of messages to a new folder.
2301 * \param dest Destination folder
2302 * \param msglist List of messages
2304 gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
2306 g_return_val_if_fail(dest != NULL, -1);
2307 g_return_val_if_fail(msglist != NULL, -1);
2309 return do_copy_msgs(dest, msglist, FALSE);
2312 gint folder_item_remove_msg(FolderItem *item, gint num)
2318 g_return_val_if_fail(item != NULL, -1);
2319 folder = item->folder;
2320 g_return_val_if_fail(folder->klass->remove_msg != NULL, -1);
2322 if (!item->cache) folder_item_read_cache(item);
2324 ret = folder->klass->remove_msg(folder, item, num);
2326 msginfo = msgcache_get_msg(item->cache, num);
2327 if (msginfo != NULL) {
2328 remove_msginfo_from_cache(item, msginfo);
2329 procmsg_msginfo_free(msginfo);
2331 folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
2336 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
2341 g_return_val_if_fail(item != NULL, -1);
2342 folder = item->folder;
2343 g_return_val_if_fail(folder != NULL, -1);
2345 if (!item->cache) folder_item_read_cache(item);
2347 while (msglist != NULL) {
2348 MsgInfo *msginfo = (MsgInfo *)msglist->data;
2350 ret = folder_item_remove_msg(item, msginfo->msgnum);
2351 if (ret != 0) break;
2352 msgcache_remove_msg(item->cache, msginfo->msgnum);
2353 msglist = msglist->next;
2359 gint folder_item_remove_all_msg(FolderItem *item)
2364 g_return_val_if_fail(item != NULL, -1);
2366 folder = item->folder;
2368 g_return_val_if_fail(folder->klass->remove_all_msg != NULL, -1);
2370 result = folder->klass->remove_all_msg(folder, item);
2373 if (folder->klass->finished_remove)
2374 folder->klass->finished_remove(folder, item);
2376 folder_item_free_cache(item);
2377 item->cache = msgcache_new();
2380 item->unread_msgs = 0;
2381 item->unreadmarked_msgs = 0;
2382 item->total_msgs = 0;
2383 folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
2389 void folder_item_change_msg_flags(FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
2391 g_return_if_fail(item != NULL);
2392 g_return_if_fail(msginfo != NULL);
2394 if (item->folder->klass->change_flags != NULL) {
2395 item->folder->klass->change_flags(item->folder, item, msginfo, newflags);
2397 msginfo->flags.perm_flags = newflags;
2401 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
2405 g_return_val_if_fail(item != NULL, FALSE);
2407 folder = item->folder;
2409 g_return_val_if_fail(folder->klass->is_msg_changed != NULL, -1);
2411 return folder->klass->is_msg_changed(folder, item, msginfo);
2414 gchar *folder_item_get_cache_file(FolderItem *item)
2419 g_return_val_if_fail(item != NULL, NULL);
2420 g_return_val_if_fail(item->path != NULL, NULL);
2422 path = folder_item_get_path(item);
2423 g_return_val_if_fail(path != NULL, NULL);
2424 if (!is_dir_exist(path))
2425 make_dir_hier(path);
2426 file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
2432 gchar *folder_item_get_mark_file(FolderItem *item)
2437 g_return_val_if_fail(item != NULL, NULL);
2438 g_return_val_if_fail(item->path != NULL, NULL);
2440 path = folder_item_get_path(item);
2441 g_return_val_if_fail(path != NULL, NULL);
2442 if (!is_dir_exist(path))
2443 make_dir_hier(path);
2444 file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
2450 static gboolean folder_build_tree(GNode *node, gpointer data)
2452 Folder *folder = FOLDER(data);
2456 g_return_val_if_fail(node->data != NULL, FALSE);
2457 if (!node->parent) return FALSE;
2459 xmlnode = node->data;
2460 if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
2461 g_warning("tag name != \"folderitem\"\n");
2465 item = folder_item_new(folder, "", "");
2466 if (folder->klass->item_set_xml != NULL)
2467 folder->klass->item_set_xml(folder, item, xmlnode->tag);
2469 folder_item_set_xml(folder, item, xmlnode->tag);
2471 item->parent = FOLDER_ITEM(node->parent->data);
2472 item->folder = folder;
2473 switch (item->stype) {
2474 case F_INBOX: folder->inbox = item; break;
2475 case F_OUTBOX: folder->outbox = item; break;
2476 case F_DRAFT: folder->draft = item; break;
2477 case F_QUEUE: folder->queue = item; break;
2478 case F_TRASH: folder->trash = item; break;
2481 folder_item_prefs_read_config(item);
2484 xml_free_node(xmlnode);
2489 static gboolean folder_read_folder_func(GNode *node, gpointer data)
2495 FolderClass *klass = NULL;
2497 if (g_node_depth(node) != 2) return FALSE;
2498 g_return_val_if_fail(node->data != NULL, FALSE);
2500 xmlnode = node->data;
2501 if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
2502 g_warning("tag name != \"folder\"\n");
2505 g_node_unlink(node);
2506 list = xmlnode->tag->attr;
2507 for (; list != NULL; list = list->next) {
2508 XMLAttr *attr = list->data;
2510 if (!attr || !attr->name || !attr->value) continue;
2511 if (!strcmp(attr->name, "type"))
2512 klass = folder_get_class_from_string(attr->value);
2515 folder = folder_new(klass, "", "");
2516 g_return_val_if_fail(folder != NULL, FALSE);
2518 item = FOLDER_ITEM(folder->node->data);
2522 g_node_destroy(folder->node);
2523 folder->node = node;
2525 klass->set_xml(folder, xmlnode->tag);
2527 folder_set_xml(folder, xmlnode->tag);
2529 if (folder->klass->item_set_xml != NULL)
2530 folder->klass->item_set_xml(folder, item, xmlnode->tag);
2532 folder_item_set_xml(folder, item, xmlnode->tag);
2535 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2536 folder_build_tree, folder);
2541 static gchar *folder_get_list_path(void)
2543 static gchar *filename = NULL;
2546 filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2552 #define PUT_ESCAPE_STR(fp, attr, str) \
2554 fputs(" " attr "=\"", fp); \
2555 xml_file_put_escape_str(fp, str); \
2559 gint xml_attr_cmp_name(gconstpointer _a, gconstpointer _b)
2561 XMLAttr *a = (XMLAttr *) _a;
2562 XMLAttr *b = (XMLAttr *) _b;
2564 return g_str_equal(a->name, b->name) ? 0 : 1;
2567 static void folder_write_list_recursive(GNode *node, gpointer data)
2569 FILE *fp = (FILE *)data;
2575 g_return_if_fail(node != NULL);
2576 g_return_if_fail(fp != NULL);
2578 item = FOLDER_ITEM(node->data);
2579 g_return_if_fail(item != NULL);
2581 depth = g_node_depth(node);
2582 for (i = 0; i < depth; i++)
2585 XMLTag *folderitem_tag;
2588 if (item->folder->klass->get_xml != NULL)
2589 tag = item->folder->klass->get_xml(item->folder);
2591 tag = folder_get_xml(item->folder);
2593 if (item->folder->klass->item_get_xml != NULL)
2594 folderitem_tag = item->folder->klass->item_get_xml(item->folder, item);
2596 folderitem_tag = folder_item_get_xml(item->folder, item);
2597 xml_tag_add_attr(tag, "type", g_strdup(item->folder->klass->idstr));
2599 for (cur = folderitem_tag->attr; cur != NULL; cur = g_list_next(cur)) {
2600 XMLAttr *attr = (XMLAttr *) cur->data;
2602 if (g_list_find_custom(tag->attr, attr, xml_attr_cmp_name) == NULL)
2603 tag->attr = g_list_append(tag->attr, xml_copy_attr(attr));
2605 xml_free_tag(folderitem_tag);
2609 if (item->folder->klass->item_get_xml != NULL)
2610 tag = item->folder->klass->item_get_xml(item->folder, item);
2612 tag = folder_item_get_xml(item->folder, item);
2616 fprintf(fp, "<%s", tag->tag);
2617 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
2618 XMLAttr *attr = (XMLAttr *) cur->data;
2620 fprintf(fp, " %s=\"", attr->name);
2621 xml_file_put_escape_str(fp, attr->value);
2625 if (node->children) {
2629 child = node->children;
2635 folder_write_list_recursive(cur, data);
2638 for (i = 0; i < depth; i++)
2640 fprintf(fp, "</%s>\n", tag->tag);
2646 static void folder_update_op_count_rec(GNode *node)
2648 FolderItem *fitem = FOLDER_ITEM(node->data);
2650 if (g_node_depth(node) > 0) {
2651 if (fitem->op_count > 0) {
2652 fitem->op_count = 0;
2653 folder_item_update(fitem, F_ITEM_UPDATE_MSGCNT);
2655 if (node->children) {
2658 child = node->children;
2664 folder_update_op_count_rec(cur);
2670 void folder_update_op_count(void)
2675 for (cur = folder_list; cur != NULL; cur = cur->next) {
2677 folder_update_op_count_rec(folder->node);
2681 typedef struct _type_str {
2688 static gchar * folder_item_get_tree_identifier(FolderItem * item)
2690 if (item->parent != NULL) {
2694 path = folder_item_get_tree_identifier(item->parent);
2698 id = g_strconcat(path, "/", item->name, NULL);
2704 return g_strconcat("/", item->name, NULL);
2709 /* CLAWS: temporary local folder for filtering */
2710 #define TEMP_FOLDER "TEMP_FOLDER"
2711 #define PROCESSING_FOLDER_ITEM "processing"
2713 static FolderItem *processing_folder_item;
2715 static void folder_create_processing_folder(void)
2717 Folder *processing_folder;
2720 if ((processing_folder = folder_find_from_name(TEMP_FOLDER, mh_get_class())) == NULL) {
2724 g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2725 "tempfolder", NULL);
2727 folder_new(mh_get_class(), TEMP_FOLDER, tmppath);
2730 g_assert(processing_folder != NULL);
2732 debug_print("tmpparentroot %s\n", LOCAL_FOLDER(processing_folder)->rootpath);
2733 if (LOCAL_FOLDER(processing_folder)->rootpath[0] == '/')
2734 tmpname = g_strconcat(LOCAL_FOLDER(processing_folder)->rootpath,
2735 G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
2738 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2739 LOCAL_FOLDER(processing_folder)->rootpath,
2740 G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
2743 if (!is_dir_exist(tmpname)) {
2744 debug_print("*TMP* creating %s\n", tmpname);
2745 processing_folder_item = processing_folder->klass->create_folder(processing_folder,
2746 processing_folder->node->data,
2747 PROCESSING_FOLDER_ITEM);
2749 debug_print("*TMP* already created\n");
2750 processing_folder_item = folder_item_new(processing_folder, PROCESSING_FOLDER_ITEM, PROCESSING_FOLDER_ITEM);
2751 g_assert(processing_folder_item);
2752 folder_item_append(processing_folder->node->data, processing_folder_item);
2754 g_assert(processing_folder_item != NULL);
2758 FolderItem *folder_get_default_processing(void)
2760 if (!processing_folder_item) {
2761 folder_create_processing_folder();
2763 return processing_folder_item;
2766 /* folder_persist_prefs_new() - return hash table with persistent
2767 * settings (and folder name as key).
2768 * (note that in claws other options are in the folder_item_prefs_RC
2769 * file, so those don't need to be included in PersistPref yet)
2771 GHashTable *folder_persist_prefs_new(Folder *folder)
2773 GHashTable *pptable;
2775 g_return_val_if_fail(folder, NULL);
2776 pptable = g_hash_table_new(g_str_hash, g_str_equal);
2777 folder_get_persist_prefs_recursive(folder->node, pptable);
2781 void folder_persist_prefs_free(GHashTable *pptable)
2783 g_return_if_fail(pptable);
2784 g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
2785 g_hash_table_destroy(pptable);
2788 const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
2790 if (pptable == NULL || name == NULL) return NULL;
2791 return g_hash_table_lookup(pptable, name);
2794 void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
2796 const PersistPrefs *pp;
2797 gchar *id = folder_item_get_identifier(item);
2799 pp = folder_get_persist_prefs(pptable, id);
2804 /* CLAWS: since not all folder properties have been migrated to
2805 * folderlist.xml, we need to call the old stuff first before
2806 * setting things that apply both to Main and Claws. */
2807 folder_item_prefs_read_config(item);
2809 item->collapsed = pp->collapsed;
2810 item->thread_collapsed = pp->thread_collapsed;
2811 item->threaded = pp->threaded;
2812 item->ret_rcpt = pp->ret_rcpt;
2813 item->hide_read_msgs = pp->hide_read_msgs;
2814 item->sort_key = pp->sort_key;
2815 item->sort_type = pp->sort_type;
2818 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
2820 FolderItem *item = FOLDER_ITEM(node->data);
2825 g_return_if_fail(node != NULL);
2826 g_return_if_fail(item != NULL);
2828 /* NOTE: item->path == NULL means top level folder; not interesting
2829 * to store preferences of that one. */
2831 id = folder_item_get_identifier(item);
2832 pp = g_new0(PersistPrefs, 1);
2833 g_return_if_fail(pp != NULL);
2834 pp->collapsed = item->collapsed;
2835 pp->thread_collapsed = item->thread_collapsed;
2836 pp->threaded = item->threaded;
2837 pp->ret_rcpt = item->ret_rcpt;
2838 pp->hide_read_msgs = item->hide_read_msgs;
2839 pp->sort_key = item->sort_key;
2840 pp->sort_type = item->sort_type;
2841 g_hash_table_insert(pptable, id, pp);
2844 if (node->children) {
2845 child = node->children;
2849 folder_get_persist_prefs_recursive(cur, pptable);
2854 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
2863 void folder_item_apply_processing(FolderItem *item)
2865 GSList *processing_list;
2866 GSList *mlist, *cur;
2868 g_return_if_fail(item != NULL);
2870 processing_list = item->prefs->processing;
2871 if (processing_list == NULL)
2874 folder_item_update_freeze();
2876 mlist = folder_item_get_msg_list(item);
2877 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2880 msginfo = (MsgInfo *) cur->data;
2881 filter_message_by_msginfo(processing_list, msginfo);
2882 procmsg_msginfo_free(msginfo);
2884 g_slist_free(mlist);
2886 folder_item_update_thaw();
2890 * functions for handling FolderItem content changes
2892 static gint folder_item_update_freeze_cnt = 0;
2895 * Notify the folder system about changes to a folder. If the
2896 * update system is not frozen the FOLDER_ITEM_UPDATE_HOOKLIST will
2897 * be invoked, otherwise the changes will be remebered until
2898 * the folder system is thawed.
2900 * \param item The FolderItem that was changed
2901 * \param update_flags Type of changed that was made
2903 void folder_item_update(FolderItem *item, FolderItemUpdateFlags update_flags)
2905 if (folder_item_update_freeze_cnt == 0) {
2906 FolderItemUpdateData source;
2909 source.update_flags = update_flags;
2910 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
2912 item->update_flags |= update_flags;
2916 void folder_item_update_recursive(FolderItem *item, FolderItemUpdateFlags update_flags)
2918 GNode *node = item->folder->node;
2920 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2921 node = node->children;
2923 folder_item_update(item, update_flags);
2924 while (node != NULL) {
2925 if (node && node->data) {
2926 FolderItem *next_item = (FolderItem*) node->data;
2928 folder_item_update(next_item, update_flags);
2934 void folder_item_update_freeze(void)
2936 folder_item_update_freeze_cnt++;
2939 static void folder_item_update_func(FolderItem *item, gpointer data)
2941 FolderItemUpdateData source;
2943 if (item->update_flags) {
2945 source.update_flags = item->update_flags;
2946 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
2947 item->update_flags = 0;
2951 void folder_item_update_thaw(void)
2953 if (folder_item_update_freeze_cnt > 0)
2954 folder_item_update_freeze_cnt--;
2955 if (folder_item_update_freeze_cnt == 0) {
2956 /* Update all folders */
2957 folder_func_to_all_folders(folder_item_update_func, NULL);
2961 #undef PUT_ESCAPE_STR