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 FolderItem *folder_item_new(Folder *folder, const gchar *name, const gchar *path)
148 FolderItem *item = NULL;
150 if (folder->klass->item_new) {
151 item = folder->klass->item_new(folder);
153 item = g_new0(FolderItem, 1);
156 g_return_val_if_fail(item != NULL, NULL);
158 item->stype = F_NORMAL;
159 item->name = g_strdup(name);
160 item->path = g_strdup(path);
163 item->unread_msgs = 0;
164 item->unreadmarked_msgs = 0;
165 item->total_msgs = 0;
168 item->no_sub = FALSE;
169 item->no_select = FALSE;
170 item->collapsed = FALSE;
171 item->thread_collapsed = FALSE;
172 item->threaded = TRUE;
173 item->ret_rcpt = FALSE;
174 item->opened = FALSE;
178 item->account = NULL;
179 item->apply_sub = FALSE;
180 item->mark_queue = NULL;
183 item->prefs = folder_item_prefs_new();
188 void folder_item_append(FolderItem *parent, FolderItem *item)
190 g_return_if_fail(parent != NULL);
191 g_return_if_fail(parent->folder != NULL);
192 g_return_if_fail(parent->node != NULL);
193 g_return_if_fail(item != NULL);
195 item->parent = parent;
196 item->folder = parent->folder;
197 item->node = g_node_append_data(parent->node, item);
200 static gboolean folder_item_remove_func(GNode *node, gpointer data)
202 FolderItem *item = FOLDER_ITEM(node->data);
203 FolderUpdateData hookdata;
205 if (item->cache != NULL) {
206 msgcache_destroy(item->cache);
210 hookdata.folder = item->folder;
211 hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_REMOVE_FOLDERITEM;
212 hookdata.item = item;
213 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
215 folder_item_destroy(item);
220 void folder_item_remove(FolderItem *item)
224 g_return_if_fail(item != NULL);
225 g_return_if_fail(item->folder != NULL);
226 g_return_if_fail(item->node != NULL);
230 if (item->folder->node == node)
231 item->folder->node = NULL;
233 g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
234 folder_item_remove_func, NULL);
235 g_node_destroy(node);
238 void folder_item_remove_children(FolderItem *item)
242 g_return_if_fail(item != NULL);
243 g_return_if_fail(item->folder != NULL);
244 g_return_if_fail(item->node != NULL);
246 node = item->node->children;
247 while (node != NULL) {
249 folder_item_remove(FOLDER_ITEM(node->data));
254 void folder_item_destroy(FolderItem *item)
258 g_return_if_fail(item != NULL);
260 folder = item->folder;
262 if (folder->inbox == item)
263 folder->inbox = NULL;
264 else if (folder->outbox == item)
265 folder->outbox = NULL;
266 else if (folder->draft == item)
267 folder->draft = NULL;
268 else if (folder->queue == item)
269 folder->queue = NULL;
270 else if (folder->trash == item)
271 folder->trash = NULL;
275 folder_item_free_cache(item);
277 folder_item_prefs_free(item->prefs);
281 if (item->folder != NULL) {
282 if(item->folder->klass->item_destroy) {
283 item->folder->klass->item_destroy(item->folder, item);
290 static void add_xml_attr(XMLTag *tag, const gchar *name, gchar *value)
294 attr = g_new0(XMLAttr, 1);
295 attr->name = g_strdup(name);
298 tag->attr = g_list_append(tag->attr, attr);
301 void folder_item_set_attrs(Folder *folder, FolderItem *item, XMLTag *tag)
305 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
306 XMLAttr *attr = (XMLAttr *) cur->data;
308 if (!attr || !attr->name || !attr->value) continue;
309 if (!strcmp(attr->name, "type")) {
310 if (!strcasecmp(attr->value, "normal"))
311 item->stype = F_NORMAL;
312 else if (!strcasecmp(attr->value, "inbox"))
313 item->stype = F_INBOX;
314 else if (!strcasecmp(attr->value, "outbox"))
315 item->stype = F_OUTBOX;
316 else if (!strcasecmp(attr->value, "draft"))
317 item->stype = F_DRAFT;
318 else if (!strcasecmp(attr->value, "queue"))
319 item->stype = F_QUEUE;
320 else if (!strcasecmp(attr->value, "trash"))
321 item->stype = F_TRASH;
322 } else if (!strcmp(attr->name, "name")) {
323 if (item->name != NULL)
325 item->name = g_strdup(attr->value);
326 } else if (!strcmp(attr->name, "path")) {
327 if (item->path != NULL)
329 item->path = g_strdup(attr->value);
330 } else if (!strcmp(attr->name, "mtime"))
331 item->mtime = strtoul(attr->value, NULL, 10);
332 else if (!strcmp(attr->name, "new"))
333 item->new_msgs = atoi(attr->value);
334 else if (!strcmp(attr->name, "unread"))
335 item->unread_msgs = atoi(attr->value);
336 else if (!strcmp(attr->name, "unreadmarked"))
337 item->unreadmarked_msgs = atoi(attr->value);
338 else if (!strcmp(attr->name, "total"))
339 item->total_msgs = atoi(attr->value);
340 else if (!strcmp(attr->name, "no_sub"))
341 item->no_sub = *attr->value == '1' ? TRUE : FALSE;
342 else if (!strcmp(attr->name, "no_select"))
343 item->no_select = *attr->value == '1' ? TRUE : FALSE;
344 else if (!strcmp(attr->name, "collapsed"))
345 item->collapsed = *attr->value == '1' ? TRUE : FALSE;
346 else if (!strcmp(attr->name, "thread_collapsed"))
347 item->thread_collapsed = *attr->value == '1' ? TRUE : FALSE;
348 else if (!strcmp(attr->name, "threaded"))
349 item->threaded = *attr->value == '1' ? TRUE : FALSE;
350 else if (!strcmp(attr->name, "hidereadmsgs"))
351 item->hide_read_msgs = *attr->value == '1' ? TRUE : FALSE;
352 else if (!strcmp(attr->name, "reqretrcpt"))
353 item->ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
354 else if (!strcmp(attr->name, "sort_key")) {
355 if (!strcmp(attr->value, "none"))
356 item->sort_key = SORT_BY_NONE;
357 else if (!strcmp(attr->value, "number"))
358 item->sort_key = SORT_BY_NUMBER;
359 else if (!strcmp(attr->value, "size"))
360 item->sort_key = SORT_BY_SIZE;
361 else if (!strcmp(attr->value, "date"))
362 item->sort_key = SORT_BY_DATE;
363 else if (!strcmp(attr->value, "from"))
364 item->sort_key = SORT_BY_FROM;
365 else if (!strcmp(attr->value, "subject"))
366 item->sort_key = SORT_BY_SUBJECT;
367 else if (!strcmp(attr->value, "score"))
368 item->sort_key = SORT_BY_SCORE;
369 else if (!strcmp(attr->value, "label"))
370 item->sort_key = SORT_BY_LABEL;
371 else if (!strcmp(attr->value, "mark"))
372 item->sort_key = SORT_BY_MARK;
373 else if (!strcmp(attr->value, "unread"))
374 item->sort_key = SORT_BY_STATUS;
375 else if (!strcmp(attr->value, "mime"))
376 item->sort_key = SORT_BY_MIME;
377 else if (!strcmp(attr->value, "to"))
378 item->sort_key = SORT_BY_TO;
379 else if (!strcmp(attr->value, "locked"))
380 item->sort_key = SORT_BY_LOCKED;
381 } else if (!strcmp(attr->name, "sort_type")) {
382 if (!strcmp(attr->value, "ascending"))
383 item->sort_type = SORT_ASCENDING;
385 item->sort_type = SORT_DESCENDING;
386 } else if (!strcmp(attr->name, "account_id")) {
387 PrefsAccount *account;
389 account = account_find_from_id(atoi(attr->value));
391 g_warning("account_id: %s not found\n", attr->value);
393 item->account = account;
394 } else if (!strcmp(attr->name, "apply_sub"))
395 item->apply_sub = *attr->value == '1' ? TRUE : FALSE;
399 XMLTag *folder_item_get_attrs(Folder *folder, FolderItem *item)
401 static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
402 "draft", "queue", "trash"};
403 static gchar *sort_key_str[] = {"none", "number", "size", "date",
404 "from", "subject", "score", "label",
405 "mark", "unread", "mime", "to",
409 tag = g_new0(XMLTag, 1);
410 tag->tag = g_strdup("folderitem");
412 add_xml_attr(tag, "type", g_strdup(folder_item_stype_str[item->stype]));
414 add_xml_attr(tag, "name", g_strdup(item->name));
416 add_xml_attr(tag, "path", g_strdup(item->path));
418 add_xml_attr(tag, "no_sub", g_strdup("1"));
420 add_xml_attr(tag, "no_select", g_strdup("1"));
421 add_xml_attr(tag, "collapsed", g_strdup(item->collapsed && item->node->children ? "1" : "0"));
422 add_xml_attr(tag, "thread_collapsed", g_strdup(item->thread_collapsed ? "1" : "0"));
423 add_xml_attr(tag, "threaded", g_strdup(item->threaded ? "1" : "0"));
424 add_xml_attr(tag, "hidereadmsgs", g_strdup(item->hide_read_msgs ? "1" : "0"));
426 add_xml_attr(tag, "reqretrcpt", g_strdup("1"));
428 if (item->sort_key != SORT_BY_NONE) {
429 add_xml_attr(tag, "sort_key", g_strdup(sort_key_str[item->sort_key]));
430 add_xml_attr(tag, "sort_type", g_strdup(item->sort_type == SORT_ASCENDING ? "ascending" : "descending"));
433 add_xml_attr(tag, "mtime", g_strdup_printf("%ld", (unsigned long int) item->mtime));
434 add_xml_attr(tag, "new", g_strdup_printf("%d", item->new_msgs));
435 add_xml_attr(tag, "unread", g_strdup_printf("%d", item->unread_msgs));
436 add_xml_attr(tag, "unreadmarked", g_strdup_printf("%d", item->unreadmarked_msgs));
437 add_xml_attr(tag, "total", g_strdup_printf("%d", item->total_msgs));
440 add_xml_attr(tag, "account_id", g_strdup_printf("%d", item->account->account_id));
442 add_xml_attr(tag, "apply_sub", g_strdup("1"));
447 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
449 g_return_if_fail(folder != NULL);
451 folder->ui_func = func;
452 folder->ui_func_data = data;
455 void folder_set_name(Folder *folder, const gchar *name)
457 g_return_if_fail(folder != NULL);
459 g_free(folder->name);
460 folder->name = name ? g_strdup(name) : NULL;
461 if (folder->node && folder->node->data) {
462 FolderItem *item = (FolderItem *)folder->node->data;
465 item->name = name ? g_strdup(name) : NULL;
469 gboolean folder_tree_destroy_func(GNode *node, gpointer data) {
470 FolderItem *item = (FolderItem *) node->data;
472 folder_item_destroy(item);
476 void folder_tree_destroy(Folder *folder)
480 g_return_if_fail(folder != NULL);
484 prefs_scoring_clear_folder(folder);
485 prefs_filtering_clear_folder(folder);
488 g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
489 folder_tree_destroy_func, NULL);
490 g_node_destroy(node);
495 void folder_add(Folder *folder)
501 g_return_if_fail(folder != NULL);
503 for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
504 cur_folder = FOLDER(cur->data);
505 if (FOLDER_TYPE(folder) == F_MH) {
506 if (FOLDER_TYPE(cur_folder) != F_MH) break;
507 } else if (FOLDER_TYPE(folder) == F_MBOX) {
508 if (FOLDER_TYPE(cur_folder) != F_MH &&
509 FOLDER_TYPE(cur_folder) != F_MBOX) break;
510 } else if (FOLDER_TYPE(folder) == F_IMAP) {
511 if (FOLDER_TYPE(cur_folder) != F_MH &&
512 FOLDER_TYPE(cur_folder) != F_MBOX &&
513 FOLDER_TYPE(cur_folder) != F_IMAP) break;
514 } else if (FOLDER_TYPE(folder) == F_NEWS) {
515 if (FOLDER_TYPE(cur_folder) != F_MH &&
516 FOLDER_TYPE(cur_folder) != F_MBOX &&
517 FOLDER_TYPE(cur_folder) != F_IMAP &&
518 FOLDER_TYPE(cur_folder) != F_NEWS) break;
522 folder_list = g_list_insert(folder_list, folder, i);
525 GList *folder_get_list(void)
530 gint folder_read_list(void)
536 path = folder_get_list_path();
537 if (!is_file_exist(path)) return -1;
538 node = xml_parse_file(path);
539 if (!node) return -1;
541 xmlnode = node->data;
542 if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
543 g_warning("wrong folder list\n");
548 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
549 folder_read_folder_func, NULL);
558 void folder_write_list(void)
565 path = folder_get_list_path();
566 if ((pfile = prefs_write_open(path)) == NULL) return;
568 fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
569 conv_get_current_charset_str());
570 fputs("\n<folderlist>\n", pfile->fp);
572 for (list = folder_list; list != NULL; list = list->next) {
574 folder_write_list_recursive(folder->node, pfile->fp);
577 fputs("</folderlist>\n", pfile->fp);
579 if (prefs_file_close(pfile) < 0)
580 g_warning("failed to write folder list.\n");
583 gboolean folder_scan_tree_func(GNode *node, gpointer data)
585 GHashTable *pptable = (GHashTable *)data;
586 FolderItem *item = (FolderItem *)node->data;
588 folder_item_restore_persist_prefs(item, pptable);
589 folder_item_scan_full(item, FALSE);
594 void folder_scan_tree(Folder *folder)
597 FolderUpdateData hookdata;
599 if (!folder->klass->scan_tree)
602 pptable = folder_persist_prefs_new(folder);
605 * should be changed and tree update should be done without
606 * destroying the tree first
608 folder_tree_destroy(folder);
609 folder->klass->scan_tree(folder);
611 hookdata.folder = folder;
612 hookdata.update_flags = FOLDER_TREE_CHANGED;
613 hookdata.item = NULL;
614 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
616 g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_scan_tree_func, pptable);
617 folder_persist_prefs_free(pptable);
619 prefs_matcher_read_config();
624 FolderItem *folder_create_folder(FolderItem *parent, const gchar *name)
626 FolderItem *new_item;
627 FolderUpdateData hookdata;
629 new_item = parent->folder->klass->create_folder(parent->folder, parent, name);
631 new_item->cache = msgcache_new();
633 hookdata.folder = new_item->folder;
634 hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_NEW_FOLDERITEM;
635 hookdata.item = new_item;
636 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
646 guint unreadmarked_msgs;
650 struct FuncToAllFoldersData
652 FolderItemFunc function;
656 static gboolean folder_func_to_all_folders_func(GNode *node, gpointer data)
659 struct FuncToAllFoldersData *function_data = (struct FuncToAllFoldersData *) data;
661 g_return_val_if_fail(node->data != NULL, FALSE);
663 item = FOLDER_ITEM(node->data);
664 g_return_val_if_fail(item != NULL, FALSE);
666 function_data->function(item, function_data->data);
671 void folder_func_to_all_folders(FolderItemFunc function, gpointer data)
675 struct FuncToAllFoldersData function_data;
677 function_data.function = function;
678 function_data.data = data;
680 for (list = folder_list; list != NULL; list = list->next) {
681 folder = FOLDER(list->data);
683 g_node_traverse(folder->node, G_PRE_ORDER,
685 folder_func_to_all_folders_func,
690 static void folder_count_total_msgs_func(FolderItem *item, gpointer data)
692 struct TotalMsgCount *count = (struct TotalMsgCount *)data;
694 count->new_msgs += item->new_msgs;
695 count->unread_msgs += item->unread_msgs;
696 count->unreadmarked_msgs += item->unreadmarked_msgs;
697 count->total_msgs += item->total_msgs;
700 struct TotalMsgStatus
708 static gboolean folder_get_status_full_all_func(GNode *node, gpointer data)
711 struct TotalMsgStatus *status = (struct TotalMsgStatus *)data;
714 g_return_val_if_fail(node->data != NULL, FALSE);
716 item = FOLDER_ITEM(node->data);
718 if (!item->path) return FALSE;
720 status->new += item->new_msgs;
721 status->unread += item->unread_msgs;
722 status->total += item->total_msgs;
725 id = folder_item_get_identifier(item);
726 g_string_sprintfa(status->str, "%5d %5d %5d %s\n",
727 item->new_msgs, item->unread_msgs,
728 item->total_msgs, id);
735 static void folder_get_status_full_all(GString *str, guint *new, guint *unread,
740 struct TotalMsgStatus status;
742 status.new = status.unread = status.total = 0;
745 debug_print("Counting total number of messages...\n");
747 for (list = folder_list; list != NULL; list = list->next) {
748 folder = FOLDER(list->data);
750 g_node_traverse(folder->node, G_PRE_ORDER,
752 folder_get_status_full_all_func,
757 *unread = status.unread;
758 *total = status.total;
761 gchar *folder_get_status(GPtrArray *folders, gboolean full)
763 guint new, unread, total;
768 new = unread = total = 0;
770 str = g_string_new(NULL);
773 for (i = 0; i < folders->len; i++) {
776 item = g_ptr_array_index(folders, i);
777 new += item->new_msgs;
778 unread += item->unread_msgs;
779 total += item->total_msgs;
784 id = folder_item_get_identifier(item);
785 g_string_sprintfa(str, "%5d %5d %5d %s\n",
786 item->new_msgs, item->unread_msgs,
787 item->total_msgs, id);
792 folder_get_status_full_all(full ? str : NULL,
793 &new, &unread, &total);
797 g_string_sprintfa(str, "%5d %5d %5d\n", new, unread, total);
799 g_string_sprintfa(str, "%d %d %d\n", new, unread, total);
802 g_string_free(str, FALSE);
807 void folder_count_total_msgs(guint *new_msgs, guint *unread_msgs, guint *unreadmarked_msgs, guint *total_msgs)
809 struct TotalMsgCount count;
811 count.new_msgs = count.unread_msgs = count.unreadmarked_msgs = count.total_msgs = 0;
813 debug_print("Counting total number of messages...\n");
815 folder_func_to_all_folders(folder_count_total_msgs_func, &count);
817 *new_msgs = count.new_msgs;
818 *unread_msgs = count.unread_msgs;
819 *unreadmarked_msgs = count.unreadmarked_msgs;
820 *total_msgs = count.total_msgs;
823 Folder *folder_find_from_path(const gchar *path)
828 for (list = folder_list; list != NULL; list = list->next) {
830 if ((FOLDER_TYPE(folder) == F_MH ||
831 FOLDER_TYPE(folder) == F_MBOX) &&
832 !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
839 Folder *folder_find_from_name(const gchar *name, FolderClass *klass)
844 for (list = folder_list; list != NULL; list = list->next) {
846 if (folder->klass == klass &&
847 strcmp2(name, folder->name) == 0)
854 static gboolean folder_item_find_func(GNode *node, gpointer data)
856 FolderItem *item = node->data;
858 const gchar *path = d[0];
860 if (path_cmp(path, item->path) != 0)
868 FolderItem *folder_find_item_from_path(const gchar *path)
873 folder = folder_get_default_folder();
874 g_return_val_if_fail(folder != NULL, NULL);
876 d[0] = (gpointer)path;
878 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
879 folder_item_find_func, d);
883 FolderClass *folder_get_class_from_string(const gchar *str)
887 classlist = folder_get_class_list();
888 for (; classlist != NULL; classlist = g_slist_next(classlist)) {
889 FolderClass *class = (FolderClass *) classlist->data;
890 if (g_strcasecmp(class->idstr, str) == 0)
897 gchar *folder_get_identifier(Folder *folder)
901 g_return_val_if_fail(folder != NULL, NULL);
903 type_str = folder->klass->idstr;
904 return g_strconcat("#", type_str, "/", folder->name, NULL);
907 gchar *folder_item_get_identifier(FolderItem *item)
912 g_return_val_if_fail(item != NULL, NULL);
913 g_return_val_if_fail(item->path != NULL, NULL);
915 folder_id = folder_get_identifier(item->folder);
916 id = g_strconcat(folder_id, "/", item->path, NULL);
922 FolderItem *folder_find_item_from_identifier(const gchar *identifier)
932 g_return_val_if_fail(identifier != NULL, NULL);
934 if (*identifier != '#')
935 return folder_find_item_from_path(identifier);
937 Xstrdup_a(str, identifier, return NULL);
939 p = strchr(str, '/');
941 return folder_find_item_from_path(identifier);
944 class = folder_get_class_from_string(&str[1]);
946 return folder_find_item_from_path(identifier);
951 return folder_find_item_from_path(identifier);
955 folder = folder_find_from_name(name, class);
957 return folder_find_item_from_path(identifier);
961 d[0] = (gpointer)path;
963 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
964 folder_item_find_func, d);
969 * Get a displayable name for a FolderItem
971 * \param item FolderItem for that a name should be created
972 * \return Displayable name for item, returned string has to
975 gchar *folder_item_get_name(FolderItem *item)
979 g_return_val_if_fail(item != NULL, g_strdup(""));
981 switch (item->stype) {
983 name = g_strdup(!strcmp2(item->name, INBOX_DIR) ? _("Inbox") :
987 name = g_strdup(!strcmp2(item->name, OUTBOX_DIR) ? _("Sent") :
991 name = g_strdup(!strcmp2(item->name, QUEUE_DIR) ? _("Queue") :
995 name = g_strdup(!strcmp2(item->name, TRASH_DIR) ? _("Trash") :
999 name = g_strdup(!strcmp2(item->name, DRAFT_DIR) ? _("Drafts") :
1008 * should probably be done by a virtual function,
1009 * the folder knows the ui string and how to abbrev
1011 if (!item->parent) {
1012 name = g_strconcat(item->name, " (", item->folder->klass->uistr, ")", NULL);
1014 if (FOLDER_CLASS(item->folder) == news_get_class() &&
1015 item->path && !strcmp2(item->name, item->path))
1016 name = get_abbrev_newsgroup_name
1018 prefs_common.ng_abbrev_len);
1020 name = g_strdup(item->name);
1025 name = g_strdup("");
1030 Folder *folder_get_default_folder(void)
1032 return folder_list ? FOLDER(folder_list->data) : NULL;
1035 FolderItem *folder_get_default_inbox(void)
1039 if (!folder_list) return NULL;
1040 folder = FOLDER(folder_list->data);
1041 g_return_val_if_fail(folder != NULL, NULL);
1042 return folder->inbox;
1045 FolderItem *folder_get_default_outbox(void)
1049 if (!folder_list) return NULL;
1050 folder = FOLDER(folder_list->data);
1051 g_return_val_if_fail(folder != NULL, NULL);
1052 return folder->outbox;
1055 FolderItem *folder_get_default_draft(void)
1059 if (!folder_list) return NULL;
1060 folder = FOLDER(folder_list->data);
1061 g_return_val_if_fail(folder != NULL, NULL);
1062 return folder->draft;
1065 FolderItem *folder_get_default_queue(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->queue;
1075 FolderItem *folder_get_default_trash(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->trash;
1085 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type) \
1087 if (!folder->member) { \
1088 item = folder_item_new(folder, dir, dir); \
1089 item->stype = type; \
1090 folder_item_append(rootitem, item); \
1091 folder->member = item; \
1095 void folder_set_missing_folders(void)
1098 FolderItem *rootitem;
1102 for (list = folder_list; list != NULL; list = list->next) {
1103 folder = list->data;
1104 if (FOLDER_TYPE(folder) != F_MH) continue;
1105 rootitem = FOLDER_ITEM(folder->node->data);
1106 g_return_if_fail(rootitem != NULL);
1108 if (folder->inbox && folder->outbox && folder->draft &&
1109 folder->queue && folder->trash)
1112 if (folder->klass->create_tree(folder) < 0) {
1113 g_warning("%s: can't create the folder tree.\n",
1114 LOCAL_FOLDER(folder)->rootpath);
1118 CREATE_FOLDER_IF_NOT_EXIST(inbox, INBOX_DIR, F_INBOX);
1119 CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
1120 CREATE_FOLDER_IF_NOT_EXIST(draft, DRAFT_DIR, F_DRAFT);
1121 CREATE_FOLDER_IF_NOT_EXIST(queue, QUEUE_DIR, F_QUEUE);
1122 CREATE_FOLDER_IF_NOT_EXIST(trash, TRASH_DIR, F_TRASH);
1126 static gboolean folder_unref_account_func(GNode *node, gpointer data)
1128 FolderItem *item = node->data;
1129 PrefsAccount *account = data;
1131 if (item->account == account)
1132 item->account = NULL;
1137 void folder_unref_account_all(PrefsAccount *account)
1142 if (!account) return;
1144 for (list = folder_list; list != NULL; list = list->next) {
1145 folder = list->data;
1146 if (folder->account == account)
1147 folder->account = NULL;
1148 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1149 folder_unref_account_func, account);
1153 #undef CREATE_FOLDER_IF_NOT_EXIST
1155 gchar *folder_item_get_path(FolderItem *item)
1159 g_return_val_if_fail(item != NULL, NULL);
1160 folder = item->folder;
1161 g_return_val_if_fail(folder != NULL, NULL);
1163 return folder->klass->item_get_path(folder, item);
1166 void folder_item_set_default_flags(FolderItem *dest, MsgFlags *flags)
1168 if (!(dest->stype == F_OUTBOX ||
1169 dest->stype == F_QUEUE ||
1170 dest->stype == F_DRAFT ||
1171 dest->stype == F_TRASH)) {
1172 flags->perm_flags = MSG_NEW|MSG_UNREAD;
1174 flags->perm_flags = 0;
1176 flags->tmp_flags = MSG_CACHED;
1177 if (FOLDER_TYPE(dest->folder) == F_MH) {
1178 if (dest->stype == F_QUEUE) {
1179 MSG_SET_TMP_FLAGS(*flags, MSG_QUEUED);
1180 } else if (dest->stype == F_DRAFT) {
1181 MSG_SET_TMP_FLAGS(*flags, MSG_DRAFT);
1186 static gint folder_sort_cache_list_by_msgnum(gconstpointer a, gconstpointer b)
1188 MsgInfo *msginfo_a = (MsgInfo *) a;
1189 MsgInfo *msginfo_b = (MsgInfo *) b;
1191 return (msginfo_a->msgnum - msginfo_b->msgnum);
1194 static gint folder_sort_folder_list(gconstpointer a, gconstpointer b)
1196 guint gint_a = GPOINTER_TO_INT(a);
1197 guint gint_b = GPOINTER_TO_INT(b);
1199 return (gint_a - gint_b);
1202 gint folder_item_open(FolderItem *item)
1204 if((item->folder->klass->scan_required != NULL) && (item->folder->klass->scan_required(item->folder, item))) {
1205 folder_item_scan_full(item, TRUE);
1209 if(item->prefs->processing != NULL) {
1212 buf = g_strdup_printf(_("Processing (%s)...\n"), item->path);
1213 debug_print("%s\n", buf);
1216 folder_item_apply_processing(item);
1218 debug_print("done.\n");
1224 gint folder_item_close(FolderItem *item)
1226 GSList *mlist, *cur;
1229 g_return_val_if_fail(item != NULL, -1);
1231 if (item->new_msgs) {
1232 folder_item_update_freeze();
1233 mlist = folder_item_get_msg_list(item);
1234 for (cur = mlist ; cur != NULL ; cur = cur->next) {
1237 msginfo = (MsgInfo *) cur->data;
1238 if (MSG_IS_NEW(msginfo->flags))
1239 procmsg_msginfo_unset_flags(msginfo, MSG_NEW, 0);
1240 procmsg_msginfo_free(msginfo);
1242 g_slist_free(mlist);
1243 folder_item_update_thaw();
1246 folder_item_write_cache(item);
1248 folder_item_update(item, F_ITEM_UPDATE_MSGCNT);
1250 item->opened = FALSE;
1251 folder = item->folder;
1253 if (folder->klass->close == NULL)
1256 return folder->klass->close(folder, item);
1259 gint folder_item_scan_full(FolderItem *item, gboolean filtering)
1262 GSList *folder_list = NULL, *cache_list = NULL;
1263 GSList *folder_list_cur, *cache_list_cur, *new_list = NULL;
1264 GSList *exists_list = NULL, *elem;
1265 GSList *newmsg_list = NULL;
1266 guint newcnt = 0, unreadcnt = 0, totalcnt = 0, unreadmarkedcnt = 0;
1267 guint cache_max_num, folder_max_num, cache_cur_num, folder_cur_num;
1268 gboolean update_flags = 0, old_uids_valid = FALSE;
1270 g_return_val_if_fail(item != NULL, -1);
1271 if (item->path == NULL) return -1;
1273 folder = item->folder;
1275 g_return_val_if_fail(folder != NULL, -1);
1276 g_return_val_if_fail(folder->klass->get_num_list != NULL, -1);
1278 debug_print("Scanning folder %s for cache changes.\n", item->path);
1280 /* Get list of messages for folder and cache */
1281 if (folder->klass->get_num_list(item->folder, item, &folder_list, &old_uids_valid) < 0) {
1282 debug_print("Error fetching list of message numbers\n");
1286 if (old_uids_valid) {
1288 folder_item_read_cache(item);
1289 cache_list = msgcache_get_msg_list(item->cache);
1292 msgcache_destroy(item->cache);
1293 item->cache = msgcache_new();
1297 /* Sort both lists */
1298 cache_list = g_slist_sort(cache_list, folder_sort_cache_list_by_msgnum);
1299 folder_list = g_slist_sort(folder_list, folder_sort_folder_list);
1301 cache_list_cur = cache_list;
1302 folder_list_cur = folder_list;
1304 if (cache_list_cur != NULL) {
1305 GSList *cache_list_last;
1307 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1308 cache_list_last = g_slist_last(cache_list);
1309 cache_max_num = ((MsgInfo *)cache_list_last->data)->msgnum;
1311 cache_cur_num = G_MAXINT;
1315 if (folder_list_cur != NULL) {
1316 GSList *folder_list_last;
1318 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1319 folder_list_last = g_slist_last(folder_list);
1320 folder_max_num = GPOINTER_TO_INT(folder_list_last->data);
1322 folder_cur_num = G_MAXINT;
1326 while ((cache_cur_num != G_MAXINT) || (folder_cur_num != G_MAXINT)) {
1328 * Message only exists in the folder
1329 * Remember message for fetching
1331 if (folder_cur_num < cache_cur_num) {
1332 gboolean add = FALSE;
1334 switch(FOLDER_TYPE(folder)) {
1336 if (folder_cur_num < cache_max_num)
1339 if (folder->account->max_articles == 0) {
1343 if (folder_max_num <= folder->account->max_articles) {
1345 } else if (folder_cur_num > (folder_max_num - folder->account->max_articles)) {
1355 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(folder_cur_num));
1356 debug_print("Remembered message %d for fetching\n", folder_cur_num);
1359 /* Move to next folder number */
1360 folder_list_cur = folder_list_cur->next;
1362 if (folder_list_cur != NULL)
1363 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1365 folder_cur_num = G_MAXINT;
1371 * Message only exists in the cache
1372 * Remove the message from the cache
1374 if (cache_cur_num < folder_cur_num) {
1375 msgcache_remove_msg(item->cache, cache_cur_num);
1376 debug_print("Removed message %d from cache.\n", cache_cur_num);
1378 /* Move to next cache number */
1379 cache_list_cur = cache_list_cur->next;
1381 if (cache_list_cur != NULL)
1382 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1384 cache_cur_num = G_MAXINT;
1386 update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
1392 * Message number exists in folder and cache!
1393 * Check if the message has been modified
1395 if (cache_cur_num == folder_cur_num) {
1398 msginfo = msgcache_get_msg(item->cache, folder_cur_num);
1399 if (folder->klass->is_msg_changed && folder->klass->is_msg_changed(folder, item, msginfo)) {
1400 msgcache_remove_msg(item->cache, msginfo->msgnum);
1401 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(msginfo->msgnum));
1402 procmsg_msginfo_free(msginfo);
1404 debug_print("Remembering message %d to update...\n", folder_cur_num);
1406 exists_list = g_slist_prepend(exists_list, msginfo);
1408 /* Move to next folder and cache number */
1409 cache_list_cur = cache_list_cur->next;
1410 folder_list_cur = folder_list_cur->next;
1412 if (cache_list_cur != NULL)
1413 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1415 cache_cur_num = G_MAXINT;
1417 if (folder_list_cur != NULL)
1418 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1420 folder_cur_num = G_MAXINT;
1426 for(cache_list_cur = cache_list; cache_list_cur != NULL; cache_list_cur = g_slist_next(cache_list_cur))
1427 procmsg_msginfo_free((MsgInfo *) cache_list_cur->data);
1429 g_slist_free(cache_list);
1430 g_slist_free(folder_list);
1432 if (new_list != NULL) {
1433 if (folder->klass->get_msginfos) {
1434 newmsg_list = folder->klass->get_msginfos(folder, item, new_list);
1435 } else if (folder->klass->get_msginfo) {
1438 for (elem = new_list; elem != NULL; elem = g_slist_next(elem)) {
1442 num = GPOINTER_TO_INT(elem->data);
1443 msginfo = folder->klass->get_msginfo(folder, item, num);
1444 if (msginfo != NULL) {
1445 newmsg_list = g_slist_prepend(newmsg_list, msginfo);
1446 debug_print("Added newly found message %d to cache.\n", num);
1450 g_slist_free(new_list);
1453 folder_item_update_freeze();
1454 if (newmsg_list != NULL) {
1457 for (elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) {
1458 MsgInfo *msginfo = (MsgInfo *) elem->data;
1460 msgcache_add_msg(item->cache, msginfo);
1461 if ((filtering == TRUE) &&
1462 (item->stype == F_INBOX) &&
1463 (item->folder->account != NULL) &&
1464 (item->folder->account->filter_on_recv) &&
1465 procmsg_msginfo_filter(msginfo))
1466 procmsg_msginfo_free(msginfo);
1468 exists_list = g_slist_prepend(exists_list, msginfo);
1470 g_slist_free(newmsg_list);
1472 update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
1475 for (elem = exists_list; elem != NULL; elem = g_slist_next(elem)) {
1478 msginfo = elem->data;
1479 if (MSG_IS_IGNORE_THREAD(msginfo->flags) && (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
1480 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1481 if (!MSG_IS_IGNORE_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD)) {
1482 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1483 procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
1485 if ((item->stype == F_OUTBOX ||
1486 item->stype == F_QUEUE ||
1487 item->stype == F_DRAFT ||
1488 item->stype == F_TRASH) &&
1489 (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
1490 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1491 if (MSG_IS_NEW(msginfo->flags))
1493 if (MSG_IS_UNREAD(msginfo->flags))
1495 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
1499 procmsg_msginfo_free(msginfo);
1501 g_slist_free(exists_list);
1503 item->new_msgs = newcnt;
1504 item->unread_msgs = unreadcnt;
1505 item->total_msgs = totalcnt;
1506 item->unreadmarked_msgs = unreadmarkedcnt;
1508 update_flags |= F_ITEM_UPDATE_MSGCNT;
1510 folder_item_update(item, update_flags);
1511 folder_item_update_thaw();
1516 gint folder_item_scan(FolderItem *item)
1518 return folder_item_scan_full(item, TRUE);
1521 static gboolean folder_scan_all_items_func(GNode *node, gpointer data)
1523 FolderItem *item = node->data;
1525 folder_item_scan(item);
1530 void folder_scan_all_items(Folder * folder)
1532 g_node_traverse(folder->node, G_PRE_ORDER,
1533 G_TRAVERSE_ALL, -1, folder_scan_all_items_func, NULL);
1536 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
1539 folder_item_scan(FOLDER_ITEM(key));
1542 void folder_item_scan_foreach(GHashTable *table)
1544 g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
1547 void folder_count_total_cache_memusage(FolderItem *item, gpointer data)
1549 gint *memusage = (gint *)data;
1551 if (item->cache == NULL)
1554 *memusage += msgcache_get_memory_usage(item->cache);
1557 gint folder_cache_time_compare_func(gconstpointer a, gconstpointer b)
1559 FolderItem *fa = (FolderItem *)a;
1560 FolderItem *fb = (FolderItem *)b;
1562 return (gint) (msgcache_get_last_access_time(fa->cache) - msgcache_get_last_access_time(fb->cache));
1565 void folder_find_expired_caches(FolderItem *item, gpointer data)
1567 GSList **folder_item_list = (GSList **)data;
1568 gint difftime, expiretime;
1570 if (item->cache == NULL)
1573 if (item->opened > 0)
1576 difftime = (gint) (time(NULL) - msgcache_get_last_access_time(item->cache));
1577 expiretime = prefs_common.cache_min_keep_time * 60;
1578 debug_print("Cache unused time: %d (Expire time: %d)\n", difftime, expiretime);
1579 if (difftime > expiretime) {
1580 *folder_item_list = g_slist_insert_sorted(*folder_item_list, item, folder_cache_time_compare_func);
1584 void folder_item_free_cache(FolderItem *item)
1586 g_return_if_fail(item != NULL);
1588 if (item->cache == NULL)
1591 if (item->opened > 0)
1594 folder_item_write_cache(item);
1595 msgcache_destroy(item->cache);
1599 void folder_clean_cache_memory(void)
1603 folder_func_to_all_folders(folder_count_total_cache_memusage, &memusage);
1604 debug_print("Total cache memory usage: %d\n", memusage);
1606 if (memusage > (prefs_common.cache_max_mem_usage * 1024)) {
1607 GSList *folder_item_list = NULL, *listitem;
1609 debug_print("Trying to free cache memory\n");
1611 folder_func_to_all_folders(folder_find_expired_caches, &folder_item_list);
1612 listitem = folder_item_list;
1613 while((listitem != NULL) && (memusage > (prefs_common.cache_max_mem_usage * 1024))) {
1614 FolderItem *item = (FolderItem *)(listitem->data);
1616 debug_print("Freeing cache memory for %s\n", item->path);
1617 memusage -= msgcache_get_memory_usage(item->cache);
1618 folder_item_free_cache(item);
1619 listitem = listitem->next;
1621 g_slist_free(folder_item_list);
1625 void folder_item_read_cache(FolderItem *item)
1627 gchar *cache_file, *mark_file;
1629 g_return_if_fail(item != NULL);
1631 cache_file = folder_item_get_cache_file(item);
1632 mark_file = folder_item_get_mark_file(item);
1633 item->cache = msgcache_read_cache(item, cache_file);
1635 item->cache = msgcache_new();
1636 folder_item_scan_full(item, TRUE);
1638 msgcache_read_mark(item->cache, mark_file);
1642 folder_clean_cache_memory();
1645 void folder_item_write_cache(FolderItem *item)
1647 gchar *cache_file, *mark_file;
1648 FolderItemPrefs *prefs;
1652 if (!item || !item->path || !item->cache)
1655 id = folder_item_get_identifier(item);
1656 debug_print("Save cache for folder %s\n", id);
1659 cache_file = folder_item_get_cache_file(item);
1660 mark_file = folder_item_get_mark_file(item);
1661 if (msgcache_write(cache_file, mark_file, item->cache) < 0) {
1662 prefs = item->prefs;
1663 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
1664 /* for cache file */
1665 filemode = prefs->folder_chmod;
1666 if (filemode & S_IRGRP) filemode |= S_IWGRP;
1667 if (filemode & S_IROTH) filemode |= S_IWOTH;
1668 chmod(cache_file, filemode);
1676 MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
1681 g_return_val_if_fail(item != NULL, NULL);
1683 folder = item->folder;
1685 folder_item_read_cache(item);
1687 if ((msginfo = msgcache_get_msg(item->cache, num)) != NULL)
1690 g_return_val_if_fail(folder->klass->get_msginfo, NULL);
1691 if ((msginfo = folder->klass->get_msginfo(folder, item, num)) != NULL) {
1692 msgcache_add_msg(item->cache, msginfo);
1699 MsgInfo *folder_item_get_msginfo_by_msgid(FolderItem *item, const gchar *msgid)
1704 g_return_val_if_fail(item != NULL, NULL);
1705 g_return_val_if_fail(msgid != NULL, NULL);
1707 folder = item->folder;
1709 folder_item_read_cache(item);
1711 if ((msginfo = msgcache_get_msg_by_id(item->cache, msgid)) != NULL)
1717 GSList *folder_item_get_msg_list(FolderItem *item)
1719 g_return_val_if_fail(item != NULL, NULL);
1721 if (item->cache == 0)
1722 folder_item_read_cache(item);
1724 g_return_val_if_fail(item->cache != NULL, NULL);
1726 return msgcache_get_msg_list(item->cache);
1729 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
1733 g_return_val_if_fail(item != NULL, NULL);
1735 folder = item->folder;
1737 g_return_val_if_fail(folder->klass->fetch_msg != NULL, NULL);
1739 return folder->klass->fetch_msg(folder, item, num);
1742 static gint folder_item_get_msg_num_by_file(FolderItem *dest, const gchar *file)
1744 static HeaderEntry hentry[] = {{"Message-ID:", NULL, TRUE},
1745 {NULL, NULL, FALSE}};
1749 gchar buf[BUFFSIZE];
1751 if ((fp = fopen(file, "rb")) == NULL)
1754 if ((dest->stype == F_QUEUE) || (dest->stype == F_DRAFT))
1755 while (fgets(buf, sizeof(buf), fp) != NULL)
1756 if (buf[0] == '\r' || buf[0] == '\n') break;
1758 procheader_get_header_fields(fp, hentry);
1759 if (hentry[0].body) {
1760 extract_parenthesis(hentry[0].body, '<', '>');
1761 remove_space(hentry[0].body);
1762 if ((msginfo = msgcache_get_msg_by_id(dest->cache, hentry[0].body)) != NULL) {
1763 msgnum = msginfo->msgnum;
1764 procmsg_msginfo_free(msginfo);
1766 debug_print("found message as uid %d\n", msgnum);
1770 g_free(hentry[0].body);
1771 hentry[0].body = NULL;
1777 static void copy_msginfo_flags(MsgInfo *source, MsgInfo *dest)
1779 MsgPermFlags perm_flags = 0;
1780 MsgTmpFlags tmp_flags = 0;
1782 /* create new flags */
1783 if (source != NULL) {
1784 /* copy original flags */
1785 perm_flags = source->flags.perm_flags;
1786 tmp_flags = source->flags.tmp_flags;
1788 perm_flags = dest->flags.perm_flags;
1789 tmp_flags = dest->flags.tmp_flags;
1792 /* remove new, unread and deleted in special folders */
1793 if (dest->folder->stype == F_OUTBOX ||
1794 dest->folder->stype == F_QUEUE ||
1795 dest->folder->stype == F_DRAFT ||
1796 dest->folder->stype == F_TRASH)
1797 perm_flags &= ~(MSG_NEW | MSG_UNREAD | MSG_DELETED);
1799 /* set ignore flag of ignored parent exists */
1800 if (procmsg_msg_has_flagged_parent(dest, MSG_IGNORE_THREAD))
1801 perm_flags |= MSG_IGNORE_THREAD;
1803 /* Unset tmp flags that should not be copied */
1804 tmp_flags &= ~(MSG_MOVE | MSG_COPY);
1806 /* unset flags that are set but should not */
1807 procmsg_msginfo_unset_flags(dest,
1808 dest->flags.perm_flags & ~perm_flags,
1809 dest->flags.tmp_flags & ~tmp_flags);
1811 procmsg_msginfo_set_flags(dest,
1812 ~dest->flags.perm_flags & perm_flags,
1813 ~dest->flags.tmp_flags & tmp_flags);
1815 folder_item_update(dest->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
1818 static void add_msginfo_to_cache(FolderItem *item, MsgInfo *newmsginfo, MsgInfo *flagsource)
1820 /* update folder stats */
1821 if (MSG_IS_NEW(newmsginfo->flags))
1823 if (MSG_IS_UNREAD(newmsginfo->flags))
1824 item->unread_msgs++;
1825 if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
1826 item->unreadmarked_msgs++;
1829 copy_msginfo_flags(flagsource, newmsginfo);
1831 msgcache_add_msg(item->cache, newmsginfo);
1834 static void remove_msginfo_from_cache(FolderItem *item, MsgInfo *msginfo)
1836 MsgInfoUpdate msginfo_update;
1839 folder_item_read_cache(item);
1841 if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1842 msginfo->folder->new_msgs--;
1843 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1844 msginfo->folder->unread_msgs--;
1845 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
1846 msginfo->folder->unreadmarked_msgs--;
1847 msginfo->folder->total_msgs--;
1849 msginfo_update.msginfo = msginfo;
1850 msginfo_update.flags = MSGINFO_UPDATE_DELETED;
1851 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1853 msgcache_remove_msg(item->cache, msginfo->msgnum);
1854 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
1857 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
1858 MsgFlags *flags, gboolean remove_source)
1861 MsgFileInfo fileinfo;
1863 g_return_val_if_fail(dest != NULL, -1);
1864 g_return_val_if_fail(file != NULL, -1);
1866 fileinfo.msginfo = NULL;
1867 fileinfo.file = (gchar *)file;
1868 fileinfo.flags = flags;
1869 file_list.data = &fileinfo;
1870 file_list.next = NULL;
1872 return folder_item_add_msgs(dest, &file_list, remove_source);
1875 gint folder_item_add_msgs(FolderItem *dest, GSList *file_list,
1876 gboolean remove_source)
1879 gint ret, num, lastnum = -1;
1881 GRelation *relation;
1882 MsgFileInfo *fileinfo = NULL;
1883 gboolean folderscan = FALSE;
1885 g_return_val_if_fail(dest != NULL, -1);
1886 g_return_val_if_fail(file_list != NULL, -1);
1887 g_return_val_if_fail(dest->folder != NULL, -1);
1889 folder = dest->folder;
1891 relation = g_relation_new(2);
1892 g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
1894 if (folder->klass->add_msgs != NULL) {
1895 ret = folder->klass->add_msgs(folder, dest, file_list, relation);
1897 g_relation_destroy(relation);
1901 for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
1902 fileinfo = (MsgFileInfo *) file_cur->data;
1904 ret = folder->klass->add_msg(folder, dest, fileinfo->file, fileinfo->flags);
1906 g_relation_destroy(relation);
1909 g_relation_insert(relation, fileinfo, GINT_TO_POINTER(ret));
1913 for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
1916 fileinfo = (MsgFileInfo *) file_cur->data;
1917 tuples = g_relation_select(relation, fileinfo, 0);
1918 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1919 g_tuples_destroy(tuples);
1922 MsgInfo *newmsginfo;
1926 folder_item_scan_full(dest, FALSE);
1929 num = folder_item_get_msg_num_by_file(dest, fileinfo->file);
1935 if (num >= 0 && remove_source) {
1936 if (unlink(fileinfo->file) < 0)
1937 FILE_OP_ERROR(fileinfo->file, "unlink");
1944 ((newmsginfo = folder->klass->get_msginfo(folder, dest, num)) != NULL)) {
1945 add_msginfo_to_cache(dest, newmsginfo, NULL);
1946 procmsg_msginfo_free(newmsginfo);
1947 } else if ((newmsginfo = msgcache_get_msg(dest->cache, num)) != NULL) {
1948 /* TODO: set default flags */
1949 procmsg_msginfo_free(newmsginfo);
1954 g_relation_destroy(relation);
1960 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1965 g_return_val_if_fail(dest != NULL, -1);
1966 g_return_val_if_fail(msginfo != NULL, -1);
1968 folder = dest->folder;
1969 if (dest->last_num < 0) folder->scan(folder, dest);
1971 num = folder->move_msg(folder, dest, msginfo);
1972 if (num > 0) dest->last_num = num;
1978 FolderItem *folder_item_move_recursive(FolderItem *src, FolderItem *dest)
1981 FolderItem *new_item;
1982 FolderItem *next_item;
1984 gchar *old_id, *new_id;
1986 mlist = folder_item_get_msg_list(src);
1989 debug_print("Moving %s to %s\n", src->path, dest->path);
1990 new_item = folder_create_folder(dest, g_basename(src->path));
1991 if (new_item == NULL) {
1992 printf("Can't create folder\n");
1996 if (new_item->folder == NULL)
1997 new_item->folder = dest->folder;
2000 log_message(_("Moving %s to %s...\n"),
2001 src->name, new_item->path);
2002 folder_item_move_msgs(new_item, mlist);
2005 folder_item_prefs_copy_prefs(src, new_item);
2006 new_item->collapsed = src->collapsed;
2007 new_item->thread_collapsed = src->thread_collapsed;
2008 new_item->threaded = src->threaded;
2009 new_item->ret_rcpt = src->ret_rcpt;
2010 new_item->hide_read_msgs = src->hide_read_msgs;
2011 new_item->sort_key = src->sort_key;
2012 new_item->sort_type = src->sort_type;
2014 prefs_matcher_write_config();
2017 srcnode = src->folder->node;
2018 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, src);
2019 srcnode = srcnode->children;
2020 while (srcnode != NULL) {
2021 if (srcnode && srcnode->data) {
2022 next_item = (FolderItem*) srcnode->data;
2023 srcnode = srcnode->next;
2024 if (folder_item_move_recursive(next_item, new_item) == NULL)
2028 old_id = folder_item_get_identifier(src);
2029 new_id = folder_item_get_identifier(new_item);
2030 debug_print("updating rules : %s => %s\n", old_id, new_id);
2032 src->folder->klass->remove_folder(src->folder, src);
2033 folder_write_list();
2035 if (old_id != NULL && new_id != NULL)
2036 prefs_filtering_rename_path(old_id, new_id);
2043 gint folder_item_move_to(FolderItem *src, FolderItem *dest, FolderItem **new_item)
2045 FolderItem *tmp = dest->parent;
2046 gchar * src_identifier, * dst_identifier;
2047 gchar * phys_srcpath, * phys_dstpath;
2051 return F_MOVE_FAILED_DEST_IS_CHILD;
2058 src_identifier = folder_item_get_identifier(src);
2059 dst_identifier = folder_item_get_identifier(dest);
2061 if(dst_identifier == NULL && dest->folder && dest->parent == NULL) {
2062 /* dest can be a root folder */
2063 dst_identifier = folder_get_identifier(dest->folder);
2065 if (src_identifier == NULL || dst_identifier == NULL) {
2066 debug_print("Can't get identifiers\n");
2067 return F_MOVE_FAILED;
2070 if (src->folder != dest->folder) {
2071 return F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX;
2074 phys_srcpath = folder_item_get_path(src);
2075 phys_dstpath = g_strconcat(folder_item_get_path(dest),G_DIR_SEPARATOR_S,g_basename(phys_srcpath),NULL);
2077 if (src->parent == dest || src == dest) {
2078 g_free(src_identifier);
2079 g_free(dst_identifier);
2080 g_free(phys_srcpath);
2081 g_free(phys_dstpath);
2082 return F_MOVE_FAILED_DEST_IS_PARENT;
2084 debug_print("moving \"%s\" to \"%s\"\n", phys_srcpath, phys_dstpath);
2085 if ((tmp = folder_item_move_recursive(src, dest)) == NULL) {
2086 return F_MOVE_FAILED;
2089 g_free(src_identifier);
2090 g_free(dst_identifier);
2091 g_free(phys_srcpath);
2092 g_free(phys_dstpath);
2100 * Copy a list of message to a new folder and remove
2101 * source messages if wanted
2103 static gint do_copy_msgs(FolderItem *dest, GSList *msglist, gboolean remove_source)
2107 gint num, lastnum = -1;
2108 gboolean folderscan = FALSE;
2109 GRelation *relation;
2111 g_return_val_if_fail(dest != NULL, -1);
2112 g_return_val_if_fail(msglist != NULL, -1);
2114 folder = dest->folder;
2116 g_return_val_if_fail(folder->klass->copy_msg != NULL, -1);
2118 relation = g_relation_new(2);
2119 g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
2120 g_relation_index(relation, 1, g_direct_hash, g_direct_equal);
2123 * Copy messages to destination folder and
2124 * store new message numbers in newmsgnums
2126 if (folder->klass->copy_msgs != NULL) {
2127 if (folder->klass->copy_msgs(folder, dest, msglist, relation) < 0) {
2128 g_relation_destroy(relation);
2132 for (l = msglist ; l != NULL ; l = g_slist_next(l)) {
2133 MsgInfo * msginfo = (MsgInfo *) l->data;
2135 num = folder->klass->copy_msg(folder, dest, msginfo);
2136 g_relation_insert(relation, msginfo, GINT_TO_POINTER(num));
2140 /* Read cache for dest folder */
2141 if (!dest->cache) folder_item_read_cache(dest);
2144 * Fetch new MsgInfos for new messages in dest folder,
2145 * add them to the msgcache and update folder message counts
2147 if (g_relation_count(relation, GINT_TO_POINTER(0), 1) > 0) {
2148 folder_item_scan_full(dest, FALSE);
2152 for (l = msglist; l != NULL; l = g_slist_next(l)) {
2153 MsgInfo *msginfo = (MsgInfo *) l->data;
2156 tuples = g_relation_select(relation, msginfo, 0);
2157 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
2158 g_tuples_destroy(tuples);
2161 MsgInfo *newmsginfo;
2164 if (msginfo->msgid != NULL) {
2165 newmsginfo = folder_item_get_msginfo_by_msgid(dest, msginfo->msgid);
2166 if (newmsginfo != NULL) {
2167 copy_msginfo_flags(msginfo, newmsginfo);
2168 num = newmsginfo->msgnum;
2169 procmsg_msginfo_free(newmsginfo);
2173 newmsginfo = folder->klass->get_msginfo(folder, dest, num);
2174 if (newmsginfo != NULL) {
2175 add_msginfo_to_cache(dest, newmsginfo, msginfo);
2176 procmsg_msginfo_free(newmsginfo);
2185 if (remove_source) {
2187 * Remove source messages from their folders if
2188 * copying was successfull and update folder
2191 for (l = msglist; l != NULL; l = g_slist_next(l)) {
2192 MsgInfo *msginfo = (MsgInfo *) l->data;
2193 FolderItem *item = msginfo->folder;
2196 tuples = g_relation_select(relation, msginfo, 0);
2197 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
2198 g_tuples_destroy(tuples);
2200 if ((num >= 0) && (item->folder->klass->remove_msg != NULL)) {
2201 item->folder->klass->remove_msg(item->folder,
2204 remove_msginfo_from_cache(item, msginfo);
2209 if (folder->klass->finished_copy)
2210 folder->klass->finished_copy(folder, dest);
2212 g_relation_destroy(relation);
2217 * Move a message to a new folder.
2219 * \param dest Destination folder
2220 * \param msginfo The message
2222 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
2226 g_return_val_if_fail(dest != NULL, -1);
2227 g_return_val_if_fail(msginfo != NULL, -1);
2229 list.data = msginfo;
2232 return do_copy_msgs(dest, &list, TRUE);
2236 * Move a list of messages to a new folder.
2238 * \param dest Destination folder
2239 * \param msglist List of messages
2241 gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
2243 g_return_val_if_fail(dest != NULL, -1);
2244 g_return_val_if_fail(msglist != NULL, -1);
2246 return do_copy_msgs(dest, msglist, TRUE);
2250 * Copy a message to a new folder.
2252 * \param dest Destination folder
2253 * \param msginfo The message
2255 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
2259 g_return_val_if_fail(dest != NULL, -1);
2260 g_return_val_if_fail(msginfo != NULL, -1);
2262 list.data = msginfo;
2265 return do_copy_msgs(dest, &list, FALSE);
2269 * Copy a list of messages to a new folder.
2271 * \param dest Destination folder
2272 * \param msglist List of messages
2274 gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
2276 g_return_val_if_fail(dest != NULL, -1);
2277 g_return_val_if_fail(msglist != NULL, -1);
2279 return do_copy_msgs(dest, msglist, FALSE);
2282 gint folder_item_remove_msg(FolderItem *item, gint num)
2288 g_return_val_if_fail(item != NULL, -1);
2289 folder = item->folder;
2290 g_return_val_if_fail(folder->klass->remove_msg != NULL, -1);
2292 if (!item->cache) folder_item_read_cache(item);
2294 ret = folder->klass->remove_msg(folder, item, num);
2296 msginfo = msgcache_get_msg(item->cache, num);
2297 if (msginfo != NULL) {
2298 remove_msginfo_from_cache(item, msginfo);
2299 procmsg_msginfo_free(msginfo);
2301 folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
2306 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
2311 g_return_val_if_fail(item != NULL, -1);
2312 folder = item->folder;
2313 g_return_val_if_fail(folder != NULL, -1);
2315 if (!item->cache) folder_item_read_cache(item);
2317 while (msglist != NULL) {
2318 MsgInfo *msginfo = (MsgInfo *)msglist->data;
2320 ret = folder_item_remove_msg(item, msginfo->msgnum);
2321 if (ret != 0) break;
2322 msgcache_remove_msg(item->cache, msginfo->msgnum);
2323 msglist = msglist->next;
2329 gint folder_item_remove_all_msg(FolderItem *item)
2334 g_return_val_if_fail(item != NULL, -1);
2336 folder = item->folder;
2338 g_return_val_if_fail(folder->klass->remove_all_msg != NULL, -1);
2340 result = folder->klass->remove_all_msg(folder, item);
2343 if (folder->klass->finished_remove)
2344 folder->klass->finished_remove(folder, item);
2346 folder_item_free_cache(item);
2347 item->cache = msgcache_new();
2350 item->unread_msgs = 0;
2351 item->unreadmarked_msgs = 0;
2352 item->total_msgs = 0;
2353 folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
2359 void folder_item_change_msg_flags(FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
2361 g_return_if_fail(item != NULL);
2362 g_return_if_fail(msginfo != NULL);
2364 if (item->folder->klass->change_flags != NULL) {
2365 item->folder->klass->change_flags(item->folder, item, msginfo, newflags);
2367 msginfo->flags.perm_flags = newflags;
2371 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
2375 g_return_val_if_fail(item != NULL, FALSE);
2377 folder = item->folder;
2379 g_return_val_if_fail(folder->klass->is_msg_changed != NULL, -1);
2381 return folder->klass->is_msg_changed(folder, item, msginfo);
2384 gchar *folder_item_get_cache_file(FolderItem *item)
2389 g_return_val_if_fail(item != NULL, NULL);
2390 g_return_val_if_fail(item->path != NULL, NULL);
2392 path = folder_item_get_path(item);
2393 g_return_val_if_fail(path != NULL, NULL);
2394 if (!is_dir_exist(path))
2395 make_dir_hier(path);
2396 file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
2402 gchar *folder_item_get_mark_file(FolderItem *item)
2407 g_return_val_if_fail(item != NULL, NULL);
2408 g_return_val_if_fail(item->path != NULL, NULL);
2410 path = folder_item_get_path(item);
2411 g_return_val_if_fail(path != NULL, NULL);
2412 if (!is_dir_exist(path))
2413 make_dir_hier(path);
2414 file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
2420 static gboolean folder_build_tree(GNode *node, gpointer data)
2422 Folder *folder = FOLDER(data);
2426 g_return_val_if_fail(node->data != NULL, FALSE);
2427 if (!node->parent) return FALSE;
2429 xmlnode = node->data;
2430 if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
2431 g_warning("tag name != \"folderitem\"\n");
2435 item = folder_item_new(folder, "", "");
2436 if (folder->klass->item_set_xml != NULL)
2437 folder->klass->item_set_xml(folder, item, xmlnode->tag);
2439 folder_item_set_attrs(folder, item, xmlnode->tag);
2441 item->parent = FOLDER_ITEM(node->parent->data);
2442 item->folder = folder;
2443 switch (item->stype) {
2444 case F_INBOX: folder->inbox = item; break;
2445 case F_OUTBOX: folder->outbox = item; break;
2446 case F_DRAFT: folder->draft = item; break;
2447 case F_QUEUE: folder->queue = item; break;
2448 case F_TRASH: folder->trash = item; break;
2451 folder_item_prefs_read_config(item);
2454 xml_free_node(xmlnode);
2459 static gboolean folder_read_folder_func(GNode *node, gpointer data)
2465 FolderClass *class = NULL;
2466 const gchar *name = NULL;
2467 const gchar *path = NULL;
2468 PrefsAccount *account = NULL;
2469 gboolean collapsed = FALSE, threaded = TRUE, apply_sub = FALSE;
2470 gboolean ret_rcpt = FALSE, thread_collapsed = FALSE; /* CLAWS */
2472 if (g_node_depth(node) != 2) return FALSE;
2473 g_return_val_if_fail(node->data != NULL, FALSE);
2475 xmlnode = node->data;
2476 if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
2477 g_warning("tag name != \"folder\"\n");
2480 g_node_unlink(node);
2481 list = xmlnode->tag->attr;
2482 for (; list != NULL; list = list->next) {
2483 XMLAttr *attr = list->data;
2485 if (!attr || !attr->name || !attr->value) continue;
2486 if (!strcmp(attr->name, "type"))
2487 class = folder_get_class_from_string(attr->value);
2488 else if (!strcmp(attr->name, "name"))
2490 else if (!strcmp(attr->name, "path"))
2492 else if (!strcmp(attr->name, "collapsed"))
2493 collapsed = *attr->value == '1' ? TRUE : FALSE;
2494 else if (!strcmp(attr->name, "thread_collapsed"))
2495 thread_collapsed = *attr->value == '1' ? TRUE : FALSE;
2496 else if (!strcmp(attr->name, "threaded"))
2497 threaded = *attr->value == '1' ? TRUE : FALSE;
2498 else if (!strcmp(attr->name, "account_id")) {
2499 account = account_find_from_id(atoi(attr->value));
2500 if (!account) g_warning("account_id: %s not found\n",
2502 } else if (!strcmp(attr->name, "apply_sub"))
2503 apply_sub = *attr->value == '1' ? TRUE : FALSE;
2504 else if (!strcmp(attr->name, "reqretrcpt"))
2505 ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
2508 folder = folder_new(class, name, path);
2509 g_return_val_if_fail(folder != NULL, FALSE);
2510 folder->account = account;
2511 if (account != NULL)
2512 account->folder = folder;
2513 item = FOLDER_ITEM(folder->node->data);
2516 g_node_destroy(folder->node);
2517 folder->node = node;
2519 item->collapsed = collapsed;
2520 item->thread_collapsed = thread_collapsed;
2521 item->threaded = threaded;
2522 item->account = account;
2523 item->apply_sub = apply_sub;
2524 item->ret_rcpt = ret_rcpt;
2526 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2527 folder_build_tree, folder);
2532 static gchar *folder_get_list_path(void)
2534 static gchar *filename = NULL;
2537 filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2543 #define PUT_ESCAPE_STR(fp, attr, str) \
2545 fputs(" " attr "=\"", fp); \
2546 xml_file_put_escape_str(fp, str); \
2550 static void folder_write_list_recursive(GNode *node, gpointer data)
2552 FILE *fp = (FILE *)data;
2556 g_return_if_fail(node != NULL);
2557 g_return_if_fail(fp != NULL);
2559 item = FOLDER_ITEM(node->data);
2560 g_return_if_fail(item != NULL);
2562 depth = g_node_depth(node);
2563 for (i = 0; i < depth; i++)
2566 Folder *folder = item->folder;
2568 fprintf(fp, "<folder type=\"%s\"", folder->klass->idstr);
2570 PUT_ESCAPE_STR(fp, "name", folder->name);
2571 if (FOLDER_TYPE(folder) == F_MH ||
2572 FOLDER_TYPE(folder) == F_MBOX ||
2573 FOLDER_TYPE(folder) == F_MAILDIR)
2574 PUT_ESCAPE_STR(fp, "path",
2575 LOCAL_FOLDER(folder)->rootpath);
2576 if (item->collapsed && node->children)
2577 fputs(" collapsed=\"1\"", fp);
2578 if (folder->account)
2579 fprintf(fp, " account_id=\"%d\"",
2580 folder->account->account_id);
2581 if (item->apply_sub)
2582 fputs(" apply_sub=\"1\"", fp);
2584 fputs(" reqretrcpt=\"1\"", fp);
2589 if (item->folder->klass->item_get_xml != NULL)
2590 tag = item->folder->klass->item_get_xml(item->folder, item);
2592 tag = folder_item_get_attrs(item->folder, item);
2594 fprintf(fp, "<%s", tag->tag);
2595 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
2596 XMLAttr *attr = (XMLAttr *) cur->data;
2598 fprintf(fp, " %s=\"", attr->name);
2599 xml_file_put_escape_str(fp, attr->value);
2605 if (node->children) {
2609 child = node->children;
2615 folder_write_list_recursive(cur, data);
2618 for (i = 0; i < depth; i++)
2620 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
2625 static void folder_update_op_count_rec(GNode *node)
2627 FolderItem *fitem = FOLDER_ITEM(node->data);
2629 if (g_node_depth(node) > 0) {
2630 if (fitem->op_count > 0) {
2631 fitem->op_count = 0;
2632 folder_item_update(fitem, F_ITEM_UPDATE_MSGCNT);
2634 if (node->children) {
2637 child = node->children;
2643 folder_update_op_count_rec(cur);
2649 void folder_update_op_count(void)
2654 for (cur = folder_list; cur != NULL; cur = cur->next) {
2656 folder_update_op_count_rec(folder->node);
2660 typedef struct _type_str {
2667 static gchar * folder_item_get_tree_identifier(FolderItem * item)
2669 if (item->parent != NULL) {
2673 path = folder_item_get_tree_identifier(item->parent);
2677 id = g_strconcat(path, "/", item->name, NULL);
2683 return g_strconcat("/", item->name, NULL);
2688 /* CLAWS: temporary local folder for filtering */
2689 #define TEMP_FOLDER "TEMP_FOLDER"
2690 #define PROCESSING_FOLDER_ITEM "processing"
2692 static FolderItem *processing_folder_item;
2694 static void folder_create_processing_folder(void)
2696 Folder *processing_folder;
2699 if ((processing_folder = folder_find_from_name(TEMP_FOLDER, mh_get_class())) == NULL) {
2703 g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2704 "tempfolder", NULL);
2706 folder_new(mh_get_class(), TEMP_FOLDER, tmppath);
2709 g_assert(processing_folder != NULL);
2711 debug_print("tmpparentroot %s\n", LOCAL_FOLDER(processing_folder)->rootpath);
2712 if (LOCAL_FOLDER(processing_folder)->rootpath[0] == '/')
2713 tmpname = g_strconcat(LOCAL_FOLDER(processing_folder)->rootpath,
2714 G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
2717 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2718 LOCAL_FOLDER(processing_folder)->rootpath,
2719 G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
2722 if (!is_dir_exist(tmpname)) {
2723 debug_print("*TMP* creating %s\n", tmpname);
2724 processing_folder_item = processing_folder->klass->create_folder(processing_folder,
2725 processing_folder->node->data,
2726 PROCESSING_FOLDER_ITEM);
2728 debug_print("*TMP* already created\n");
2729 processing_folder_item = folder_item_new(processing_folder, PROCESSING_FOLDER_ITEM, PROCESSING_FOLDER_ITEM);
2730 g_assert(processing_folder_item);
2731 folder_item_append(processing_folder->node->data, processing_folder_item);
2733 g_assert(processing_folder_item != NULL);
2737 FolderItem *folder_get_default_processing(void)
2739 if (!processing_folder_item) {
2740 folder_create_processing_folder();
2742 return processing_folder_item;
2745 /* folder_persist_prefs_new() - return hash table with persistent
2746 * settings (and folder name as key).
2747 * (note that in claws other options are in the folder_item_prefs_RC
2748 * file, so those don't need to be included in PersistPref yet)
2750 GHashTable *folder_persist_prefs_new(Folder *folder)
2752 GHashTable *pptable;
2754 g_return_val_if_fail(folder, NULL);
2755 pptable = g_hash_table_new(g_str_hash, g_str_equal);
2756 folder_get_persist_prefs_recursive(folder->node, pptable);
2760 void folder_persist_prefs_free(GHashTable *pptable)
2762 g_return_if_fail(pptable);
2763 g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
2764 g_hash_table_destroy(pptable);
2767 const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
2769 if (pptable == NULL || name == NULL) return NULL;
2770 return g_hash_table_lookup(pptable, name);
2773 void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
2775 const PersistPrefs *pp;
2776 gchar *id = folder_item_get_identifier(item);
2778 pp = folder_get_persist_prefs(pptable, id);
2783 /* CLAWS: since not all folder properties have been migrated to
2784 * folderlist.xml, we need to call the old stuff first before
2785 * setting things that apply both to Main and Claws. */
2786 folder_item_prefs_read_config(item);
2788 item->collapsed = pp->collapsed;
2789 item->thread_collapsed = pp->thread_collapsed;
2790 item->threaded = pp->threaded;
2791 item->ret_rcpt = pp->ret_rcpt;
2792 item->hide_read_msgs = pp->hide_read_msgs;
2793 item->sort_key = pp->sort_key;
2794 item->sort_type = pp->sort_type;
2797 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
2799 FolderItem *item = FOLDER_ITEM(node->data);
2804 g_return_if_fail(node != NULL);
2805 g_return_if_fail(item != NULL);
2807 /* NOTE: item->path == NULL means top level folder; not interesting
2808 * to store preferences of that one. */
2810 id = folder_item_get_identifier(item);
2811 pp = g_new0(PersistPrefs, 1);
2812 g_return_if_fail(pp != NULL);
2813 pp->collapsed = item->collapsed;
2814 pp->thread_collapsed = item->thread_collapsed;
2815 pp->threaded = item->threaded;
2816 pp->ret_rcpt = item->ret_rcpt;
2817 pp->hide_read_msgs = item->hide_read_msgs;
2818 pp->sort_key = item->sort_key;
2819 pp->sort_type = item->sort_type;
2820 g_hash_table_insert(pptable, id, pp);
2823 if (node->children) {
2824 child = node->children;
2828 folder_get_persist_prefs_recursive(cur, pptable);
2833 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
2842 void folder_item_apply_processing(FolderItem *item)
2844 GSList *processing_list;
2845 GSList *mlist, *cur;
2847 g_return_if_fail(item != NULL);
2849 processing_list = item->prefs->processing;
2850 if (processing_list == NULL)
2853 folder_item_update_freeze();
2855 mlist = folder_item_get_msg_list(item);
2856 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2859 msginfo = (MsgInfo *) cur->data;
2860 filter_message_by_msginfo(processing_list, msginfo);
2861 procmsg_msginfo_free(msginfo);
2863 g_slist_free(mlist);
2865 folder_item_update_thaw();
2869 * functions for handling FolderItem content changes
2871 static gint folder_item_update_freeze_cnt = 0;
2874 * Notify the folder system about changes to a folder. If the
2875 * update system is not frozen the FOLDER_ITEM_UPDATE_HOOKLIST will
2876 * be invoked, otherwise the changes will be remebered until
2877 * the folder system is thawed.
2879 * \param item The FolderItem that was changed
2880 * \param update_flags Type of changed that was made
2882 void folder_item_update(FolderItem *item, FolderItemUpdateFlags update_flags)
2884 if (folder_item_update_freeze_cnt == 0) {
2885 FolderItemUpdateData source;
2888 source.update_flags = update_flags;
2889 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
2891 item->update_flags |= update_flags;
2895 void folder_item_update_recursive(FolderItem *item, FolderItemUpdateFlags update_flags)
2897 GNode *node = item->folder->node;
2899 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2900 node = node->children;
2902 folder_item_update(item, update_flags);
2903 while (node != NULL) {
2904 if (node && node->data) {
2905 FolderItem *next_item = (FolderItem*) node->data;
2907 folder_item_update(next_item, update_flags);
2913 void folder_item_update_freeze(void)
2915 folder_item_update_freeze_cnt++;
2918 static void folder_item_update_func(FolderItem *item, gpointer data)
2920 FolderItemUpdateData source;
2922 if (item->update_flags) {
2924 source.update_flags = item->update_flags;
2925 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
2926 item->update_flags = 0;
2930 void folder_item_update_thaw(void)
2932 if (folder_item_update_freeze_cnt > 0)
2933 folder_item_update_freeze_cnt--;
2934 if (folder_item_update_freeze_cnt == 0) {
2935 /* Update all folders */
2936 folder_func_to_all_folders(folder_item_update_func, NULL);
2940 #undef PUT_ESCAPE_STR