2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2002 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>
36 #include "folderview.h"
41 #include "mbox_folder.h"
46 #include "prefs_common.h"
48 #include "prefs_account.h"
49 #include "prefs_folder_item.h"
50 #include "procheader.h"
52 static GList *folder_list = NULL;
54 static void folder_init (Folder *folder,
57 static gboolean folder_read_folder_func (GNode *node,
59 static gchar *folder_get_list_path (void);
60 static void folder_write_list_recursive (GNode *node,
62 static void folder_update_op_count_rec (GNode *node);
65 static void folder_get_persist_prefs_recursive
66 (GNode *node, GHashTable *pptable);
67 static gboolean persist_prefs_free (gpointer key, gpointer val, gpointer data);
68 void folder_item_read_cache (FolderItem *item);
69 void folder_item_write_cache (FolderItem *item);
72 Folder *folder_new(FolderType type, const gchar *name, const gchar *path)
74 Folder *folder = NULL;
76 name = name ? name : path;
79 folder = mbox_folder_new(name, path);
82 folder = mh_folder_new(name, path);
85 folder = imap_folder_new(name, path);
88 folder = news_folder_new(name, path);
97 static void folder_init(Folder *folder, const gchar *name)
101 g_return_if_fail(folder != NULL);
103 folder_set_name(folder, name);
105 /* Init folder data */
106 folder->type = F_UNKNOWN;
107 folder->account = NULL;
108 folder->inbox = NULL;
109 folder->outbox = NULL;
110 folder->draft = NULL;
111 folder->queue = NULL;
112 folder->trash = NULL;
114 /* Init Folder functions */
115 folder->fetch_msg = NULL;
116 folder->fetch_msginfo = NULL;
117 folder->fetch_msginfos = NULL;
118 folder->get_num_list = NULL;
119 folder->ui_func = NULL;
120 folder->ui_func_data = NULL;
121 folder->check_msgnum_validity = NULL;
123 /* Create root folder item */
124 item = folder_item_new(name, NULL);
125 item->folder = folder;
126 folder->node = g_node_new(item);
130 void folder_local_folder_init(Folder *folder, const gchar *name,
133 folder_init(folder, name);
134 LOCAL_FOLDER(folder)->rootpath = g_strdup(path);
137 void folder_remote_folder_init(Folder *folder, const gchar *name,
140 folder_init(folder, name);
141 REMOTE_FOLDER(folder)->session = NULL;
144 void folder_destroy(Folder *folder)
146 g_return_if_fail(folder != NULL);
148 switch (folder->type) {
150 mbox_folder_destroy(MBOX_FOLDER(folder));
152 mh_folder_destroy(MH_FOLDER(folder));
155 imap_folder_destroy(IMAP_FOLDER(folder));
158 news_folder_destroy(NEWS_FOLDER(folder));
164 folder_list = g_list_remove(folder_list, folder);
166 folder_tree_destroy(folder);
167 g_free(folder->name);
171 void folder_local_folder_destroy(LocalFolder *lfolder)
173 g_return_if_fail(lfolder != NULL);
175 g_free(lfolder->rootpath);
178 void folder_remote_folder_destroy(RemoteFolder *rfolder)
180 g_return_if_fail(rfolder != NULL);
182 if (rfolder->session)
183 session_destroy(rfolder->session);
187 Folder *mbox_folder_new(const gchar *name, const gchar *path)
189 /* not yet implemented */
193 Folder *maildir_folder_new(const gchar *name, const gchar *path)
195 /* not yet implemented */
200 FolderItem *folder_item_new(const gchar *name, const gchar *path)
204 item = g_new0(FolderItem, 1);
206 item->stype = F_NORMAL;
207 item->name = g_strdup(name);
208 item->path = g_strdup(path);
209 item->account = NULL;
216 item->no_sub = FALSE;
217 item->no_select = FALSE;
218 item->collapsed = FALSE;
219 item->threaded = TRUE;
220 item->ret_rcpt = FALSE;
221 item->opened = FALSE;
224 item->mark_queue = NULL;
227 item->prefs = prefs_folder_item_new();
232 void folder_item_append(FolderItem *parent, FolderItem *item)
236 g_return_if_fail(parent != NULL);
237 g_return_if_fail(parent->folder != NULL);
238 g_return_if_fail(item != NULL);
240 node = parent->folder->node;
241 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, parent);
242 g_return_if_fail(node != NULL);
244 item->parent = parent;
245 item->folder = parent->folder;
246 g_node_append_data(node, item);
249 void folder_item_remove(FolderItem *item)
253 g_return_if_fail(item != NULL);
254 g_return_if_fail(item->folder != NULL);
256 node = item->folder->node;
257 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
258 g_return_if_fail(node != NULL);
260 /* TODO: free all FolderItem's first */
261 if (item->folder->node == node)
262 item->folder->node = NULL;
263 g_node_destroy(node);
266 void folder_item_destroy(FolderItem *item)
268 g_return_if_fail(item != NULL);
270 debug_print(_("Destroying folder item %s\n"), item->path);
273 folder_item_free_cache(item);
279 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
281 g_return_if_fail(folder != NULL);
283 folder->ui_func = func;
284 folder->ui_func_data = data;
287 void folder_set_name(Folder *folder, const gchar *name)
289 g_return_if_fail(folder != NULL);
291 g_free(folder->name);
292 folder->name = name ? g_strdup(name) : NULL;
293 if (folder->node && folder->node->data) {
294 FolderItem *item = (FolderItem *)folder->node->data;
297 item->name = name ? g_strdup(name) : NULL;
301 gboolean folder_tree_destroy_func(GNode *node, gpointer data) {
302 FolderItem *item = (FolderItem *) node->data;
304 folder_item_destroy(item);
308 void folder_tree_destroy(Folder *folder)
310 /* TODO: destroy all FolderItem before */
311 g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_tree_destroy_func, NULL);
312 g_node_destroy(folder->node);
314 folder->inbox = NULL;
315 folder->outbox = NULL;
316 folder->draft = NULL;
317 folder->queue = NULL;
318 folder->trash = NULL;
322 void folder_add(Folder *folder)
328 g_return_if_fail(folder != NULL);
330 for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
331 cur_folder = FOLDER(cur->data);
332 if (folder->type == F_MH) {
333 if (cur_folder->type != F_MH) break;
334 } else if (folder->type == F_MBOX) {
335 if (cur_folder->type != F_MH &&
336 cur_folder->type != F_MBOX) break;
337 } else if (folder->type == F_IMAP) {
338 if (cur_folder->type != F_MH &&
339 cur_folder->type != F_MBOX &&
340 cur_folder->type != F_IMAP) break;
341 } else if (folder->type == F_NEWS) {
342 if (cur_folder->type != F_MH &&
343 cur_folder->type != F_MBOX &&
344 cur_folder->type != F_IMAP &&
345 cur_folder->type != F_NEWS) break;
349 folder_list = g_list_insert(folder_list, folder, i);
352 GList *folder_get_list(void)
357 gint folder_read_list(void)
363 path = folder_get_list_path();
364 if (!is_file_exist(path)) return -1;
365 node = xml_parse_file(path);
366 if (!node) return -1;
368 xmlnode = node->data;
369 if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
370 g_warning("wrong folder list\n");
375 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
376 folder_read_folder_func, NULL);
385 void folder_write_list(void)
392 path = folder_get_list_path();
393 if ((pfile = prefs_write_open(path)) == NULL) return;
395 fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
396 conv_get_current_charset_str());
397 fputs("\n<folderlist>\n", pfile->fp);
399 for (list = folder_list; list != NULL; list = list->next) {
401 folder_write_list_recursive(folder->node, pfile->fp);
404 fputs("</folderlist>\n", pfile->fp);
406 if (prefs_write_close(pfile) < 0)
407 g_warning("failed to write folder list.\n");
417 struct FuncToAllFoldersData
419 FolderItemFunc function;
423 static gboolean folder_func_to_all_folders_func(GNode *node, gpointer data)
426 struct FuncToAllFoldersData *function_data = (struct FuncToAllFoldersData *) data;
428 g_return_val_if_fail(node->data != NULL, FALSE);
430 item = FOLDER_ITEM(node->data);
431 g_return_val_if_fail(item != NULL, FALSE);
433 function_data->function(item, function_data->data);
438 void folder_func_to_all_folders(FolderItemFunc function, gpointer data)
442 struct FuncToAllFoldersData function_data;
444 function_data.function = function;
445 function_data.data = data;
447 for (list = folder_list; list != NULL; list = list->next) {
448 folder = FOLDER(list->data);
450 g_node_traverse(folder->node, G_PRE_ORDER,
452 folder_func_to_all_folders_func,
457 static void folder_count_total_msgs_func(FolderItem *item, gpointer data)
459 struct TotalMsgCount *count = (struct TotalMsgCount *)data;
461 count->new += item->new;
462 count->unread += item->unread;
463 count->total += item->total;
466 void folder_count_total_msgs(guint *new, guint *unread, guint *total)
468 struct TotalMsgCount count;
470 count.new = count.unread = count.total = 0;
472 debug_print(_("Counting total number of messages...\n"));
474 folder_func_to_all_folders(folder_count_total_msgs_func, &count);
477 *unread = count.unread;
478 *total = count.total;
481 Folder *folder_find_from_path(const gchar *path)
486 for (list = folder_list; list != NULL; list = list->next) {
488 if ((folder->type == F_MH || folder->type == F_MBOX) &&
489 !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
496 Folder *folder_find_from_name(const gchar *name, FolderType type)
501 for (list = folder_list; list != NULL; list = list->next) {
503 if (folder->type == type && strcmp2(name, folder->name) == 0)
510 static gboolean folder_item_find_func(GNode *node, gpointer data)
512 FolderItem *item = node->data;
514 const gchar *path = d[0];
516 if (path_cmp(path, item->path) != 0)
524 FolderItem *folder_find_item_from_path(const gchar *path)
529 folder = folder_get_default_folder();
530 g_return_val_if_fail(folder != NULL, NULL);
532 d[0] = (gpointer)path;
534 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
535 folder_item_find_func, d);
539 static const struct {
542 } type_str_table[] = {
545 {"#maildir", F_MAILDIR},
550 static gchar *folder_get_type_string(FolderType type)
554 for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
556 if (type_str_table[i].type == type)
557 return type_str_table[i].str;
563 static FolderType folder_get_type_from_string(const gchar *str)
567 for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
569 if (g_strcasecmp(type_str_table[i].str, str) == 0)
570 return type_str_table[i].type;
576 gchar *folder_get_identifier(Folder *folder)
580 g_return_val_if_fail(folder != NULL, NULL);
582 type_str = folder_get_type_string(folder->type);
583 return g_strconcat(type_str, "/", folder->name, NULL);
586 gchar *folder_item_get_identifier(FolderItem *item)
591 g_return_val_if_fail(item != NULL, NULL);
592 g_return_val_if_fail(item->path != NULL, NULL);
594 folder_id = folder_get_identifier(item->folder);
595 id = g_strconcat(folder_id, "/", item->path, NULL);
601 FolderItem *folder_find_item_from_identifier(const gchar *identifier)
611 g_return_val_if_fail(identifier != NULL, NULL);
613 if (*identifier != '#')
614 return folder_find_item_from_path(identifier);
616 Xstrdup_a(str, identifier, return NULL);
618 p = strchr(str, '/');
620 return folder_find_item_from_path(identifier);
623 type = folder_get_type_from_string(str);
624 if (type == F_UNKNOWN)
625 return folder_find_item_from_path(identifier);
630 return folder_find_item_from_path(identifier);
634 folder = folder_find_from_name(name, type);
636 return folder_find_item_from_path(identifier);
640 d[0] = (gpointer)path;
642 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
643 folder_item_find_func, d);
647 Folder *folder_get_default_folder(void)
649 return folder_list ? FOLDER(folder_list->data) : NULL;
652 FolderItem *folder_get_default_inbox(void)
656 if (!folder_list) return NULL;
657 folder = FOLDER(folder_list->data);
658 g_return_val_if_fail(folder != NULL, NULL);
659 return folder->inbox;
662 FolderItem *folder_get_default_outbox(void)
666 if (!folder_list) return NULL;
667 folder = FOLDER(folder_list->data);
668 g_return_val_if_fail(folder != NULL, NULL);
669 return folder->outbox;
672 FolderItem *folder_get_default_draft(void)
676 if (!folder_list) return NULL;
677 folder = FOLDER(folder_list->data);
678 g_return_val_if_fail(folder != NULL, NULL);
679 return folder->draft;
682 FolderItem *folder_get_default_queue(void)
686 if (!folder_list) return NULL;
687 folder = FOLDER(folder_list->data);
688 g_return_val_if_fail(folder != NULL, NULL);
689 return folder->queue;
692 FolderItem *folder_get_default_trash(void)
696 if (!folder_list) return NULL;
697 folder = FOLDER(folder_list->data);
698 g_return_val_if_fail(folder != NULL, NULL);
699 return folder->trash;
702 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type) \
704 if (!folder->member) { \
705 item = folder_item_new(dir, dir); \
706 item->stype = type; \
707 folder_item_append(rootitem, item); \
708 folder->member = item; \
712 void folder_set_missing_folders(void)
715 FolderItem *rootitem;
719 for (list = folder_list; list != NULL; list = list->next) {
721 if (folder->type != F_MH) continue;
722 rootitem = FOLDER_ITEM(folder->node->data);
723 g_return_if_fail(rootitem != NULL);
725 if (folder->inbox && folder->outbox && folder->draft &&
726 folder->queue && folder->trash)
729 if (folder->create_tree(folder) < 0) {
730 g_warning("%s: can't create the folder tree.\n",
731 LOCAL_FOLDER(folder)->rootpath);
735 CREATE_FOLDER_IF_NOT_EXIST(inbox, INBOX_DIR, F_INBOX);
736 CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
737 CREATE_FOLDER_IF_NOT_EXIST(draft, DRAFT_DIR, F_DRAFT);
738 CREATE_FOLDER_IF_NOT_EXIST(queue, QUEUE_DIR, F_QUEUE);
739 CREATE_FOLDER_IF_NOT_EXIST(trash, TRASH_DIR, F_TRASH);
743 #undef CREATE_FOLDER_IF_NOT_EXIST
745 gchar *folder_item_get_path(FolderItem *item)
750 g_return_val_if_fail(item != NULL, NULL);
752 if (FOLDER_TYPE(item->folder) == F_MH)
753 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
754 else if (FOLDER_TYPE(item->folder) == F_MBOX) {
755 path = mbox_get_virtual_path(item);
758 folder_path = g_strconcat(get_mbox_cache_dir(),
759 G_DIR_SEPARATOR_S, path, NULL);
764 else if (FOLDER_TYPE(item->folder) == F_IMAP) {
765 g_return_val_if_fail(item->folder->account != NULL, NULL);
766 folder_path = g_strconcat(get_imap_cache_dir(),
768 item->folder->account->recv_server,
770 item->folder->account->userid,
772 } else if (FOLDER_TYPE(item->folder) == F_NEWS) {
773 g_return_val_if_fail(item->folder->account != NULL, NULL);
774 folder_path = g_strconcat(get_news_cache_dir(),
776 item->folder->account->nntp_server,
781 g_return_val_if_fail(folder_path != NULL, NULL);
783 if (folder_path[0] == G_DIR_SEPARATOR) {
785 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
788 path = g_strdup(folder_path);
791 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
792 folder_path, G_DIR_SEPARATOR_S,
795 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
803 void folder_item_set_default_flags(FolderItem *dest, MsgFlags *flags)
805 if (!(dest->stype == F_OUTBOX ||
806 dest->stype == F_QUEUE ||
807 dest->stype == F_DRAFT ||
808 dest->stype == F_TRASH)) {
809 flags->perm_flags = MSG_NEW|MSG_UNREAD;
811 flags->perm_flags = 0;
813 flags->tmp_flags = MSG_CACHED;
814 if (dest->folder->type == F_MH) {
815 if (dest->stype == F_QUEUE) {
816 MSG_SET_TMP_FLAGS(*flags, MSG_QUEUED);
817 } else if (dest->stype == F_DRAFT) {
818 MSG_SET_TMP_FLAGS(*flags, MSG_DRAFT);
820 } else if (dest->folder->type == F_IMAP) {
821 MSG_SET_TMP_FLAGS(*flags, MSG_IMAP);
822 } else if (dest->folder->type == F_NEWS) {
823 MSG_SET_TMP_FLAGS(*flags, MSG_NEWS);
832 gint folder_item_scan(FolderItem *item)
835 GSList *folder_list, *cache_list, *elem, *new_list = NULL;
837 guint min = 0xffffffff, max = 0, cache_max = 0, maxgetcount = 0;
838 FolderScanInfo *folderscaninfo;
839 guint newcnt = 0, unreadcnt = 0, totalcnt = 0;
841 g_return_val_if_fail(item != NULL, -1);
842 if(item->path == NULL) return -1;
844 folder = item->folder;
846 g_return_val_if_fail(folder != NULL, -1);
847 g_return_val_if_fail(folder->get_num_list != NULL, -1);
849 debug_print(_("Scanning folder %s for cache changes.\n"), item->path);
851 /* Get list of messages for folder and cache */
852 if(!folder->check_msgnum_validity ||
853 folder->check_msgnum_validity(folder, item)) {
855 folder_item_read_cache(item);
856 cache_list = msgcache_get_msg_list(item->cache);
859 msgcache_destroy(item->cache);
860 item->cache = msgcache_new();
863 folder_list = folder->get_num_list(item->folder, item);
865 /* Get min und max number in folder */
866 for(elem = cache_list; elem != NULL; elem = elem->next) {
867 MsgInfo *msginfo = (MsgInfo *)elem->data;
869 min = MIN(msginfo->msgnum, min);
870 max = MAX(msginfo->msgnum, max);
873 for(elem = folder_list; elem != NULL; elem = elem->next) {
874 guint num = GPOINTER_TO_INT(elem->data);
880 debug_print("Folder message number range from %d to %d\n", min, max);
883 for(elem = cache_list; elem != NULL; elem = elem->next) {
884 MsgInfo *msginfo = (MsgInfo *)elem->data;
886 procmsg_msginfo_free(msginfo);
888 g_slist_free(folder_list);
889 g_slist_free(cache_list);
894 folderscaninfo = g_new0(FolderScanInfo, max - min + 1);
896 for(elem = folder_list; elem != NULL; elem = elem->next) {
897 guint num = GPOINTER_TO_INT(elem->data);
899 folderscaninfo[num - min] |= IN_FOLDER;
901 for(elem = cache_list; elem != NULL; elem = elem->next) {
902 MsgInfo *msginfo = (MsgInfo *)elem->data;
904 folderscaninfo[msginfo->msgnum - min] |= IN_CACHE;
905 procmsg_msginfo_free(msginfo);
908 for(i = max - min; i >= 0; i--) {
912 /* Add message to cache if in folder and not in cache */
913 if( (folderscaninfo[i] & IN_FOLDER) &&
914 !(folderscaninfo[i] & IN_CACHE) &&
915 (folder->type != F_NEWS ||
916 ((prefs_common.max_articles == 0) || (num > (max - prefs_common.max_articles))) &&
919 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(num));
920 debug_print(_("Remembered message %d for fetching\n"), num);
922 /* Remove message from cache if not in folder and in cache */
923 if(!(folderscaninfo[i] & IN_FOLDER) &&
924 (folderscaninfo[i] & IN_CACHE)) {
925 msgcache_remove_msg(item->cache, i + min);
926 debug_print(_("Removed message %d from cache.\n"), num);
928 /* Check if msginfo needs update if in cache and in folder */
929 if((folderscaninfo[i] & IN_FOLDER) &&
930 (folderscaninfo[i] & IN_CACHE) &&
931 (folder->is_msg_changed != NULL)) {
934 msginfo = msgcache_get_msg(item->cache, num);
935 if(folder->is_msg_changed(folder, item, msginfo)) {
938 msgcache_remove_msg(item->cache, msginfo->msgnum);
940 newmsginfo = folder->fetch_msginfo(folder, item, num);
941 msgcache_add_msg(item->cache, newmsginfo);
942 if(MSG_IS_NEW(newmsginfo->flags) && !MSG_IS_IGNORE_THREAD(newmsginfo->flags))
944 if(MSG_IS_UNREAD(newmsginfo->flags) && !MSG_IS_IGNORE_THREAD(newmsginfo->flags))
946 procmsg_msginfo_free(newmsginfo);
948 debug_print(_("Updated msginfo for message %d.\n"), num);
950 if(MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
952 if(MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
956 procmsg_msginfo_free(msginfo);
960 if(folder->fetch_msginfos) {
965 newmsg_list = folder->fetch_msginfos(folder, item, new_list);
966 for(elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) {
967 msginfo = (MsgInfo *) elem->data;
968 msgcache_add_msg(item->cache, msginfo);
969 if(MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
971 if(MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
974 procmsg_msginfo_free(msginfo);
976 g_slist_free(newmsg_list);
977 folderview_update_item(item, FALSE);
979 } else if (folder->fetch_msginfo) {
980 for(elem = new_list; elem != NULL; elem = g_slist_next(elem)) {
985 num = GPOINTER_TO_INT(elem->data);
986 msginfo = folder->fetch_msginfo(folder, item, num);
987 if(msginfo != NULL) {
988 msgcache_add_msg(item->cache, msginfo);
989 if(MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
991 if(MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
994 procmsg_msginfo_free(msginfo);
995 debug_print(_("Added newly found message %d to cache.\n"), num);
998 folderview_update_item(item, FALSE);
1002 item->unread = unreadcnt;
1003 item->total = totalcnt;
1005 g_slist_free(folder_list);
1006 g_slist_free(cache_list);
1007 g_slist_free(new_list);
1008 g_free(folderscaninfo);
1013 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
1016 folder_item_scan(FOLDER_ITEM(key));
1019 void folder_item_scan_foreach(GHashTable *table)
1021 g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
1024 void folder_count_total_cache_memusage(FolderItem *item, gpointer data)
1026 gint *memusage = (gint *)data;
1028 if(item->cache == NULL)
1031 *memusage += msgcache_get_memory_usage(item->cache);
1034 gint folder_cache_time_compare_func(gconstpointer a, gconstpointer b)
1036 FolderItem *fa = (FolderItem *)a;
1037 FolderItem *fb = (FolderItem *)b;
1039 return (gint) (msgcache_get_last_access_time(fa->cache) - msgcache_get_last_access_time(fb->cache));
1042 void folder_find_expired_caches(FolderItem *item, gpointer data)
1044 GSList **folder_item_list = (GSList **)data;
1045 gint difftime, expiretime;
1047 if(item->cache == NULL)
1050 difftime = (gint) (time(NULL) - msgcache_get_last_access_time(item->cache));
1051 expiretime = prefs_common.cache_min_keep_time * 60;
1052 debug_print(_("Cache unused time: %d (Expire time: %d)\n"), difftime, expiretime);
1053 if(difftime > expiretime) {
1054 *folder_item_list = g_slist_insert_sorted(*folder_item_list, item, folder_cache_time_compare_func);
1058 void folder_item_free_cache(FolderItem *item)
1060 g_return_if_fail(item != NULL);
1061 g_return_if_fail(item->cache != NULL);
1063 folder_item_write_cache(item);
1064 msgcache_destroy(item->cache);
1068 void folder_clean_cache_memory()
1072 folder_func_to_all_folders(folder_count_total_cache_memusage, &memusage);
1073 debug_print(_("Total cache memory usage: %d\n"), memusage);
1075 if(memusage > (prefs_common.cache_max_mem_usage * 1024)) {
1076 GSList *folder_item_list = NULL, *listitem;
1078 debug_print(_("Trying to free cache memory\n"));
1080 folder_func_to_all_folders(folder_find_expired_caches, &folder_item_list);
1081 listitem = folder_item_list;
1082 while((listitem != NULL) && (memusage > (prefs_common.cache_max_mem_usage * 1024))) {
1083 FolderItem *item = (FolderItem *)(listitem->data);
1085 debug_print(_("Freeing cache memory for %s\n"), item->path);
1086 memusage -= msgcache_get_memory_usage(item->cache);
1087 folder_item_free_cache(item);
1088 listitem = listitem->next;
1090 g_slist_free(folder_item_list);
1094 void folder_item_read_cache(FolderItem *item)
1096 gchar *cache_file, *mark_file;
1098 g_return_if_fail(item != NULL);
1100 cache_file = folder_item_get_cache_file(item);
1101 mark_file = folder_item_get_mark_file(item);
1102 item->cache = msgcache_read_cache(item, cache_file);
1104 item->cache = msgcache_new();
1105 folder_item_scan(item);
1107 msgcache_read_mark(item->cache, mark_file);
1111 folder_clean_cache_memory();
1114 void folder_item_write_cache(FolderItem *item)
1116 gchar *cache_file, *mark_file;
1117 PrefsFolderItem *prefs;
1121 if (!item || !item->path || !item->cache)
1124 id = folder_item_get_identifier(item);
1125 debug_print(_("Save cache for folder %s\n"), id);
1128 cache_file = folder_item_get_cache_file(item);
1129 mark_file = folder_item_get_mark_file(item);
1130 if(msgcache_write(cache_file, mark_file, item->cache) < 0) {
1131 prefs = item->prefs;
1132 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
1133 /* for cache file */
1134 filemode = prefs->folder_chmod;
1135 if (filemode & S_IRGRP) filemode |= S_IWGRP;
1136 if (filemode & S_IROTH) filemode |= S_IWOTH;
1137 chmod(cache_file, filemode);
1145 MsgInfo *folder_item_fetch_msginfo(FolderItem *item, gint num)
1150 g_return_val_if_fail(item != NULL, NULL);
1152 folder = item->folder;
1154 folder_item_read_cache(item);
1156 if(msginfo = msgcache_get_msg(item->cache, num))
1159 g_return_val_if_fail(folder->fetch_msginfo, NULL);
1160 msginfo = folder->fetch_msginfo(folder, item, num);
1164 GSList *folder_item_get_msg_list(FolderItem *item)
1166 g_return_val_if_fail(item != NULL, NULL);
1168 if(item->cache == 0)
1169 folder_item_read_cache(item);
1171 g_return_val_if_fail(item->cache != NULL, NULL);
1173 return msgcache_get_msg_list(item->cache);
1176 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
1180 g_return_val_if_fail(item != NULL, NULL);
1182 folder = item->folder;
1184 g_return_val_if_fail(folder->fetch_msg != NULL, NULL);
1186 return folder->fetch_msg(folder, item, num);
1189 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
1190 gboolean remove_source)
1196 g_return_val_if_fail(dest != NULL, -1);
1197 g_return_val_if_fail(file != NULL, -1);
1199 folder = dest->folder;
1201 g_return_val_if_fail(folder->add_msg != NULL, -1);
1204 folder_item_read_cache(dest);
1206 num = folder->add_msg(folder, dest, file, remove_source);
1209 msginfo = folder->fetch_msginfo(folder, dest, num);
1211 if(MSG_IS_NEW(msginfo->flags))
1213 if(MSG_IS_UNREAD(msginfo->flags))
1217 dest->last_num = num;
1218 msgcache_add_msg(dest->cache, msginfo);
1219 procmsg_msginfo_free(msginfo);
1226 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1231 g_return_val_if_fail(dest != NULL, -1);
1232 g_return_val_if_fail(msginfo != NULL, -1);
1234 folder = dest->folder;
1235 if (dest->last_num < 0) folder->scan(folder, dest);
1237 num = folder->move_msg(folder, dest, msginfo);
1238 if (num > 0) dest->last_num = num;
1244 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1249 Folder * src_folder;
1251 g_return_val_if_fail(dest != NULL, -1);
1252 g_return_val_if_fail(msginfo != NULL, -1);
1254 folder = dest->folder;
1256 g_return_val_if_fail(folder->remove_msg != NULL, -1);
1257 g_return_val_if_fail(folder->copy_msg != NULL, -1);
1259 if (!dest->cache) folder_item_read_cache(dest);
1261 src_folder = msginfo->folder->folder;
1263 num = folder->copy_msg(folder, dest, msginfo);
1266 MsgInfo *newmsginfo;
1268 newmsginfo = folder->fetch_msginfo(folder, dest, num);
1269 newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
1270 if (dest->stype == F_OUTBOX ||
1271 dest->stype == F_QUEUE ||
1272 dest->stype == F_DRAFT ||
1273 dest->stype == F_TRASH)
1274 MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
1275 MSG_NEW|MSG_UNREAD|MSG_DELETED);
1276 msgcache_add_msg(dest->cache, newmsginfo);
1279 if(src_folder->remove_msg) {
1280 src_folder->remove_msg(src_folder,
1284 msgcache_remove_msg(msginfo->folder->cache, msginfo->msgnum);
1286 if (MSG_IS_NEW(msginfo->flags))
1287 msginfo->folder->new--;
1288 if (MSG_IS_NEW(newmsginfo->flags))
1290 if (MSG_IS_UNREAD(msginfo->flags))
1291 msginfo->folder->unread--;
1292 if (MSG_IS_UNREAD(newmsginfo->flags))
1294 msginfo->folder->total--;
1297 procmsg_msginfo_free(newmsginfo);
1300 if (folder->finished_copy)
1301 folder->finished_copy(folder, dest);
1307 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
1312 g_return_val_if_fail(dest != NULL, -1);
1313 g_return_val_if_fail(msglist != NULL, -1);
1315 folder = dest->folder;
1316 if (dest->last_num < 0) folder->scan(folder, dest);
1318 num = folder->move_msgs_with_dest(folder, dest, msglist);
1319 if (num > 0) dest->last_num = num;
1320 else dest->op_count = 0;
1326 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
1334 g_return_val_if_fail(dest != NULL, -1);
1335 g_return_val_if_fail(msglist != NULL, -1);
1337 folder = dest->folder;
1339 g_return_val_if_fail(folder->copy_msg != NULL, -1);
1340 g_return_val_if_fail(folder->remove_msg != NULL, -1);
1342 if (!dest->cache) folder_item_read_cache(dest);
1345 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1346 MsgInfo * msginfo = (MsgInfo *) l->data;
1348 if (!item && msginfo->folder != NULL)
1349 item = msginfo->folder;
1350 if (!item->cache) folder_item_read_cache(dest);
1352 num = folder->copy_msg(folder, dest, msginfo);
1354 MsgInfo *newmsginfo;
1356 newmsginfo = folder->fetch_msginfo(folder, dest, num);
1358 newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
1359 if (dest->stype == F_OUTBOX ||
1360 dest->stype == F_QUEUE ||
1361 dest->stype == F_DRAFT ||
1362 dest->stype == F_TRASH)
1363 MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
1364 MSG_NEW|MSG_UNREAD|MSG_DELETED);
1365 msgcache_add_msg(dest->cache, newmsginfo);
1367 if (MSG_IS_NEW(msginfo->flags))
1368 msginfo->folder->new--;
1369 if (MSG_IS_NEW(newmsginfo->flags))
1371 if (MSG_IS_UNREAD(msginfo->flags))
1372 msginfo->folder->unread--;
1373 if (MSG_IS_UNREAD(newmsginfo->flags))
1375 msginfo->folder->total--;
1378 procmsg_msginfo_free(newmsginfo);
1380 item->folder->remove_msg(item->folder,
1383 msgcache_remove_msg(item->cache, msginfo->msgnum);
1387 if (folder->finished_copy)
1388 folder->finished_copy(folder, dest);
1390 return dest->last_num;
1394 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
1399 g_return_val_if_fail(dest != NULL, -1);
1400 g_return_val_if_fail(msginfo != NULL, -1);
1402 folder = dest->folder;
1403 if (dest->last_num < 0) folder->scan(folder, dest);
1405 num = folder->copy_msg(folder, dest, msginfo);
1406 if (num > 0) dest->last_num = num;
1412 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
1417 Folder * src_folder;
1419 g_return_val_if_fail(dest != NULL, -1);
1420 g_return_val_if_fail(msginfo != NULL, -1);
1422 folder = dest->folder;
1424 g_return_val_if_fail(folder->copy_msg != NULL, -1);
1426 if (!dest->cache) folder_item_read_cache(dest);
1428 num = folder->copy_msg(folder, dest, msginfo);
1430 MsgInfo *newmsginfo;
1432 newmsginfo = folder->fetch_msginfo(folder, dest, num);
1433 newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
1434 if (dest->stype == F_OUTBOX ||
1435 dest->stype == F_QUEUE ||
1436 dest->stype == F_DRAFT ||
1437 dest->stype == F_TRASH)
1438 MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
1439 MSG_NEW|MSG_UNREAD|MSG_DELETED);
1440 msgcache_add_msg(dest->cache, newmsginfo);
1442 if (MSG_IS_NEW(newmsginfo->flags))
1444 if (MSG_IS_UNREAD(newmsginfo->flags))
1448 procmsg_msginfo_free(newmsginfo);
1451 if (folder->finished_copy)
1452 folder->finished_copy(folder, dest);
1458 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
1463 g_return_val_if_fail(dest != NULL, -1);
1464 g_return_val_if_fail(msglist != NULL, -1);
1466 folder = dest->folder;
1467 if (dest->last_num < 0) folder->scan(folder, dest);
1469 num = folder->copy_msgs_with_dest(folder, dest, msglist);
1470 if (num > 0) dest->last_num = num;
1471 else dest->op_count = 0;
1477 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
1484 g_return_val_if_fail(dest != NULL, -1);
1485 g_return_val_if_fail(msglist != NULL, -1);
1487 folder = dest->folder;
1489 g_return_val_if_fail(folder->copy_msg != NULL, -1);
1491 if (!dest->cache) folder_item_read_cache(dest);
1493 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1494 MsgInfo * msginfo = (MsgInfo *) l->data;
1496 num = folder->copy_msg(folder, dest, msginfo);
1498 MsgInfo *newmsginfo;
1500 newmsginfo = folder->fetch_msginfo(folder, dest, num);
1502 newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
1503 if (dest->stype == F_OUTBOX ||
1504 dest->stype == F_QUEUE ||
1505 dest->stype == F_DRAFT ||
1506 dest->stype == F_TRASH)
1507 MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
1508 MSG_NEW|MSG_UNREAD|MSG_DELETED);
1509 msgcache_add_msg(dest->cache, newmsginfo);
1511 if (MSG_IS_NEW(newmsginfo->flags))
1513 if (MSG_IS_UNREAD(newmsginfo->flags))
1517 procmsg_msginfo_free(newmsginfo);
1522 if (folder->finished_copy)
1523 folder->finished_copy(folder, dest);
1525 return dest->last_num;
1528 gint folder_item_remove_msg(FolderItem *item, gint num)
1534 g_return_val_if_fail(item != NULL, -1);
1536 folder = item->folder;
1537 if (!item->cache) folder_item_read_cache(item);
1539 ret = folder->remove_msg(folder, item, num);
1541 msginfo = msgcache_get_msg(item->cache, num);
1542 if(MSG_IS_NEW(msginfo->flags))
1544 if(MSG_IS_UNREAD(msginfo->flags))
1547 procmsg_msginfo_free(msginfo);
1548 msgcache_remove_msg(item->cache, num);
1553 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
1557 g_return_val_if_fail(item != NULL, -1);
1559 if (!item->cache) folder_item_read_cache(item);
1561 while (msglist != NULL) {
1562 MsgInfo *msginfo = (MsgInfo *)msglist->data;
1564 ret = folder_item_remove_msg(item, msginfo->msgnum);
1565 if (ret != 0) break;
1566 msgcache_remove_msg(item->cache, msginfo->msgnum);
1567 msglist = msglist->next;
1573 gint folder_item_remove_all_msg(FolderItem *item)
1578 g_return_val_if_fail(item != NULL, -1);
1580 folder = item->folder;
1582 g_return_val_if_fail(folder->remove_all_msg != NULL, -1);
1584 result = folder->remove_all_msg(folder, item);
1587 if (folder->finished_remove)
1588 folder->finished_remove(folder, item);
1594 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
1598 g_return_val_if_fail(item != NULL, FALSE);
1600 folder = item->folder;
1602 g_return_val_if_fail(folder->is_msg_changed != NULL, -1);
1604 return folder->is_msg_changed(folder, item, msginfo);
1607 gchar *folder_item_get_cache_file(FolderItem *item)
1612 g_return_val_if_fail(item != NULL, NULL);
1613 g_return_val_if_fail(item->path != NULL, NULL);
1615 path = folder_item_get_path(item);
1616 g_return_val_if_fail(path != NULL, NULL);
1617 if (!is_dir_exist(path))
1618 make_dir_hier(path);
1619 file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
1625 gchar *folder_item_get_mark_file(FolderItem *item)
1630 g_return_val_if_fail(item != NULL, NULL);
1631 g_return_val_if_fail(item->path != NULL, NULL);
1633 path = folder_item_get_path(item);
1634 g_return_val_if_fail(path != NULL, NULL);
1635 if (!is_dir_exist(path))
1636 make_dir_hier(path);
1637 file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
1643 static gboolean folder_build_tree(GNode *node, gpointer data)
1645 Folder *folder = FOLDER(data);
1649 SpecialFolderItemType stype = F_NORMAL;
1650 const gchar *name = NULL;
1651 const gchar *path = NULL;
1652 PrefsAccount *account = NULL;
1653 gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE,
1654 threaded = TRUE, ret_rcpt = FALSE, hidereadmsgs = FALSE;
1655 FolderSortKey sort_key = SORT_BY_NONE;
1656 FolderSortType sort_type = SORT_ASCENDING;
1657 gint new = 0, unread = 0, total = 0;
1660 g_return_val_if_fail(node->data != NULL, FALSE);
1661 if (!node->parent) return FALSE;
1663 xmlnode = node->data;
1664 if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1665 g_warning("tag name != \"folderitem\"\n");
1669 list = xmlnode->tag->attr;
1670 for (; list != NULL; list = list->next) {
1671 XMLAttr *attr = list->data;
1673 if (!attr || !attr->name || !attr->value) continue;
1674 if (!strcmp(attr->name, "type")) {
1675 if (!strcasecmp(attr->value, "normal"))
1677 else if (!strcasecmp(attr->value, "inbox"))
1679 else if (!strcasecmp(attr->value, "outbox"))
1681 else if (!strcasecmp(attr->value, "draft"))
1683 else if (!strcasecmp(attr->value, "queue"))
1685 else if (!strcasecmp(attr->value, "trash"))
1687 } else if (!strcmp(attr->name, "name"))
1689 else if (!strcmp(attr->name, "path"))
1691 else if (!strcmp(attr->name, "account_id")) {
1692 account = account_find_from_id(atoi(attr->value));
1693 if (!account) g_warning("account_id: %s not found\n",
1695 } else if (!strcmp(attr->name, "mtime"))
1696 mtime = strtoul(attr->value, NULL, 10);
1697 else if (!strcmp(attr->name, "new"))
1698 new = atoi(attr->value);
1699 else if (!strcmp(attr->name, "unread"))
1700 unread = atoi(attr->value);
1701 else if (!strcmp(attr->name, "total"))
1702 total = atoi(attr->value);
1703 else if (!strcmp(attr->name, "no_sub"))
1704 no_sub = *attr->value == '1' ? TRUE : FALSE;
1705 else if (!strcmp(attr->name, "no_select"))
1706 no_select = *attr->value == '1' ? TRUE : FALSE;
1707 else if (!strcmp(attr->name, "collapsed"))
1708 collapsed = *attr->value == '1' ? TRUE : FALSE;
1709 else if (!strcmp(attr->name, "threaded"))
1710 threaded = *attr->value == '1' ? TRUE : FALSE;
1711 else if (!strcmp(attr->name, "hidereadmsgs"))
1712 hidereadmsgs = *attr->value == '1' ? TRUE : FALSE;
1713 else if (!strcmp(attr->name, "reqretrcpt"))
1714 ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
1715 else if (!strcmp(attr->name, "sort_key")) {
1716 if (!strcmp(attr->value, "none"))
1717 sort_key = SORT_BY_NONE;
1718 else if (!strcmp(attr->value, "number"))
1719 sort_key = SORT_BY_NUMBER;
1720 else if (!strcmp(attr->value, "size"))
1721 sort_key = SORT_BY_SIZE;
1722 else if (!strcmp(attr->value, "date"))
1723 sort_key = SORT_BY_DATE;
1724 else if (!strcmp(attr->value, "from"))
1725 sort_key = SORT_BY_FROM;
1726 else if (!strcmp(attr->value, "subject"))
1727 sort_key = SORT_BY_SUBJECT;
1728 else if (!strcmp(attr->value, "score"))
1729 sort_key = SORT_BY_SCORE;
1730 else if (!strcmp(attr->value, "label"))
1731 sort_key = SORT_BY_LABEL;
1732 else if (!strcmp(attr->value, "mark"))
1733 sort_key = SORT_BY_MARK;
1734 else if (!strcmp(attr->value, "unread"))
1735 sort_key = SORT_BY_UNREAD;
1736 else if (!strcmp(attr->value, "mime"))
1737 sort_key = SORT_BY_MIME;
1738 else if (!strcmp(attr->value, "locked"))
1739 sort_key = SORT_BY_LOCKED;
1740 } else if (!strcmp(attr->name, "sort_type")) {
1741 if (!strcmp(attr->value, "ascending"))
1742 sort_type = SORT_ASCENDING;
1744 sort_type = SORT_DESCENDING;
1748 item = folder_item_new(name, path);
1749 item->stype = stype;
1750 item->account = account;
1751 item->mtime = mtime;
1753 item->unread = unread;
1754 item->total = total;
1755 item->no_sub = no_sub;
1756 item->no_select = no_select;
1757 item->collapsed = collapsed;
1758 item->threaded = threaded;
1759 item->hide_read_msgs = hidereadmsgs;
1760 item->ret_rcpt = ret_rcpt;
1761 item->sort_key = sort_key;
1762 item->sort_type = sort_type;
1763 item->parent = FOLDER_ITEM(node->parent->data);
1764 item->folder = folder;
1766 case F_INBOX: folder->inbox = item; break;
1767 case F_OUTBOX: folder->outbox = item; break;
1768 case F_DRAFT: folder->draft = item; break;
1769 case F_QUEUE: folder->queue = item; break;
1770 case F_TRASH: folder->trash = item; break;
1774 prefs_folder_item_read_config(item);
1777 xml_free_node(xmlnode);
1782 static gboolean folder_read_folder_func(GNode *node, gpointer data)
1787 FolderType type = F_UNKNOWN;
1788 const gchar *name = NULL;
1789 const gchar *path = NULL;
1790 PrefsAccount *account = NULL;
1791 gboolean collapsed = FALSE, threaded = TRUE, ret_rcpt = FALSE;
1793 if (g_node_depth(node) != 2) return FALSE;
1794 g_return_val_if_fail(node->data != NULL, FALSE);
1796 xmlnode = node->data;
1797 if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1798 g_warning("tag name != \"folder\"\n");
1801 g_node_unlink(node);
1802 list = xmlnode->tag->attr;
1803 for (; list != NULL; list = list->next) {
1804 XMLAttr *attr = list->data;
1806 if (!attr || !attr->name || !attr->value) continue;
1807 if (!strcmp(attr->name, "type")) {
1808 if (!strcasecmp(attr->value, "mh"))
1810 else if (!strcasecmp(attr->value, "mbox"))
1812 else if (!strcasecmp(attr->value, "maildir"))
1814 else if (!strcasecmp(attr->value, "imap"))
1816 else if (!strcasecmp(attr->value, "news"))
1818 } else if (!strcmp(attr->name, "name"))
1820 else if (!strcmp(attr->name, "path"))
1822 else if (!strcmp(attr->name, "account_id")) {
1823 account = account_find_from_id(atoi(attr->value));
1824 if (!account) g_warning("account_id: %s not found\n",
1826 } else if (!strcmp(attr->name, "collapsed"))
1827 collapsed = *attr->value == '1' ? TRUE : FALSE;
1828 else if (!strcmp(attr->name, "threaded"))
1829 threaded = *attr->value == '1' ? TRUE : FALSE;
1830 else if (!strcmp(attr->name, "reqretrcpt"))
1831 ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
1834 folder = folder_new(type, name, path);
1835 g_return_val_if_fail(folder != NULL, FALSE);
1836 folder->account = account;
1837 if (account && (type == F_IMAP || type == F_NEWS))
1838 account->folder = REMOTE_FOLDER(folder);
1839 node->data = folder->node->data;
1840 g_node_destroy(folder->node);
1841 folder->node = node;
1843 FOLDER_ITEM(node->data)->collapsed = collapsed;
1844 FOLDER_ITEM(node->data)->threaded = threaded;
1845 FOLDER_ITEM(node->data)->ret_rcpt = ret_rcpt;
1847 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1848 folder_build_tree, folder);
1853 static gchar *folder_get_list_path(void)
1855 static gchar *filename = NULL;
1858 filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1864 static void folder_write_list_recursive(GNode *node, gpointer data)
1866 FILE *fp = (FILE *)data;
1867 FolderItem *item = FOLDER_ITEM(node->data);
1869 static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1871 static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1872 "draft", "queue", "trash"};
1873 static gchar *sort_key_str[] = {"none", "number", "size", "date",
1874 "from", "subject", "score", "label",
1875 "mark", "unread", "mime", "locked" };
1877 g_return_if_fail(item != NULL);
1879 depth = g_node_depth(node);
1880 for (i = 0; i < depth; i++)
1883 Folder *folder = item->folder;
1885 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
1887 fputs(" name=\"", fp);
1888 xml_file_put_escape_str(fp, folder->name);
1891 if ((folder->type == F_MH) || (folder->type == F_MBOX)) {
1892 fputs(" path=\"", fp);
1893 xml_file_put_escape_str
1894 (fp, LOCAL_FOLDER(folder)->rootpath);
1897 if (folder->account)
1898 fprintf(fp, " account_id=\"%d\"",
1899 folder->account->account_id);
1900 if (item->collapsed && node->children)
1901 fputs(" collapsed=\"1\"", fp);
1903 fputs(" reqretrcpt=\"1\"", fp);
1905 fprintf(fp, "<folderitem type=\"%s\"",
1906 folder_item_stype_str[item->stype]);
1908 fputs(" name=\"", fp);
1909 xml_file_put_escape_str(fp, item->name);
1913 fputs(" path=\"", fp);
1914 xml_file_put_escape_str(fp, item->path);
1918 fprintf(fp, " account_id=\"%d\"",
1919 item->account->account_id);
1921 fputs(" no_sub=\"1\"", fp);
1922 if (item->no_select)
1923 fputs(" no_select=\"1\"", fp);
1924 if (item->collapsed && node->children)
1925 fputs(" collapsed=\"1\"", fp);
1927 fputs(" threaded=\"1\"", fp);
1929 fputs(" threaded=\"0\"", fp);
1930 if (item->hide_read_msgs)
1931 fputs(" hidereadmsgs=\"1\"", fp);
1933 fputs(" hidereadmsgs=\"0\"", fp);
1935 fputs(" reqretrcpt=\"1\"", fp);
1937 if (item->sort_key != SORT_BY_NONE) {
1938 fprintf(fp, " sort_key=\"%s\"",
1939 sort_key_str[item->sort_key]);
1940 if (item->sort_type == SORT_ASCENDING)
1941 fprintf(fp, " sort_type=\"ascending\"");
1943 fprintf(fp, " sort_type=\"descending\"");
1947 " mtime=\"%lu\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1948 item->mtime, item->new, item->unread, item->total);
1951 if (node->children) {
1955 child = node->children;
1961 folder_write_list_recursive(cur, data);
1964 for (i = 0; i < depth; i++)
1966 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1971 static void folder_update_op_count_rec(GNode *node) {
1972 FolderItem *fitem = FOLDER_ITEM(node->data);
1974 if (g_node_depth(node) > 0) {
1975 if (fitem->op_count > 0) {
1976 fitem->op_count = 0;
1977 folderview_update_item(fitem, 0);
1979 if (node->children) {
1982 child = node->children;
1988 folder_update_op_count_rec(cur);
1994 void folder_update_op_count() {
1998 for (cur = folder_list; cur != NULL; cur = cur->next) {
2000 folder_update_op_count_rec(folder->node);
2004 typedef struct _type_str {
2011 static gchar * folder_item_get_tree_identifier(FolderItem * item)
2013 if (item->parent != NULL) {
2017 path = folder_item_get_tree_identifier(item->parent);
2021 id = g_strconcat(path, "/", item->name, NULL);
2027 return g_strconcat("/", item->name, NULL);
2032 /* CLAWS: temporary local folder for filtering */
2033 static Folder *processing_folder;
2034 static FolderItem *processing_folder_item;
2036 static void folder_create_processing_folder(void)
2038 #define PROCESSING_FOLDER ".processing"
2040 FolderItem *tmpfolder;
2043 tmpparent = folder_get_default_folder();
2044 g_assert(tmpparent);
2045 debug_print("tmpparentroot %s\n", LOCAL_FOLDER(tmpparent)->rootpath);
2046 if (LOCAL_FOLDER(tmpparent)->rootpath[0] == '/')
2047 tmpname = g_strconcat(LOCAL_FOLDER(tmpparent)->rootpath,
2048 G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
2051 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2052 LOCAL_FOLDER(tmpparent)->rootpath,
2053 G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
2056 processing_folder = folder_new(F_MH, "PROCESSING", LOCAL_FOLDER(tmpparent)->rootpath);
2057 g_assert(processing_folder);
2059 if (!is_dir_exist(tmpname)) {
2060 debug_print("*TMP* creating %s\n", tmpname);
2061 processing_folder_item = processing_folder->create_folder(processing_folder,
2062 processing_folder->node->data,
2064 g_assert(processing_folder_item);
2067 debug_print("*TMP* already created\n");
2068 processing_folder_item = folder_item_new(".processing", ".processing");
2069 g_assert(processing_folder_item);
2070 folder_item_append(processing_folder->node->data, processing_folder_item);
2075 FolderItem *folder_get_default_processing(void)
2077 if (!processing_folder_item) {
2078 folder_create_processing_folder();
2080 return processing_folder_item;
2083 /* folder_persist_prefs_new() - return hash table with persistent
2084 * settings (and folder name as key).
2085 * (note that in claws other options are in the PREFS_FOLDER_ITEM_RC
2086 * file, so those don't need to be included in PersistPref yet)
2088 GHashTable *folder_persist_prefs_new(Folder *folder)
2090 GHashTable *pptable;
2092 g_return_val_if_fail(folder, NULL);
2093 pptable = g_hash_table_new(g_str_hash, g_str_equal);
2094 folder_get_persist_prefs_recursive(folder->node, pptable);
2098 void folder_persist_prefs_free(GHashTable *pptable)
2100 g_return_if_fail(pptable);
2101 g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
2102 g_hash_table_destroy(pptable);
2105 const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
2107 if (pptable == NULL || name == NULL) return NULL;
2108 return g_hash_table_lookup(pptable, name);
2111 void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
2113 const PersistPrefs *pp;
2115 pp = folder_get_persist_prefs(pptable, item->path);
2118 /* CLAWS: since not all folder properties have been migrated to
2119 * folderlist.xml, we need to call the old stuff first before
2120 * setting things that apply both to Main and Claws. */
2121 prefs_folder_item_read_config(item);
2123 item->collapsed = pp->collapsed;
2124 item->threaded = pp->threaded;
2125 item->ret_rcpt = pp->ret_rcpt;
2126 item->hide_read_msgs = pp->hide_read_msgs;
2127 item->sort_key = pp->sort_key;
2128 item->sort_type = pp->sort_type;
2131 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
2133 FolderItem *item = FOLDER_ITEM(node->data);
2137 g_return_if_fail(node != NULL);
2138 g_return_if_fail(item != NULL);
2140 /* FIXME: item->path == NULL for top level folder, so this means that
2141 * properties of MH folder root will not be stored. Not quite important,
2142 * because the top level folder properties are not special anyway. */
2144 pp = g_new0(PersistPrefs, 1);
2145 g_return_if_fail(pp != NULL);
2146 pp->collapsed = item->collapsed;
2147 pp->threaded = item->threaded;
2148 pp->ret_rcpt = item->ret_rcpt;
2149 pp->hide_read_msgs = item->hide_read_msgs;
2150 pp->sort_key = item->sort_key;
2151 pp->sort_type = item->sort_type;
2152 g_hash_table_insert(pptable, item->path, pp);
2155 if (node->children) {
2156 child = node->children;
2160 folder_get_persist_prefs_recursive(cur, pptable);
2165 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)