868cf5073c4d8092d82e8c75190be9e1b082fddf
[claws.git] / src / folder.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto
4  *
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.
9  *
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.
14  *
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.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33
34 #include "intl.h"
35 #include "folder.h"
36 #include "session.h"
37 #include "imap.h"
38 #include "news.h"
39 #include "mh.h"
40 #include "mbox_folder.h"
41 #include "utils.h"
42 #include "xml.h"
43 #include "codeconv.h"
44 #include "prefs_gtk.h"
45 #include "account.h"
46 #include "filtering.h"
47 #include "scoring.h"
48 #include "prefs_folder_item.h"
49 #include "procheader.h"
50 #include "hooks.h"
51 #include "log.h"
52
53 /* Dependecies to be removed ?! */
54 #include "prefs_common.h"
55 #include "prefs_account.h"
56 #include "prefs_folder_item.h"
57
58 static GList *folder_list = NULL;
59
60 static void folder_init         (Folder         *folder,
61                                  const gchar    *name);
62
63 static gboolean folder_read_folder_func (GNode          *node,
64                                          gpointer        data);
65 static gchar *folder_get_list_path      (void);
66 static void folder_write_list_recursive (GNode          *node,
67                                          gpointer        data);
68 static void folder_update_op_count_rec  (GNode          *node);
69
70
71 static void folder_get_persist_prefs_recursive
72                                         (GNode *node, GHashTable *pptable);
73 static gboolean persist_prefs_free      (gpointer key, gpointer val, gpointer data);
74 void folder_item_read_cache             (FolderItem *item);
75 void folder_item_free_cache             (FolderItem *item);
76 gint folder_item_scan_full              (FolderItem *item, gboolean filtering);
77
78 static GSList *classlist;
79
80 void folder_system_init(void)
81 {
82         folder_register_class(mh_get_class());
83         folder_register_class(imap_get_class());
84         folder_register_class(news_get_class());
85         folder_register_class(mbox_get_class());
86 }
87
88 GSList *folder_get_class_list(void)
89 {
90         return classlist;
91 }
92
93 void folder_register_class(FolderClass *klass)
94 {
95         debug_print("registering folder class %s\n", klass->idstr);
96         classlist = g_slist_append(classlist, klass);
97 }
98
99 Folder *folder_new(FolderClass *klass, const gchar *name, const gchar *path)
100 {
101         Folder *folder = NULL;
102         FolderItem *item;
103
104         g_return_val_if_fail(klass != NULL, NULL);
105
106         name = name ? name : path;
107         folder = klass->new_folder(name, path);
108
109         /* Create root folder item */
110         item = folder_item_new(folder, name, NULL);
111         item->folder = folder;
112         folder->node = g_node_new(item);
113         folder->data = NULL;
114
115         return folder;
116 }
117
118 static void folder_init(Folder *folder, const gchar *name)
119 {
120         g_return_if_fail(folder != NULL);
121
122         folder_set_name(folder, name);
123
124         /* Init folder data */
125         folder->account = NULL;
126         folder->inbox = NULL;
127         folder->outbox = NULL;
128         folder->draft = NULL;
129         folder->queue = NULL;
130         folder->trash = NULL;
131 }
132
133 void folder_local_folder_init(Folder *folder, const gchar *name,
134                               const gchar *path)
135 {
136         folder_init(folder, name);
137         LOCAL_FOLDER(folder)->rootpath = g_strdup(path);
138 }
139
140 void folder_remote_folder_init(Folder *folder, const gchar *name,
141                                const gchar *path)
142 {
143         folder_init(folder, name);
144         REMOTE_FOLDER(folder)->session = NULL;
145 }
146
147 void folder_destroy(Folder *folder)
148 {
149         g_return_if_fail(folder != NULL);
150         g_return_if_fail(folder->klass->destroy_folder != NULL);
151
152         folder_list = g_list_remove(folder_list, folder);
153
154         folder_tree_destroy(folder);
155
156         folder->klass->destroy_folder(folder);
157
158         g_free(folder->name);
159         g_free(folder);
160 }
161
162 void folder_local_folder_destroy(LocalFolder *lfolder)
163 {
164         g_return_if_fail(lfolder != NULL);
165
166         g_free(lfolder->rootpath);
167 }
168
169 void folder_remote_folder_destroy(RemoteFolder *rfolder)
170 {
171         g_return_if_fail(rfolder != NULL);
172
173         if (rfolder->session)
174                 session_destroy(rfolder->session);
175 }
176
177 FolderItem *folder_item_new(Folder *folder, const gchar *name, const gchar *path)
178 {
179         FolderItem *item = NULL;
180
181         if (folder->klass->item_new) {
182                 item = folder->klass->item_new(folder);
183         } else {
184                 item = g_new0(FolderItem, 1);
185         }
186
187         g_return_val_if_fail(item != NULL, NULL);
188
189         item->stype = F_NORMAL;
190         item->name = g_strdup(name);
191         item->path = g_strdup(path);
192         item->mtime = 0;
193         item->new_msgs = 0;
194         item->unread_msgs = 0;
195         item->unreadmarked_msgs = 0;
196         item->total_msgs = 0;
197         item->last_num = -1;
198         item->cache = NULL;
199         item->no_sub = FALSE;
200         item->no_select = FALSE;
201         item->collapsed = FALSE;
202         item->thread_collapsed = FALSE;
203         item->threaded  = TRUE;
204         item->ret_rcpt  = FALSE;
205         item->opened    = FALSE;
206         item->parent = NULL;
207         item->folder = NULL;
208         item->account = NULL;
209         item->apply_sub = FALSE;
210         item->mark_queue = NULL;
211         item->data = NULL;
212
213         item->prefs = prefs_folder_item_new();
214
215         return item;
216 }
217
218 void folder_item_append(FolderItem *parent, FolderItem *item)
219 {
220         GNode *node;
221
222         g_return_if_fail(parent != NULL);
223         g_return_if_fail(parent->folder != NULL);
224         g_return_if_fail(item != NULL);
225
226         node = parent->folder->node;
227         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, parent);
228         g_return_if_fail(node != NULL);
229
230         item->parent = parent;
231         item->folder = parent->folder;
232         g_node_append_data(node, item);
233 }
234
235 void folder_item_remove(FolderItem *item)
236 {
237         GNode *node;
238
239         g_return_if_fail(item != NULL);
240         g_return_if_fail(item->folder != NULL);
241
242         node = item->folder->node;
243         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
244         g_return_if_fail(node != NULL);
245
246         /* TODO: free all FolderItem's first */
247         if (item->folder->node == node)
248                 item->folder->node = NULL;
249         g_node_destroy(node);
250 }
251
252 void folder_item_destroy(FolderItem *item)
253 {
254         g_return_if_fail(item != NULL);
255
256         debug_print("Destroying folder item %s\n", item->path);
257
258         if (item->cache)
259                 folder_item_free_cache(item);
260         g_free(item->name);
261         g_free(item->path);
262
263         if (item->folder != NULL) {
264                 if(item->folder->klass->item_destroy) {
265                         item->folder->klass->item_destroy(item->folder, item);
266                 } else {
267                         g_free(item);
268                 }
269         }
270 }
271
272 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
273 {
274         g_return_if_fail(folder != NULL);
275
276         folder->ui_func = func;
277         folder->ui_func_data = data;
278 }
279
280 void folder_set_name(Folder *folder, const gchar *name)
281 {
282         g_return_if_fail(folder != NULL);
283
284         g_free(folder->name);
285         folder->name = name ? g_strdup(name) : NULL;
286         if (folder->node && folder->node->data) {
287                 FolderItem *item = (FolderItem *)folder->node->data;
288
289                 g_free(item->name);
290                 item->name = name ? g_strdup(name) : NULL;
291         }
292 }
293
294 gboolean folder_tree_destroy_func(GNode *node, gpointer data) {
295         FolderItem *item = (FolderItem *) node->data;
296
297         folder_item_destroy(item);
298         return FALSE;
299 }
300
301 void folder_tree_destroy(Folder *folder)
302 {
303         g_return_if_fail(folder != NULL);
304         g_return_if_fail(folder->node != NULL);
305         
306         prefs_scoring_clear_folder(folder);
307         prefs_filtering_clear_folder(folder);
308
309         g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_tree_destroy_func, NULL);
310         if (folder->node)
311                 g_node_destroy(folder->node);
312
313         folder->inbox = NULL;
314         folder->outbox = NULL;
315         folder->draft = NULL;
316         folder->queue = NULL;
317         folder->trash = NULL;
318         folder->node = NULL;
319 }
320
321 void folder_add(Folder *folder)
322 {
323         Folder *cur_folder;
324         GList *cur;
325         gint i;
326
327         g_return_if_fail(folder != NULL);
328
329         for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
330                 cur_folder = FOLDER(cur->data);
331                 if (FOLDER_TYPE(folder) == F_MH) {
332                         if (FOLDER_TYPE(cur_folder) != F_MH) break;
333                 } else if (FOLDER_TYPE(folder) == F_MBOX) {
334                         if (FOLDER_TYPE(cur_folder) != F_MH &&
335                             FOLDER_TYPE(cur_folder) != F_MBOX) break;
336                 } else if (FOLDER_TYPE(folder) == F_IMAP) {
337                         if (FOLDER_TYPE(cur_folder) != F_MH &&
338                             FOLDER_TYPE(cur_folder) != F_MBOX &&
339                             FOLDER_TYPE(cur_folder) != F_IMAP) break;
340                 } else if (FOLDER_TYPE(folder) == F_NEWS) {
341                         if (FOLDER_TYPE(cur_folder) != F_MH &&
342                             FOLDER_TYPE(cur_folder) != F_MBOX &&
343                             FOLDER_TYPE(cur_folder) != F_IMAP &&
344                             FOLDER_TYPE(cur_folder) != F_NEWS) break;
345                 }
346         }
347
348         folder_list = g_list_insert(folder_list, folder, i);
349 }
350
351 GList *folder_get_list(void)
352 {
353         return folder_list;
354 }
355
356 gint folder_read_list(void)
357 {
358         GNode *node;
359         XMLNode *xmlnode;
360         gchar *path;
361
362         path = folder_get_list_path();
363         if (!is_file_exist(path)) return -1;
364         node = xml_parse_file(path);
365         if (!node) return -1;
366
367         xmlnode = node->data;
368         if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
369                 g_warning("wrong folder list\n");
370                 xml_free_tree(node);
371                 return -1;
372         }
373
374         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
375                         folder_read_folder_func, NULL);
376
377         xml_free_tree(node);
378         if (folder_list)
379                 return 0;
380         else
381                 return -1;
382 }
383
384 void folder_write_list(void)
385 {
386         GList *list;
387         Folder *folder;
388         gchar *path;
389         PrefFile *pfile;
390
391         path = folder_get_list_path();
392         if ((pfile = prefs_write_open(path)) == NULL) return;
393
394         fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
395                 conv_get_current_charset_str());
396         fputs("\n<folderlist>\n", pfile->fp);
397
398         for (list = folder_list; list != NULL; list = list->next) {
399                 folder = list->data;
400                 folder_write_list_recursive(folder->node, pfile->fp);
401         }
402
403         fputs("</folderlist>\n", pfile->fp);
404
405         if (prefs_file_close(pfile) < 0)
406                 g_warning("failed to write folder list.\n");
407 }
408
409 gboolean folder_scan_tree_func(GNode *node, gpointer data)
410 {
411         GHashTable *pptable = (GHashTable *)data;
412         FolderItem *item = (FolderItem *)node->data;
413         
414         folder_item_restore_persist_prefs(item, pptable);
415         folder_item_scan_full(item, FALSE);
416
417         return FALSE;
418 }
419
420 void folder_scan_tree(Folder *folder)
421 {
422         GHashTable *pptable;
423         
424         if (!folder->klass->scan_tree)
425                 return;
426         
427         pptable = folder_persist_prefs_new(folder);
428
429         /*
430          * should be changed and tree update should be done without 
431          * destroying the tree first
432          */
433         folder_tree_destroy(folder);
434         folder->klass->scan_tree(folder);
435
436         g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_scan_tree_func, pptable);
437         folder_persist_prefs_free(pptable);
438
439         prefs_matcher_read_config();
440
441         folder_write_list();
442 }
443
444 FolderItem *folder_create_folder(FolderItem *parent, const gchar *name)
445 {
446         FolderItem *new_item;
447
448         new_item = parent->folder->klass->create_folder(parent->folder, parent, name);
449         if (new_item)
450                 new_item->cache = msgcache_new();
451
452         return new_item;
453 }
454
455 struct TotalMsgCount
456 {
457         guint new_msgs;
458         guint unread_msgs;
459         guint unreadmarked_msgs;
460         guint total_msgs;
461 };
462
463 struct FuncToAllFoldersData
464 {
465         FolderItemFunc  function;
466         gpointer        data;
467 };
468
469 static gboolean folder_func_to_all_folders_func(GNode *node, gpointer data)
470 {
471         FolderItem *item;
472         struct FuncToAllFoldersData *function_data = (struct FuncToAllFoldersData *) data;
473
474         g_return_val_if_fail(node->data != NULL, FALSE);
475
476         item = FOLDER_ITEM(node->data);
477         g_return_val_if_fail(item != NULL, FALSE);
478
479         function_data->function(item, function_data->data);
480
481         return FALSE;
482 }
483
484 void folder_func_to_all_folders(FolderItemFunc function, gpointer data)
485 {
486         GList *list;
487         Folder *folder;
488         struct FuncToAllFoldersData function_data;
489         
490         function_data.function = function;
491         function_data.data = data;
492
493         for (list = folder_list; list != NULL; list = list->next) {
494                 folder = FOLDER(list->data);
495                 if (folder->node)
496                         g_node_traverse(folder->node, G_PRE_ORDER,
497                                         G_TRAVERSE_ALL, -1,
498                                         folder_func_to_all_folders_func,
499                                         &function_data);
500         }
501 }
502
503 static void folder_count_total_msgs_func(FolderItem *item, gpointer data)
504 {
505         struct TotalMsgCount *count = (struct TotalMsgCount *)data;
506
507         count->new_msgs += item->new_msgs;
508         count->unread_msgs += item->unread_msgs;
509         count->unreadmarked_msgs += item->unreadmarked_msgs;
510         count->total_msgs += item->total_msgs;
511 }
512
513 void folder_count_total_msgs(guint *new_msgs, guint *unread_msgs, guint *unreadmarked_msgs, guint *total_msgs)
514 {
515         struct TotalMsgCount count;
516
517         count.new_msgs = count.unread_msgs = count.unreadmarked_msgs = count.total_msgs = 0;
518
519         debug_print("Counting total number of messages...\n");
520
521         folder_func_to_all_folders(folder_count_total_msgs_func, &count);
522
523         *new_msgs = count.new_msgs;
524         *unread_msgs = count.unread_msgs;
525         *unreadmarked_msgs = count.unreadmarked_msgs;
526         *total_msgs = count.total_msgs;
527 }
528
529 Folder *folder_find_from_path(const gchar *path)
530 {
531         GList *list;
532         Folder *folder;
533
534         for (list = folder_list; list != NULL; list = list->next) {
535                 folder = list->data;
536                 if ((FOLDER_TYPE(folder) == F_MH || FOLDER_TYPE(folder) == F_MBOX) &&
537                     !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
538                         return folder;
539         }
540
541         return NULL;
542 }
543
544 Folder *folder_find_from_name(const gchar *name, FolderClass *klass)
545 {
546         GList *list;
547         Folder *folder;
548
549         for (list = folder_list; list != NULL; list = list->next) {
550                 folder = list->data;
551                 if (folder->klass == klass && strcmp2(name, folder->name) == 0)
552                         return folder;
553         }
554
555         return NULL;
556 }
557
558 static gboolean folder_item_find_func(GNode *node, gpointer data)
559 {
560         FolderItem *item = node->data;
561         gpointer *d = data;
562         const gchar *path = d[0];
563
564         if (path_cmp(path, item->path) != 0)
565                 return FALSE;
566
567         d[1] = item;
568
569         return TRUE;
570 }
571
572 FolderItem *folder_find_item_from_path(const gchar *path)
573 {
574         Folder *folder;
575         gpointer d[2];
576
577         folder = folder_get_default_folder();
578         g_return_val_if_fail(folder != NULL, NULL);
579
580         d[0] = (gpointer)path;
581         d[1] = NULL;
582         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
583                         folder_item_find_func, d);
584         return d[1];
585 }
586
587 FolderClass *folder_get_class_from_string(const gchar *str)
588 {
589         GSList *classlist;
590
591         classlist = folder_get_class_list();
592         for (; classlist != NULL; classlist = g_slist_next(classlist)) {
593                 FolderClass *class = (FolderClass *) classlist->data;
594                 if (g_strcasecmp(class->idstr, str) == 0)
595                         return class;
596         }
597
598         return NULL;
599 }
600
601 gchar *folder_get_identifier(Folder *folder)
602 {
603         gchar *type_str;
604
605         g_return_val_if_fail(folder != NULL, NULL);
606
607         type_str = folder->klass->idstr;
608         return g_strconcat("#", type_str, "/", folder->name, NULL);
609 }
610
611 gchar *folder_item_get_identifier(FolderItem *item)
612 {
613         gchar *id;
614         gchar *folder_id;
615
616         g_return_val_if_fail(item != NULL, NULL);
617         g_return_val_if_fail(item->path != NULL, NULL);
618
619         folder_id = folder_get_identifier(item->folder);
620         id = g_strconcat(folder_id, "/", item->path, NULL);
621         g_free(folder_id);
622
623         return id;
624 }
625
626 FolderItem *folder_find_item_from_identifier(const gchar *identifier)
627 {
628         Folder *folder;
629         gpointer d[2];
630         gchar *str;
631         gchar *p;
632         gchar *name;
633         gchar *path;
634         FolderClass *class;
635
636         g_return_val_if_fail(identifier != NULL, NULL);
637
638         if (*identifier != '#')
639                 return folder_find_item_from_path(identifier);
640
641         Xstrdup_a(str, identifier, return NULL);
642
643         p = strchr(str, '/');
644         if (!p)
645                 return folder_find_item_from_path(identifier);
646         *p = '\0';
647         p++;
648         class = folder_get_class_from_string(&str[1]);
649         if (class == NULL)
650                 return folder_find_item_from_path(identifier);
651
652         name = p;
653         p = strchr(p, '/');
654         if (!p)
655                 return folder_find_item_from_path(identifier);
656         *p = '\0';
657         p++;
658
659         folder = folder_find_from_name(name, class);
660         if (!folder)
661                 return folder_find_item_from_path(identifier);
662
663         path = p;
664
665         d[0] = (gpointer)path;
666         d[1] = NULL;
667         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
668                         folder_item_find_func, d);
669         return d[1];
670 }
671
672 /**
673  * Get a displayable name for a FolderItem
674  *
675  * \param item FolderItem for that a name should be created
676  * \return Displayable name for item, returned string has to
677  *         be freed
678  */
679 gchar *folder_item_get_name(FolderItem *item)
680 {
681         gchar *name = NULL;
682
683         switch (item->stype) {
684         case F_INBOX:
685                 name = g_strdup(!strcmp2(item->name, INBOX_DIR) ? _("Inbox") :
686                                 item->name);
687                 break;
688         case F_OUTBOX:
689                 name = g_strdup(!strcmp2(item->name, OUTBOX_DIR) ? _("Sent") :
690                                 item->name);
691                 break;
692         case F_QUEUE:
693                 name = g_strdup(!strcmp2(item->name, QUEUE_DIR) ? _("Queue") :
694                                 item->name);
695                 break;
696         case F_TRASH:
697                 name = g_strdup(!strcmp2(item->name, TRASH_DIR) ? _("Trash") :
698                                 item->name);
699                 break;
700         case F_DRAFT:
701                 name = g_strdup(!strcmp2(item->name, DRAFT_DIR) ? _("Drafts") :
702                                 item->name);
703                 break;
704         default:
705                 break;
706         }
707
708         if (name == NULL) {
709                 /*
710                  * should probably be done by a virtual function,
711                  * the folder knows the ui string and how to abbrev
712                 */
713                 if (!item->parent) {
714                         name = g_strconcat(item->name, " (", item->folder->klass->uistr, ")", NULL);
715                 } else {
716                         if (FOLDER_CLASS(item->folder) == news_get_class() &&
717                             item->path && !strcmp2(item->name, item->path))
718                                 name = get_abbrev_newsgroup_name
719                                         (item->path,
720                                          prefs_common.ng_abbrev_len);
721                         else
722                                 name = g_strdup(item->name);
723                 }
724         }
725
726         if (name == NULL)
727                 name = g_strdup("");
728
729         return name;
730 }
731
732 Folder *folder_get_default_folder(void)
733 {
734         return folder_list ? FOLDER(folder_list->data) : NULL;
735 }
736
737 FolderItem *folder_get_default_inbox(void)
738 {
739         Folder *folder;
740
741         if (!folder_list) return NULL;
742         folder = FOLDER(folder_list->data);
743         g_return_val_if_fail(folder != NULL, NULL);
744         return folder->inbox;
745 }
746
747 FolderItem *folder_get_default_outbox(void)
748 {
749         Folder *folder;
750
751         if (!folder_list) return NULL;
752         folder = FOLDER(folder_list->data);
753         g_return_val_if_fail(folder != NULL, NULL);
754         return folder->outbox;
755 }
756
757 FolderItem *folder_get_default_draft(void)
758 {
759         Folder *folder;
760
761         if (!folder_list) return NULL;
762         folder = FOLDER(folder_list->data);
763         g_return_val_if_fail(folder != NULL, NULL);
764         return folder->draft;
765 }
766
767 FolderItem *folder_get_default_queue(void)
768 {
769         Folder *folder;
770
771         if (!folder_list) return NULL;
772         folder = FOLDER(folder_list->data);
773         g_return_val_if_fail(folder != NULL, NULL);
774         return folder->queue;
775 }
776
777 FolderItem *folder_get_default_trash(void)
778 {
779         Folder *folder;
780
781         if (!folder_list) return NULL;
782         folder = FOLDER(folder_list->data);
783         g_return_val_if_fail(folder != NULL, NULL);
784         return folder->trash;
785 }
786
787 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type)           \
788 {                                                               \
789         if (!folder->member) {                                  \
790                 item = folder_item_new(folder, dir, dir);       \
791                 item->stype = type;                             \
792                 folder_item_append(rootitem, item);             \
793                 folder->member = item;                          \
794         }                                                       \
795 }
796
797 void folder_set_missing_folders(void)
798 {
799         Folder *folder;
800         FolderItem *rootitem;
801         FolderItem *item;
802         GList *list;
803
804         for (list = folder_list; list != NULL; list = list->next) {
805                 folder = list->data;
806                 if (FOLDER_TYPE(folder) != F_MH) continue;
807                 rootitem = FOLDER_ITEM(folder->node->data);
808                 g_return_if_fail(rootitem != NULL);
809
810                 if (folder->inbox && folder->outbox && folder->draft &&
811                     folder->queue && folder->trash)
812                         continue;
813
814                 if (folder->klass->create_tree(folder) < 0) {
815                         g_warning("%s: can't create the folder tree.\n",
816                                   LOCAL_FOLDER(folder)->rootpath);
817                         continue;
818                 }
819
820                 CREATE_FOLDER_IF_NOT_EXIST(inbox,  INBOX_DIR,  F_INBOX);
821                 CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
822                 CREATE_FOLDER_IF_NOT_EXIST(draft,  DRAFT_DIR,  F_DRAFT);
823                 CREATE_FOLDER_IF_NOT_EXIST(queue,  QUEUE_DIR,  F_QUEUE);
824                 CREATE_FOLDER_IF_NOT_EXIST(trash,  TRASH_DIR,  F_TRASH);
825         }
826 }
827
828 static gboolean folder_unref_account_func(GNode *node, gpointer data)
829 {
830         FolderItem *item = node->data;
831         PrefsAccount *account = data;
832
833         if (item->account == account)
834                 item->account = NULL;
835
836         return FALSE;
837 }
838
839 void folder_unref_account_all(PrefsAccount *account)
840 {
841         Folder *folder;
842         GList *list;
843
844         if (!account) return;
845
846         for (list = folder_list; list != NULL; list = list->next) {
847                 folder = list->data;
848                 if (folder->account == account)
849                         folder->account = NULL;
850                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
851                                 folder_unref_account_func, account);
852         }
853 }
854
855 #undef CREATE_FOLDER_IF_NOT_EXIST
856
857 gchar *folder_get_path(Folder *folder)
858 {
859         gchar *path;
860
861         g_return_val_if_fail(folder != NULL, NULL);
862
863         switch(FOLDER_TYPE(folder)) {
864
865                 case F_MH:
866                         path = g_strdup(LOCAL_FOLDER(folder)->rootpath);
867                         break;
868
869                 case F_IMAP:
870                         g_return_val_if_fail(folder->account != NULL, NULL);
871                         path = g_strconcat(get_imap_cache_dir(),
872                                            G_DIR_SEPARATOR_S,
873                                            folder->account->recv_server,
874                                            G_DIR_SEPARATOR_S,
875                                            folder->account->userid,
876                                            NULL);
877                         break;
878
879                 case F_NEWS:
880                         g_return_val_if_fail(folder->account != NULL, NULL);
881                         path = g_strconcat(get_news_cache_dir(),
882                                            G_DIR_SEPARATOR_S,
883                                            folder->account->nntp_server,
884                                            NULL);
885                         break;
886
887                 default:
888                         path = NULL;
889                         break;
890         }
891         
892         return path;
893 }
894
895 gchar *folder_item_get_path(FolderItem *item)
896 {
897         gchar *folder_path;
898         gchar *path;
899
900         g_return_val_if_fail(item != NULL, NULL);
901
902         if(FOLDER_TYPE(item->folder) != F_MBOX) {
903                 folder_path = folder_get_path(item->folder);
904                 g_return_val_if_fail(folder_path != NULL, NULL);
905
906                 if (folder_path[0] == G_DIR_SEPARATOR) {
907                         if (item->path)
908                                 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
909                                                    item->path, NULL);
910                         else
911                                 path = g_strdup(folder_path);
912                 } else {
913                         if (item->path)
914                                 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
915                                                    folder_path, G_DIR_SEPARATOR_S,
916                                                    item->path, NULL);
917                         else
918                                 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
919                                                    folder_path, NULL);
920                 }
921
922                 g_free(folder_path);
923         } else {
924                 gchar *itempath;
925
926                 itempath = mbox_get_virtual_path(item);
927                 if (itempath == NULL)
928                         return NULL;
929                 path = g_strconcat(get_mbox_cache_dir(),
930                                           G_DIR_SEPARATOR_S, itempath, NULL);
931                 g_free(itempath);
932         }
933         return path;
934 }
935
936 void folder_item_set_default_flags(FolderItem *dest, MsgFlags *flags)
937 {
938         if (!(dest->stype == F_OUTBOX ||
939               dest->stype == F_QUEUE  ||
940               dest->stype == F_DRAFT  ||
941               dest->stype == F_TRASH)) {
942                 flags->perm_flags = MSG_NEW|MSG_UNREAD;
943         } else {
944                 flags->perm_flags = 0;
945         }
946         flags->tmp_flags = MSG_CACHED;
947         if (FOLDER_TYPE(dest->folder) == F_MH) {
948                 if (dest->stype == F_QUEUE) {
949                         MSG_SET_TMP_FLAGS(*flags, MSG_QUEUED);
950                 } else if (dest->stype == F_DRAFT) {
951                         MSG_SET_TMP_FLAGS(*flags, MSG_DRAFT);
952                 }
953         }
954 }
955
956 static gint folder_sort_cache_list_by_msgnum(gconstpointer a, gconstpointer b)
957 {
958         MsgInfo *msginfo_a = (MsgInfo *) a;
959         MsgInfo *msginfo_b = (MsgInfo *) b;
960
961         return (msginfo_a->msgnum - msginfo_b->msgnum);
962 }
963
964 static gint folder_sort_folder_list(gconstpointer a, gconstpointer b)
965 {
966         guint gint_a = GPOINTER_TO_INT(a);
967         guint gint_b = GPOINTER_TO_INT(b);
968         
969         return (gint_a - gint_b);
970 }
971
972 gint folder_item_open(FolderItem *item)
973 {
974         if(((FOLDER_TYPE(item->folder) == F_IMAP) && !item->no_select) || (FOLDER_TYPE(item->folder) == F_NEWS)) {
975                 folder_item_scan_full(item, TRUE);
976         }
977
978         /* Processing */
979         if(item->prefs->processing != NULL) {
980                 gchar *buf;
981                 
982                 buf = g_strdup_printf(_("Processing (%s)...\n"), item->path);
983                 debug_print("%s\n", buf);
984                 g_free(buf);
985         
986                 folder_item_apply_processing(item);
987
988                 debug_print("done.\n");
989         }
990
991         return 0;
992 }
993
994 void folder_item_close(FolderItem *item)
995 {
996         GSList *mlist, *cur;
997         
998         g_return_if_fail(item != NULL);
999
1000         if (item->new_msgs) {
1001                 folder_item_update_freeze();
1002                 mlist = folder_item_get_msg_list(item);
1003                 for (cur = mlist ; cur != NULL ; cur = cur->next) {
1004                         MsgInfo * msginfo;
1005
1006                         msginfo = (MsgInfo *) cur->data;
1007                         if (MSG_IS_NEW(msginfo->flags))
1008                                 procmsg_msginfo_unset_flags(msginfo, MSG_NEW, 0);
1009                         procmsg_msginfo_free(msginfo);
1010                 }
1011                 g_slist_free(mlist);
1012                 folder_item_update_thaw();
1013         }               
1014
1015         folder_item_write_cache(item);
1016         
1017         folder_item_update(item, F_ITEM_UPDATE_MSGCNT);
1018 }
1019
1020 gint folder_item_scan_full(FolderItem *item, gboolean filtering)
1021 {
1022         Folder *folder;
1023         GSList *folder_list = NULL, *cache_list = NULL;
1024         GSList *folder_list_cur, *cache_list_cur, *new_list = NULL;
1025         GSList *exists_list = NULL, *elem;
1026         GSList *newmsg_list = NULL;
1027         guint newcnt = 0, unreadcnt = 0, totalcnt = 0, unreadmarkedcnt = 0;
1028         guint cache_max_num, folder_max_num, cache_cur_num, folder_cur_num;
1029         gboolean update_flags = 0;
1030     
1031         g_return_val_if_fail(item != NULL, -1);
1032         if (item->path == NULL) return -1;
1033
1034         folder = item->folder;
1035
1036         g_return_val_if_fail(folder != NULL, -1);
1037         g_return_val_if_fail(folder->klass->get_num_list != NULL, -1);
1038
1039         debug_print("Scanning folder %s for cache changes.\n", item->path);
1040
1041         /* Get list of messages for folder and cache */
1042         if (folder->klass->get_num_list(item->folder, item, &folder_list) < 0) {
1043                 debug_print("Error fetching list of message numbers\n");
1044                 return(-1);
1045         }
1046
1047         if (!folder->klass->check_msgnum_validity || 
1048             folder->klass->check_msgnum_validity(folder, item)) {
1049                 if (!item->cache)
1050                         folder_item_read_cache(item);
1051                 cache_list = msgcache_get_msg_list(item->cache);
1052         } else {
1053                 if (item->cache)
1054                         msgcache_destroy(item->cache);
1055                 item->cache = msgcache_new();
1056                 cache_list = NULL;
1057         }
1058
1059         /* Sort both lists */
1060         cache_list = g_slist_sort(cache_list, folder_sort_cache_list_by_msgnum);
1061         folder_list = g_slist_sort(folder_list, folder_sort_folder_list);
1062
1063         cache_list_cur = cache_list;
1064         folder_list_cur = folder_list;
1065
1066         if (cache_list_cur != NULL) {
1067                 GSList *cache_list_last;
1068         
1069                 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1070                 cache_list_last = g_slist_last(cache_list);
1071                 cache_max_num = ((MsgInfo *)cache_list_last->data)->msgnum;
1072         } else {
1073                 cache_cur_num = G_MAXINT;
1074                 cache_max_num = 0;
1075         }
1076
1077         if (folder_list_cur != NULL) {
1078                 GSList *folder_list_last;
1079         
1080                 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1081                 folder_list_last = g_slist_last(folder_list);
1082                 folder_max_num = GPOINTER_TO_INT(folder_list_last->data);
1083         } else {
1084                 folder_cur_num = G_MAXINT;
1085                 folder_max_num = 0;
1086         }
1087
1088         while ((cache_cur_num != G_MAXINT) || (folder_cur_num != G_MAXINT)) {
1089                 /*
1090                  *  Message only exists in the folder
1091                  *  Remember message for fetching
1092                  */
1093                 if (folder_cur_num < cache_cur_num) {
1094                         gboolean add = FALSE;
1095
1096                         switch(FOLDER_TYPE(folder)) {
1097                                 case F_NEWS:
1098                                         if (folder_cur_num < cache_max_num)
1099                                                 break;
1100                                         
1101                                         if (folder->account->max_articles == 0) {
1102                                                 add = TRUE;
1103                                         }
1104
1105                                         if (folder_max_num <= folder->account->max_articles) {
1106                                                 add = TRUE;
1107                                         } else if (folder_cur_num > (folder_max_num - folder->account->max_articles)) {
1108                                                 add = TRUE;
1109                                         }
1110                                         break;
1111                                 default:
1112                                         add = TRUE;
1113                                         break;
1114                         }
1115                         
1116                         if (add) {
1117                                 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(folder_cur_num));
1118                                 debug_print("Remembered message %d for fetching\n", folder_cur_num);
1119                         }
1120
1121                         /* Move to next folder number */
1122                         folder_list_cur = folder_list_cur->next;
1123
1124                         if (folder_list_cur != NULL)
1125                                 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1126                         else
1127                                 folder_cur_num = G_MAXINT;
1128
1129                         continue;
1130                 }
1131
1132                 /*
1133                  *  Message only exists in the cache
1134                  *  Remove the message from the cache
1135                  */
1136                 if (cache_cur_num < folder_cur_num) {
1137                         msgcache_remove_msg(item->cache, cache_cur_num);
1138                         debug_print("Removed message %d from cache.\n", cache_cur_num);
1139
1140                         /* Move to next cache number */
1141                         cache_list_cur = cache_list_cur->next;
1142
1143                         if (cache_list_cur != NULL)
1144                                 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1145                         else
1146                                 cache_cur_num = G_MAXINT;
1147
1148                         update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
1149
1150                         continue;
1151                 }
1152
1153                 /*
1154                  *  Message number exists in folder and cache!
1155                  *  Check if the message has been modified
1156                  */
1157                 if (cache_cur_num == folder_cur_num) {
1158                         MsgInfo *msginfo;
1159
1160                         msginfo = msgcache_get_msg(item->cache, folder_cur_num);
1161                         if (folder->klass->is_msg_changed && folder->klass->is_msg_changed(folder, item, msginfo)) {
1162                                 msgcache_remove_msg(item->cache, msginfo->msgnum);
1163                                 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(msginfo->msgnum));
1164                                 procmsg_msginfo_free(msginfo);
1165
1166                                 debug_print("Remembering message %d to update...\n", folder_cur_num);
1167                         } else
1168                                 exists_list = g_slist_prepend(exists_list, msginfo);
1169
1170                         /* Move to next folder and cache number */
1171                         cache_list_cur = cache_list_cur->next;
1172                         folder_list_cur = folder_list_cur->next;
1173
1174                         if (cache_list_cur != NULL)
1175                                 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1176                         else
1177                                 cache_cur_num = G_MAXINT;
1178
1179                         if (folder_list_cur != NULL)
1180                                 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1181                         else
1182                                 folder_cur_num = G_MAXINT;
1183
1184                         continue;
1185                 }
1186         }
1187         
1188         for(cache_list_cur = cache_list; cache_list_cur != NULL; cache_list_cur = g_slist_next(cache_list_cur))
1189                 procmsg_msginfo_free((MsgInfo *) cache_list_cur->data);
1190
1191         g_slist_free(cache_list);
1192         g_slist_free(folder_list);
1193
1194         if (new_list != NULL) {
1195                 if (folder->klass->get_msginfos) {
1196                         newmsg_list = folder->klass->get_msginfos(folder, item, new_list);
1197                 } else if (folder->klass->get_msginfo) {
1198                         GSList *elem;
1199         
1200                         for (elem = new_list; elem != NULL; elem = g_slist_next(elem)) {
1201                                 MsgInfo *msginfo;
1202                                 guint num;
1203
1204                                 num = GPOINTER_TO_INT(elem->data);
1205                                 msginfo = folder->klass->get_msginfo(folder, item, num);
1206                                 if (msginfo != NULL) {
1207                                         newmsg_list = g_slist_prepend(newmsg_list, msginfo);
1208                                         debug_print("Added newly found message %d to cache.\n", num);
1209                                 }
1210                         }
1211                 }
1212                 g_slist_free(new_list);
1213         }
1214
1215         if (newmsg_list != NULL) {
1216                 GSList *elem;
1217
1218                 for (elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) {
1219                         MsgInfo *msginfo = (MsgInfo *) elem->data;
1220
1221                         msgcache_add_msg(item->cache, msginfo);
1222                         if ((filtering == TRUE) &&
1223                             (item->stype == F_INBOX) &&
1224                             (item->folder->account != NULL) && 
1225                             (item->folder->account->filter_on_recv) &&
1226                             procmsg_msginfo_filter(msginfo))
1227                                 procmsg_msginfo_free(msginfo);
1228                         else
1229                                 exists_list = g_slist_prepend(exists_list, msginfo);
1230                 }
1231                 g_slist_free(newmsg_list);
1232
1233                 update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
1234         }
1235
1236         for (elem = exists_list; elem != NULL; elem = g_slist_next(elem)) {
1237                 MsgInfo *msginfo;
1238
1239                 msginfo = elem->data;
1240                 if (MSG_IS_IGNORE_THREAD(msginfo->flags) && (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
1241                         procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1242                 if (!MSG_IS_IGNORE_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD)) {
1243                         procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1244                         procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
1245                 }
1246                 if ((item->stype == F_OUTBOX ||
1247                      item->stype == F_QUEUE  ||
1248                      item->stype == F_DRAFT  ||
1249                      item->stype == F_TRASH) &&
1250                     (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
1251                         procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1252                 if (MSG_IS_NEW(msginfo->flags))
1253                         newcnt++;
1254                 if (MSG_IS_UNREAD(msginfo->flags))
1255                         unreadcnt++;
1256                 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
1257                         unreadmarkedcnt++;
1258                 totalcnt++;
1259
1260                 procmsg_msginfo_free(msginfo);
1261         }
1262         g_slist_free(exists_list);
1263
1264         item->new_msgs = newcnt;
1265         item->unread_msgs = unreadcnt;
1266         item->total_msgs = totalcnt;
1267         item->unreadmarked_msgs = unreadmarkedcnt;
1268
1269         update_flags |= F_ITEM_UPDATE_MSGCNT;
1270
1271         folder_item_update(item, update_flags);
1272
1273         return 0;
1274 }
1275
1276 gint folder_item_scan(FolderItem *item)
1277 {
1278         return folder_item_scan_full(item, TRUE);
1279 }
1280
1281 static gboolean folder_scan_all_items_func(GNode *node, gpointer data)
1282 {
1283         FolderItem *item = node->data;
1284
1285         folder_item_scan(item);
1286
1287         return FALSE;
1288 }
1289
1290 void folder_scan_all_items(Folder * folder)
1291 {
1292         g_node_traverse(folder->node, G_PRE_ORDER,
1293                         G_TRAVERSE_ALL, -1, folder_scan_all_items_func, NULL);
1294 }
1295
1296 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
1297                                           gpointer data)
1298 {
1299         folder_item_scan(FOLDER_ITEM(key));
1300 }
1301
1302 void folder_item_scan_foreach(GHashTable *table)
1303 {
1304         g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
1305 }
1306
1307 void folder_count_total_cache_memusage(FolderItem *item, gpointer data)
1308 {
1309         gint *memusage = (gint *)data;
1310
1311         if (item->cache == NULL)
1312                 return;
1313         
1314         *memusage += msgcache_get_memory_usage(item->cache);
1315 }
1316
1317 gint folder_cache_time_compare_func(gconstpointer a, gconstpointer b)
1318 {
1319         FolderItem *fa = (FolderItem *)a;
1320         FolderItem *fb = (FolderItem *)b;
1321         
1322         return (gint) (msgcache_get_last_access_time(fa->cache) - msgcache_get_last_access_time(fb->cache));
1323 }
1324
1325 void folder_find_expired_caches(FolderItem *item, gpointer data)
1326 {
1327         GSList **folder_item_list = (GSList **)data;
1328         gint difftime, expiretime;
1329         
1330         if (item->cache == NULL)
1331                 return;
1332
1333         if (item->opened > 0)
1334                 return;
1335
1336         difftime = (gint) (time(NULL) - msgcache_get_last_access_time(item->cache));
1337         expiretime = prefs_common.cache_min_keep_time * 60;
1338         debug_print("Cache unused time: %d (Expire time: %d)\n", difftime, expiretime);
1339         if (difftime > expiretime) {
1340                 *folder_item_list = g_slist_insert_sorted(*folder_item_list, item, folder_cache_time_compare_func);
1341         }
1342 }
1343
1344 void folder_item_free_cache(FolderItem *item)
1345 {
1346         g_return_if_fail(item != NULL);
1347         
1348         if (item->cache == NULL)
1349                 return;
1350         
1351         if (item->opened > 0)
1352                 return;
1353
1354         folder_item_write_cache(item);
1355         msgcache_destroy(item->cache);
1356         item->cache = NULL;
1357 }
1358
1359 void folder_clean_cache_memory(void)
1360 {
1361         gint memusage = 0;
1362
1363         folder_func_to_all_folders(folder_count_total_cache_memusage, &memusage);       
1364         debug_print("Total cache memory usage: %d\n", memusage);
1365         
1366         if (memusage > (prefs_common.cache_max_mem_usage * 1024)) {
1367                 GSList *folder_item_list = NULL, *listitem;
1368                 
1369                 debug_print("Trying to free cache memory\n");
1370
1371                 folder_func_to_all_folders(folder_find_expired_caches, &folder_item_list);      
1372                 listitem = folder_item_list;
1373                 while((listitem != NULL) && (memusage > (prefs_common.cache_max_mem_usage * 1024))) {
1374                         FolderItem *item = (FolderItem *)(listitem->data);
1375
1376                         debug_print("Freeing cache memory for %s\n", item->path);
1377                         memusage -= msgcache_get_memory_usage(item->cache);
1378                         folder_item_free_cache(item);
1379                         listitem = listitem->next;
1380                 }
1381                 g_slist_free(folder_item_list);
1382         }
1383 }
1384
1385 void folder_item_read_cache(FolderItem *item)
1386 {
1387         gchar *cache_file, *mark_file;
1388         
1389         g_return_if_fail(item != NULL);
1390
1391         cache_file = folder_item_get_cache_file(item);
1392         mark_file = folder_item_get_mark_file(item);
1393         item->cache = msgcache_read_cache(item, cache_file);
1394         if (!item->cache) {
1395                 item->cache = msgcache_new();
1396                 folder_item_scan_full(item, TRUE);
1397         }
1398         msgcache_read_mark(item->cache, mark_file);
1399         g_free(cache_file);
1400         g_free(mark_file);
1401
1402         folder_clean_cache_memory();
1403 }
1404
1405 void folder_item_write_cache(FolderItem *item)
1406 {
1407         gchar *cache_file, *mark_file;
1408         PrefsFolderItem *prefs;
1409         gint filemode = 0;
1410         gchar *id;
1411         
1412         if (!item || !item->path || !item->cache)
1413                 return;
1414
1415         id = folder_item_get_identifier(item);
1416         debug_print("Save cache for folder %s\n", id);
1417         g_free(id);
1418
1419         cache_file = folder_item_get_cache_file(item);
1420         mark_file = folder_item_get_mark_file(item);
1421         if (msgcache_write(cache_file, mark_file, item->cache) < 0) {
1422                 prefs = item->prefs;
1423                 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
1424                         /* for cache file */
1425                         filemode = prefs->folder_chmod;
1426                         if (filemode & S_IRGRP) filemode |= S_IWGRP;
1427                         if (filemode & S_IROTH) filemode |= S_IWOTH;
1428                         chmod(cache_file, filemode);
1429                 }
1430         }
1431
1432         g_free(cache_file);
1433         g_free(mark_file);
1434 }
1435
1436 MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
1437 {
1438         Folder *folder;
1439         MsgInfo *msginfo;
1440         
1441         g_return_val_if_fail(item != NULL, NULL);
1442         
1443         folder = item->folder;
1444         if (!item->cache)
1445                 folder_item_read_cache(item);
1446         
1447         if ((msginfo = msgcache_get_msg(item->cache, num)) != NULL)
1448                 return msginfo;
1449         
1450         g_return_val_if_fail(folder->klass->get_msginfo, NULL);
1451         if ((msginfo = folder->klass->get_msginfo(folder, item, num)) != NULL) {
1452                 msgcache_add_msg(item->cache, msginfo);
1453                 return msginfo;
1454         }
1455         
1456         return NULL;
1457 }
1458
1459 MsgInfo *folder_item_get_msginfo_by_msgid(FolderItem *item, const gchar *msgid)
1460 {
1461         Folder *folder;
1462         MsgInfo *msginfo;
1463         
1464         g_return_val_if_fail(item != NULL, NULL);
1465         
1466         folder = item->folder;
1467         if (!item->cache)
1468                 folder_item_read_cache(item);
1469         
1470         if ((msginfo = msgcache_get_msg_by_id(item->cache, msgid)) != NULL)
1471                 return msginfo;
1472
1473         return NULL;
1474 }
1475
1476 GSList *folder_item_get_msg_list(FolderItem *item)
1477 {
1478         g_return_val_if_fail(item != NULL, NULL);
1479         
1480         if (item->cache == 0)
1481                 folder_item_read_cache(item);
1482
1483         g_return_val_if_fail(item->cache != NULL, NULL);
1484         
1485         return msgcache_get_msg_list(item->cache);
1486 }
1487
1488 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
1489 {
1490         Folder *folder;
1491
1492         g_return_val_if_fail(item != NULL, NULL);
1493
1494         folder = item->folder;
1495
1496         g_return_val_if_fail(folder->klass->fetch_msg != NULL, NULL);
1497
1498         return folder->klass->fetch_msg(folder, item, num);
1499 }
1500
1501 static gint folder_item_get_msg_num_by_file(FolderItem *dest, const gchar *file)
1502 {
1503         static HeaderEntry hentry[] = {{"Message-ID:",  NULL, TRUE},
1504                                        {NULL,           NULL, FALSE}};
1505         FILE *fp;
1506         MsgInfo *msginfo;
1507         gint msgnum = 0;
1508         gchar buf[BUFFSIZE];
1509
1510         if ((fp = fopen(file, "rb")) == NULL)
1511                 return 0;
1512
1513         if ((dest->stype == F_QUEUE) || (dest->stype == F_DRAFT))
1514                 while (fgets(buf, sizeof(buf), fp) != NULL)
1515                         if (buf[0] == '\r' || buf[0] == '\n') break;
1516
1517         procheader_get_header_fields(fp, hentry);
1518         if (hentry[0].body) {
1519                 extract_parenthesis(hentry[0].body, '<', '>');
1520                 remove_space(hentry[0].body);
1521                 if ((msginfo = msgcache_get_msg_by_id(dest->cache, hentry[0].body)) != NULL) {
1522                         msgnum = msginfo->msgnum;
1523                         procmsg_msginfo_free(msginfo);
1524
1525                         debug_print("found message as uid %d\n", msgnum);
1526                 }
1527         }
1528         
1529         g_free(hentry[0].body);
1530         hentry[0].body = NULL;
1531         fclose(fp);
1532
1533         return msgnum;
1534 }
1535
1536 static void copy_msginfo_flags(MsgInfo *source, MsgInfo *dest)
1537 {
1538         MsgPermFlags perm_flags = 0;
1539         MsgTmpFlags tmp_flags = 0;
1540
1541         /* create new flags */
1542         if (source != NULL) {
1543                 /* copy original flags */
1544                 perm_flags = source->flags.perm_flags;
1545                 tmp_flags = source->flags.tmp_flags;
1546         } else {
1547                 perm_flags = dest->flags.perm_flags;
1548                 tmp_flags = dest->flags.tmp_flags;
1549         }
1550
1551         /* remove new, unread and deleted in special folders */
1552         if (dest->folder->stype == F_OUTBOX ||
1553             dest->folder->stype == F_QUEUE  ||
1554             dest->folder->stype == F_DRAFT  ||
1555             dest->folder->stype == F_TRASH)
1556                 perm_flags &= ~(MSG_NEW | MSG_UNREAD | MSG_DELETED);
1557
1558         /* set ignore flag of ignored parent exists */
1559         if (procmsg_msg_has_flagged_parent(dest, MSG_IGNORE_THREAD))
1560                 perm_flags |= MSG_IGNORE_THREAD;
1561
1562         /* Unset tmp flags that should not be copied */
1563         tmp_flags &= ~(MSG_MOVE | MSG_COPY);
1564
1565         /* unset flags that are set but should not */
1566         procmsg_msginfo_unset_flags(dest,
1567                                     dest->flags.perm_flags & ~perm_flags,
1568                                     dest->flags.tmp_flags  & ~tmp_flags);
1569         /* set new flags */
1570         procmsg_msginfo_set_flags(dest,
1571                                   ~dest->flags.perm_flags & perm_flags,
1572                                   ~dest->flags.tmp_flags  & tmp_flags);
1573
1574         folder_item_update(dest->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
1575 }
1576
1577 static void add_msginfo_to_cache(FolderItem *item, MsgInfo *newmsginfo, MsgInfo *flagsource)
1578 {
1579         /* update folder stats */
1580         if (MSG_IS_NEW(newmsginfo->flags))
1581                 item->new_msgs++;
1582         if (MSG_IS_UNREAD(newmsginfo->flags))
1583                 item->unread_msgs++;
1584         if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
1585                 item->unreadmarked_msgs++;
1586         item->total_msgs++;
1587
1588         copy_msginfo_flags(flagsource, newmsginfo);
1589
1590         msgcache_add_msg(item->cache, newmsginfo);
1591 }
1592
1593 static void remove_msginfo_from_cache(FolderItem *item, MsgInfo *msginfo)
1594 {
1595         if (!item->cache)
1596             folder_item_read_cache(item);
1597
1598         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1599                 msginfo->folder->new_msgs--;
1600         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1601                 msginfo->folder->unread_msgs--;
1602         if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
1603                 msginfo->folder->unreadmarked_msgs--;
1604         msginfo->folder->total_msgs--;
1605
1606         msgcache_remove_msg(item->cache, msginfo->msgnum);
1607         folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
1608 }
1609
1610 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
1611                          gboolean remove_source)
1612 {
1613         Folder *folder;
1614         gint num;
1615         MsgInfo *msginfo;
1616
1617         g_return_val_if_fail(dest != NULL, -1);
1618         g_return_val_if_fail(file != NULL, -1);
1619
1620         folder = dest->folder;
1621
1622         g_return_val_if_fail(folder->klass->add_msg != NULL, -1);
1623
1624         if (!dest->cache)
1625                 folder_item_read_cache(dest);
1626
1627         num = folder->klass->add_msg(folder, dest, file, FALSE);
1628
1629         if (num > 0) {
1630                 msginfo = folder->klass->get_msginfo(folder, dest, num);
1631
1632                 if (msginfo != NULL) {
1633                         add_msginfo_to_cache(dest, msginfo, NULL);
1634                         procmsg_msginfo_free(msginfo);
1635                         folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
1636                 }
1637
1638                 dest->last_num = num;
1639         } else if (num == 0) {
1640                 folder_item_scan_full(dest, FALSE);
1641                 num = folder_item_get_msg_num_by_file(dest, file);
1642         }
1643
1644         if (num >= 0 && remove_source) {
1645                 if (unlink(file) < 0)
1646                         FILE_OP_ERROR(file, "unlink");
1647         }
1648
1649         return num;
1650 }
1651
1652 /*
1653 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1654 {
1655         Folder *folder;
1656         gint num;
1657
1658         g_return_val_if_fail(dest != NULL, -1);
1659         g_return_val_if_fail(msginfo != NULL, -1);
1660
1661         folder = dest->folder;
1662         if (dest->last_num < 0) folder->scan(folder, dest);
1663
1664         num = folder->move_msg(folder, dest, msginfo);
1665         if (num > 0) dest->last_num = num;
1666
1667         return num;
1668 }
1669 */
1670                 
1671 FolderItem *folder_item_move_recursive (FolderItem *src, FolderItem *dest) 
1672 {
1673         GSList *mlist;
1674         FolderItem *new_item;
1675         FolderItem *next_item;
1676         GNode *srcnode;
1677         gchar *old_id, *new_id;
1678
1679         mlist = folder_item_get_msg_list(src);
1680
1681         /* move messages */
1682         debug_print("Moving %s to %s\n", src->path, dest->path);
1683         new_item = folder_create_folder(dest, g_basename(src->path));
1684         if (new_item == NULL) {
1685                 printf("Can't create folder\n");
1686                 return NULL;
1687         }
1688         
1689         if (new_item->folder == NULL)
1690                 new_item->folder = dest->folder;
1691
1692         /* move messages */
1693         log_message(_("Moving %s to %s...\n"), 
1694                         src->name, new_item->path);
1695         folder_item_move_msgs_with_dest(new_item, mlist);
1696         
1697         /*copy prefs*/
1698         prefs_folder_item_copy_prefs(src, new_item);
1699         new_item->collapsed = src->collapsed;
1700         new_item->thread_collapsed = src->thread_collapsed;
1701         new_item->threaded  = src->threaded;
1702         new_item->ret_rcpt  = src->ret_rcpt;
1703         new_item->hide_read_msgs = src->hide_read_msgs;
1704         new_item->sort_key  = src->sort_key;
1705         new_item->sort_type = src->sort_type;
1706
1707         prefs_matcher_write_config();
1708         
1709         /* recurse */
1710         srcnode = src->folder->node;    
1711         srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, src);
1712         srcnode = srcnode->children;
1713         while (srcnode != NULL) {
1714                 if (srcnode && srcnode->data) {
1715                         next_item = (FolderItem*) srcnode->data;
1716                         srcnode = srcnode->next;
1717                         if (folder_item_move_recursive(next_item, new_item) == NULL)
1718                                 return NULL;
1719                 }
1720         }
1721         old_id = folder_item_get_identifier(src);
1722         new_id = folder_item_get_identifier(new_item);
1723         debug_print("updating rules : %s => %s\n", old_id, new_id);
1724         
1725         src->folder->klass->remove_folder(src->folder, src);
1726         folder_write_list();
1727
1728         if (old_id != NULL && new_id != NULL)
1729                 prefs_filtering_rename_path(old_id, new_id);
1730         g_free(old_id);
1731         g_free(new_id);
1732
1733         return new_item;
1734 }
1735
1736 gint folder_item_move_to(FolderItem *src, FolderItem *dest, FolderItem **new_item)
1737 {
1738         FolderItem *tmp = dest->parent;
1739         gchar * src_identifier, * dst_identifier;
1740         gchar * phys_srcpath, * phys_dstpath;
1741         GNode *src_node;
1742         
1743         while (tmp) {
1744                 if (tmp == src) {
1745                         return F_MOVE_FAILED_DEST_IS_CHILD;
1746                 }
1747                 tmp = tmp->parent;
1748         }
1749         
1750         tmp = src->parent;
1751         
1752         src_identifier = folder_item_get_identifier(src);
1753         dst_identifier = folder_item_get_identifier(dest);
1754         
1755         if(dst_identifier == NULL && dest->folder && dest->parent == NULL) {
1756                 /* dest can be a root folder */
1757                 dst_identifier = folder_get_identifier(dest->folder);
1758         }
1759         if (src_identifier == NULL || dst_identifier == NULL) {
1760                 debug_print("Can't get identifiers\n");
1761                 return F_MOVE_FAILED;
1762         }
1763
1764         if (src->folder != dest->folder) {
1765                 return F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX;
1766         }
1767
1768         phys_srcpath = folder_item_get_path(src);
1769         phys_dstpath = g_strconcat(folder_item_get_path(dest),G_DIR_SEPARATOR_S,g_basename(phys_srcpath),NULL);
1770
1771         if (src->parent == dest || src == dest) {
1772                 g_free(src_identifier);
1773                 g_free(dst_identifier);
1774                 g_free(phys_srcpath);
1775                 g_free(phys_dstpath);
1776                 return F_MOVE_FAILED_DEST_IS_PARENT;
1777         }
1778         debug_print("moving \"%s\" to \"%s\"\n", phys_srcpath, phys_dstpath);
1779         if ((tmp = folder_item_move_recursive(src, dest)) == NULL) {
1780                 return F_MOVE_FAILED;
1781         }
1782         
1783         /* update rules */
1784         src_node = g_node_find(src->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, src);
1785         if (src_node) 
1786                 g_node_destroy(src_node);
1787         else
1788                 debug_print("can't remove node: it's null!\n");
1789         /* not to much worry if remove fails, move has been done */
1790         
1791         g_free(src_identifier);
1792         g_free(dst_identifier);
1793         g_free(phys_srcpath);
1794         g_free(phys_dstpath);
1795
1796         *new_item = tmp;
1797
1798         return F_MOVE_OK;
1799 }
1800
1801 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1802 {
1803         GSList *list = NULL;
1804         gint ret;
1805
1806         list = g_slist_append(list, msginfo);
1807         ret = folder_item_move_msgs_with_dest(dest, list);
1808         g_slist_free(list);
1809         
1810         return ret;
1811 }
1812
1813 /*
1814 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
1815 {
1816         Folder *folder;
1817         gint num;
1818
1819         g_return_val_if_fail(dest != NULL, -1);
1820         g_return_val_if_fail(msglist != NULL, -1);
1821
1822         folder = dest->folder;
1823         if (dest->last_num < 0) folder->scan(folder, dest);
1824
1825         num = folder->move_msgs_with_dest(folder, dest, msglist);
1826         if (num > 0) dest->last_num = num;
1827         else dest->op_count = 0;
1828
1829         return num;
1830 }
1831 */
1832
1833
1834
1835 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
1836 {
1837         Folder *folder;
1838         FolderItem *item;
1839         GSList *newmsgnums = NULL;
1840         GSList *l, *l2;
1841         gint num, lastnum = -1;
1842         gboolean folderscan = FALSE;
1843
1844         g_return_val_if_fail(dest != NULL, -1);
1845         g_return_val_if_fail(msglist != NULL, -1);
1846
1847         folder = dest->folder;
1848
1849         g_return_val_if_fail(folder->klass->copy_msg != NULL, -1);
1850         g_return_val_if_fail(folder->klass->remove_msg != NULL, -1);
1851
1852         /* 
1853          * Copy messages to destination folder and 
1854          * store new message numbers in newmsgnums
1855          */
1856         item = NULL;
1857         for (l = msglist ; l != NULL ; l = g_slist_next(l)) {
1858                 MsgInfo * msginfo = (MsgInfo *) l->data;
1859
1860                 if (!item && msginfo->folder != NULL)
1861                         item = msginfo->folder;
1862
1863                 num = folder->klass->copy_msg(folder, dest, msginfo);
1864                 newmsgnums = g_slist_append(newmsgnums, GINT_TO_POINTER(num));
1865         }
1866
1867         /* Read cache for dest folder */
1868         if (!dest->cache) folder_item_read_cache(dest);
1869
1870         /* 
1871          * Fetch new MsgInfos for new messages in dest folder,
1872          * add them to the msgcache and update folder message counts
1873          */
1874         l2 = newmsgnums;
1875         for (l = msglist; l != NULL; l = g_slist_next(l)) {
1876                 MsgInfo *msginfo = (MsgInfo *) l->data;
1877
1878                 num = GPOINTER_TO_INT(l2->data);
1879                 l2 = g_slist_next(l2);
1880
1881                 if (num >= 0) {
1882                         MsgInfo *newmsginfo;
1883
1884                         if (num == 0) {
1885                                 gchar *file;
1886
1887                                 if (!folderscan) {
1888                                         folder_item_scan_full(dest, FALSE);
1889                                         folderscan = TRUE;
1890                                 }
1891                                 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
1892                                 num = folder_item_get_msg_num_by_file(dest, file);
1893                                 g_free(file);
1894                         }
1895
1896                         if (num > lastnum)
1897                                 lastnum = num;
1898
1899                         if (num == 0)
1900                                 continue;
1901
1902                         if (!folderscan && 
1903                             ((newmsginfo = folder->klass->get_msginfo(folder, dest, num)) != NULL)) {
1904                                 add_msginfo_to_cache(dest, newmsginfo, msginfo);
1905                                 procmsg_msginfo_free(newmsginfo);
1906                         } else if ((newmsginfo = msgcache_get_msg(dest->cache, num)) != NULL) {
1907                                 copy_msginfo_flags(msginfo, newmsginfo);
1908                                 procmsg_msginfo_free(newmsginfo);
1909                         }
1910                 }
1911         }
1912
1913         /*
1914          * Remove source messages from their folders if
1915          * copying was successfull and update folder
1916          * message counts
1917          */
1918         l2 = newmsgnums;
1919         for (l = msglist; l != NULL; l = g_slist_next(l)) {
1920                 MsgInfo *msginfo = (MsgInfo *) l->data;
1921
1922                 num = GPOINTER_TO_INT(l2->data);
1923                 l2 = g_slist_next(l2);
1924                 
1925                 if (num >= 0) {
1926                         item->folder->klass->remove_msg(item->folder,
1927                                                         msginfo->folder,
1928                                                         msginfo->msgnum);
1929                         remove_msginfo_from_cache(item, msginfo);
1930                 }
1931         }
1932
1933
1934         if (folder->klass->finished_copy)
1935                 folder->klass->finished_copy(folder, dest);
1936
1937         g_slist_free(newmsgnums);
1938         return lastnum;
1939 }
1940
1941 /*
1942 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
1943 {
1944         Folder *folder;
1945         gint num;
1946
1947         g_return_val_if_fail(dest != NULL, -1);
1948         g_return_val_if_fail(msginfo != NULL, -1);
1949
1950         folder = dest->folder;
1951         if (dest->last_num < 0) folder->scan(folder, dest);
1952
1953         num = folder->copy_msg(folder, dest, msginfo);
1954         if (num > 0) dest->last_num = num;
1955
1956         return num;
1957 }
1958 */
1959
1960 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
1961 {
1962         GSList *list = NULL;
1963         gint ret;
1964
1965         list = g_slist_append(list, msginfo);
1966         ret = folder_item_copy_msgs_with_dest(dest, list);
1967         g_slist_free(list);
1968         
1969         return ret;
1970 }
1971
1972 /*
1973 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
1974 {
1975         Folder *folder;
1976         gint num;
1977
1978         g_return_val_if_fail(dest != NULL, -1);
1979         g_return_val_if_fail(msglist != NULL, -1);
1980
1981         folder = dest->folder;
1982         if (dest->last_num < 0) folder->scan(folder, dest);
1983
1984         num = folder->copy_msgs_with_dest(folder, dest, msglist);
1985         if (num > 0) dest->last_num = num;
1986         else dest->op_count = 0;
1987
1988         return num;
1989 }
1990 */
1991
1992 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
1993 {
1994         Folder *folder;
1995         gint num, lastnum = -1;
1996         GSList *newmsgnums = NULL;
1997         GSList *l, *l2;
1998         gboolean folderscan = FALSE;
1999
2000         g_return_val_if_fail(dest != NULL, -1);
2001         g_return_val_if_fail(msglist != NULL, -1);
2002
2003         folder = dest->folder;
2004  
2005         g_return_val_if_fail(folder->klass->copy_msg != NULL, -1);
2006
2007         /* 
2008          * Copy messages to destination folder and 
2009          * store new message numbers in newmsgnums
2010          */
2011         for (l = msglist ; l != NULL ; l = g_slist_next(l)) {
2012                 MsgInfo * msginfo = (MsgInfo *) l->data;
2013
2014                 num = folder->klass->copy_msg(folder, dest, msginfo);
2015                 newmsgnums = g_slist_append(newmsgnums, GINT_TO_POINTER(num));
2016         }
2017
2018         /* Read cache for dest folder */
2019         if (!dest->cache) folder_item_read_cache(dest);
2020
2021         /* 
2022          * Fetch new MsgInfos for new messages in dest folder,
2023          * add them to the msgcache and update folder message counts
2024          */
2025         l2 = newmsgnums;
2026         for (l = msglist; l != NULL; l = g_slist_next(l)) {
2027                 MsgInfo *msginfo = (MsgInfo *) l->data;
2028
2029                 num = GPOINTER_TO_INT(l2->data);
2030                 l2 = g_slist_next(l2);
2031
2032                 if (num >= 0) {
2033                         MsgInfo *newmsginfo;
2034
2035                         if (num == 0) {
2036                                 gchar *file;
2037
2038                                 if (!folderscan) {
2039                                         folder_item_scan_full(dest, FALSE);
2040                                         folderscan = TRUE;
2041                                 }
2042                                 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
2043                                 num = folder_item_get_msg_num_by_file(dest, file);
2044                                 g_free(file);
2045                         }
2046         
2047                         if (num > lastnum)
2048                                 lastnum = num;
2049
2050                         if (num == 0)
2051                                 continue;
2052
2053                         if (!folderscan && 
2054                             ((newmsginfo = folder->klass->get_msginfo(folder, dest, num)) != NULL)) {
2055                                 newmsginfo = folder->klass->get_msginfo(folder, dest, num);
2056                                 add_msginfo_to_cache(dest, newmsginfo, msginfo);
2057                                 procmsg_msginfo_free(newmsginfo);
2058                         } else if ((newmsginfo = msgcache_get_msg(dest->cache, num)) != NULL) {
2059                                 copy_msginfo_flags(msginfo, newmsginfo);
2060                                 procmsg_msginfo_free(newmsginfo);
2061                         }
2062                 }
2063         }
2064         
2065         if (folder->klass->finished_copy)
2066                 folder->klass->finished_copy(folder, dest);
2067
2068         g_slist_free(newmsgnums);
2069         return lastnum;
2070 }
2071
2072 gint folder_item_remove_msg(FolderItem *item, gint num)
2073 {
2074         Folder *folder;
2075         gint ret;
2076         MsgInfo *msginfo;
2077
2078         g_return_val_if_fail(item != NULL, -1);
2079         folder = item->folder;
2080         g_return_val_if_fail(folder->klass->remove_msg != NULL, -1);
2081
2082         if (!item->cache) folder_item_read_cache(item);
2083
2084         ret = folder->klass->remove_msg(folder, item, num);
2085
2086         msginfo = msgcache_get_msg(item->cache, num);
2087         if (msginfo != NULL) {
2088                 remove_msginfo_from_cache(item, msginfo);
2089                 procmsg_msginfo_free(msginfo);
2090         }
2091         folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
2092
2093         return ret;
2094 }
2095
2096 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
2097 {
2098         Folder *folder;
2099         gint ret = 0;
2100
2101         g_return_val_if_fail(item != NULL, -1);
2102         folder = item->folder;
2103         g_return_val_if_fail(folder != NULL, -1);
2104
2105         if (!item->cache) folder_item_read_cache(item);
2106
2107         while (msglist != NULL) {
2108                 MsgInfo *msginfo = (MsgInfo *)msglist->data;
2109
2110                 ret = folder_item_remove_msg(item, msginfo->msgnum);
2111                 if (ret != 0) break;
2112                 msgcache_remove_msg(item->cache, msginfo->msgnum);
2113                 msglist = msglist->next;
2114         }
2115
2116         return ret;
2117 }
2118
2119 gint folder_item_remove_all_msg(FolderItem *item)
2120 {
2121         Folder *folder;
2122         gint result;
2123
2124         g_return_val_if_fail(item != NULL, -1);
2125
2126         folder = item->folder;
2127
2128         g_return_val_if_fail(folder->klass->remove_all_msg != NULL, -1);
2129
2130         result = folder->klass->remove_all_msg(folder, item);
2131
2132         if (result == 0) {
2133                 if (folder->klass->finished_remove)
2134                         folder->klass->finished_remove(folder, item);
2135
2136                 folder_item_free_cache(item);
2137                 item->cache = msgcache_new();
2138
2139                 item->new_msgs = 0;
2140                 item->unread_msgs = 0;
2141                 item->unreadmarked_msgs = 0;
2142                 item->total_msgs = 0;
2143                 folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
2144         }
2145
2146         return result;
2147 }
2148
2149 void folder_item_change_msg_flags(FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
2150 {
2151         g_return_if_fail(item != NULL);
2152         g_return_if_fail(msginfo != NULL);
2153         
2154         if (item->folder->klass->change_flags != NULL) {
2155                 item->folder->klass->change_flags(item->folder, item, msginfo, newflags);
2156         } else {
2157                 msginfo->flags.perm_flags = newflags;
2158         }
2159 }
2160
2161 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
2162 {
2163         Folder *folder;
2164
2165         g_return_val_if_fail(item != NULL, FALSE);
2166
2167         folder = item->folder;
2168
2169         g_return_val_if_fail(folder->klass->is_msg_changed != NULL, -1);
2170
2171         return folder->klass->is_msg_changed(folder, item, msginfo);
2172 }
2173
2174 gchar *folder_item_get_cache_file(FolderItem *item)
2175 {
2176         gchar *path;
2177         gchar *file;
2178
2179         g_return_val_if_fail(item != NULL, NULL);
2180         g_return_val_if_fail(item->path != NULL, NULL);
2181
2182         path = folder_item_get_path(item);
2183         g_return_val_if_fail(path != NULL, NULL);
2184         if (!is_dir_exist(path))
2185                 make_dir_hier(path);
2186         file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
2187         g_free(path);
2188
2189         return file;
2190 }
2191
2192 gchar *folder_item_get_mark_file(FolderItem *item)
2193 {
2194         gchar *path;
2195         gchar *file;
2196
2197         g_return_val_if_fail(item != NULL, NULL);
2198         g_return_val_if_fail(item->path != NULL, NULL);
2199
2200         path = folder_item_get_path(item);
2201         g_return_val_if_fail(path != NULL, NULL);
2202         if (!is_dir_exist(path))
2203                 make_dir_hier(path);
2204         file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
2205         g_free(path);
2206
2207         return file;
2208 }
2209
2210 static gboolean folder_build_tree(GNode *node, gpointer data)
2211 {
2212         Folder *folder = FOLDER(data);
2213         FolderItem *item;
2214         XMLNode *xmlnode;
2215         GList *list;
2216         SpecialFolderItemType stype = F_NORMAL;
2217         const gchar *name = NULL;
2218         const gchar *path = NULL;
2219         PrefsAccount *account = NULL;
2220         gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE, 
2221                  threaded = TRUE, apply_sub = FALSE;
2222         gboolean ret_rcpt = FALSE, hidereadmsgs = FALSE,
2223                  thread_collapsed = FALSE; /* CLAWS */
2224         FolderSortKey sort_key = SORT_BY_NONE;
2225         FolderSortType sort_type = SORT_ASCENDING;
2226         gint new = 0, unread = 0, total = 0, unreadmarked = 0;
2227         time_t mtime = 0;
2228
2229         g_return_val_if_fail(node->data != NULL, FALSE);
2230         if (!node->parent) return FALSE;
2231
2232         xmlnode = node->data;
2233         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
2234                 g_warning("tag name != \"folderitem\"\n");
2235                 return FALSE;
2236         }
2237
2238         list = xmlnode->tag->attr;
2239         for (; list != NULL; list = list->next) {
2240                 XMLAttr *attr = list->data;
2241
2242                 if (!attr || !attr->name || !attr->value) continue;
2243                 if (!strcmp(attr->name, "type")) {
2244                         if (!strcasecmp(attr->value, "normal"))
2245                                 stype = F_NORMAL;
2246                         else if (!strcasecmp(attr->value, "inbox"))
2247                                 stype = F_INBOX;
2248                         else if (!strcasecmp(attr->value, "outbox"))
2249                                 stype = F_OUTBOX;
2250                         else if (!strcasecmp(attr->value, "draft"))
2251                                 stype = F_DRAFT;
2252                         else if (!strcasecmp(attr->value, "queue"))
2253                                 stype = F_QUEUE;
2254                         else if (!strcasecmp(attr->value, "trash"))
2255                                 stype = F_TRASH;
2256                 } else if (!strcmp(attr->name, "name"))
2257                         name = attr->value;
2258                 else if (!strcmp(attr->name, "path"))
2259                         path = attr->value;
2260                 else if (!strcmp(attr->name, "mtime"))
2261                         mtime = strtoul(attr->value, NULL, 10);
2262                 else if (!strcmp(attr->name, "new"))
2263                         new = atoi(attr->value);
2264                 else if (!strcmp(attr->name, "unread"))
2265                         unread = atoi(attr->value);
2266                 else if (!strcmp(attr->name, "unreadmarked"))
2267                         unreadmarked = atoi(attr->value);
2268                 else if (!strcmp(attr->name, "total"))
2269                         total = atoi(attr->value);
2270                 else if (!strcmp(attr->name, "no_sub"))
2271                         no_sub = *attr->value == '1' ? TRUE : FALSE;
2272                 else if (!strcmp(attr->name, "no_select"))
2273                         no_select = *attr->value == '1' ? TRUE : FALSE;
2274                 else if (!strcmp(attr->name, "collapsed"))
2275                         collapsed = *attr->value == '1' ? TRUE : FALSE;
2276                 else if (!strcmp(attr->name, "thread_collapsed"))
2277                         thread_collapsed =  *attr->value == '1' ? TRUE : FALSE;
2278                 else if (!strcmp(attr->name, "threaded"))
2279                         threaded =  *attr->value == '1' ? TRUE : FALSE;
2280                 else if (!strcmp(attr->name, "hidereadmsgs"))
2281                         hidereadmsgs =  *attr->value == '1' ? TRUE : FALSE;
2282                 else if (!strcmp(attr->name, "reqretrcpt"))
2283                         ret_rcpt =  *attr->value == '1' ? TRUE : FALSE;
2284                 else if (!strcmp(attr->name, "sort_key")) {
2285                         if (!strcmp(attr->value, "none"))
2286                                 sort_key = SORT_BY_NONE;
2287                         else if (!strcmp(attr->value, "number"))
2288                                 sort_key = SORT_BY_NUMBER;
2289                         else if (!strcmp(attr->value, "size"))
2290                                 sort_key = SORT_BY_SIZE;
2291                         else if (!strcmp(attr->value, "date"))
2292                                 sort_key = SORT_BY_DATE;
2293                         else if (!strcmp(attr->value, "from"))
2294                                 sort_key = SORT_BY_FROM;
2295                         else if (!strcmp(attr->value, "subject"))
2296                                 sort_key = SORT_BY_SUBJECT;
2297                         else if (!strcmp(attr->value, "score"))
2298                                 sort_key = SORT_BY_SCORE;
2299                         else if (!strcmp(attr->value, "label"))
2300                                 sort_key = SORT_BY_LABEL;
2301                         else if (!strcmp(attr->value, "mark"))
2302                                 sort_key = SORT_BY_MARK;
2303                         else if (!strcmp(attr->value, "unread"))
2304                                 sort_key = SORT_BY_STATUS;
2305                         else if (!strcmp(attr->value, "mime"))
2306                                 sort_key = SORT_BY_MIME;
2307                         else if (!strcmp(attr->value, "to"))
2308                                 sort_key = SORT_BY_TO;
2309                         else if (!strcmp(attr->value, "locked"))
2310                                 sort_key = SORT_BY_LOCKED;
2311                 } else if (!strcmp(attr->name, "sort_type")) {
2312                         if (!strcmp(attr->value, "ascending"))
2313                                 sort_type = SORT_ASCENDING;
2314                         else
2315                                 sort_type = SORT_DESCENDING;
2316                 } else if (!strcmp(attr->name, "account_id")) {
2317                         account = account_find_from_id(atoi(attr->value));
2318                         if (!account) g_warning("account_id: %s not found\n",
2319                                                 attr->value);
2320                 } else if (!strcmp(attr->name, "apply_sub"))
2321                         apply_sub = *attr->value == '1' ? TRUE : FALSE;
2322         }
2323
2324         item = folder_item_new(folder, name, path);
2325         item->stype = stype;
2326         item->mtime = mtime;
2327         item->new_msgs = new;
2328         item->unread_msgs = unread;
2329         item->unreadmarked_msgs = unreadmarked;
2330         item->total_msgs = total;
2331         item->no_sub = no_sub;
2332         item->no_select = no_select;
2333         item->collapsed = collapsed;
2334         item->thread_collapsed = thread_collapsed;
2335         item->threaded  = threaded;
2336         item->hide_read_msgs  = hidereadmsgs;
2337         item->ret_rcpt  = ret_rcpt;
2338         item->sort_key  = sort_key;
2339         item->sort_type = sort_type;
2340         item->parent = FOLDER_ITEM(node->parent->data);
2341         item->folder = folder;
2342         switch (stype) {
2343         case F_INBOX:  folder->inbox  = item; break;
2344         case F_OUTBOX: folder->outbox = item; break;
2345         case F_DRAFT:  folder->draft  = item; break;
2346         case F_QUEUE:  folder->queue  = item; break;
2347         case F_TRASH:  folder->trash  = item; break;
2348         default:       break;
2349         }
2350         item->account = account;
2351         item->apply_sub = apply_sub;
2352         prefs_folder_item_read_config(item);
2353
2354         node->data = item;
2355         xml_free_node(xmlnode);
2356
2357         return FALSE;
2358 }
2359
2360 static gboolean folder_read_folder_func(GNode *node, gpointer data)
2361 {
2362         Folder *folder;
2363         XMLNode *xmlnode;
2364         GList *list;
2365         FolderClass *class = NULL;
2366         const gchar *name = NULL;
2367         const gchar *path = NULL;
2368         PrefsAccount *account = NULL;
2369         gboolean collapsed = FALSE, threaded = TRUE, apply_sub = FALSE;
2370         gboolean ret_rcpt = FALSE, thread_collapsed = FALSE; /* CLAWS */
2371
2372         if (g_node_depth(node) != 2) return FALSE;
2373         g_return_val_if_fail(node->data != NULL, FALSE);
2374
2375         xmlnode = node->data;
2376         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
2377                 g_warning("tag name != \"folder\"\n");
2378                 return TRUE;
2379         }
2380         g_node_unlink(node);
2381         list = xmlnode->tag->attr;
2382         for (; list != NULL; list = list->next) {
2383                 XMLAttr *attr = list->data;
2384
2385                 if (!attr || !attr->name || !attr->value) continue;
2386                 if (!strcmp(attr->name, "type"))
2387                         class = folder_get_class_from_string(attr->value);
2388                 else if (!strcmp(attr->name, "name"))
2389                         name = attr->value;
2390                 else if (!strcmp(attr->name, "path"))
2391                         path = attr->value;
2392                 else if (!strcmp(attr->name, "collapsed"))
2393                         collapsed = *attr->value == '1' ? TRUE : FALSE;
2394                 else if (!strcmp(attr->name, "thread_collapsed"))
2395                         thread_collapsed = *attr->value == '1' ? TRUE : FALSE;
2396                 else if (!strcmp(attr->name, "threaded"))
2397                         threaded = *attr->value == '1' ? TRUE : FALSE;
2398                 else if (!strcmp(attr->name, "account_id")) {
2399                         account = account_find_from_id(atoi(attr->value));
2400                         if (!account) g_warning("account_id: %s not found\n",
2401                                                 attr->value);
2402                 } else if (!strcmp(attr->name, "apply_sub"))
2403                         apply_sub = *attr->value == '1' ? TRUE : FALSE;
2404                 else if (!strcmp(attr->name, "reqretrcpt"))
2405                         ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
2406         }
2407
2408         folder = folder_new(class, name, path);
2409         g_return_val_if_fail(folder != NULL, FALSE);
2410         folder->account = account;
2411         if (account != NULL)
2412                 account->folder = REMOTE_FOLDER(folder);
2413         node->data = folder->node->data;
2414         g_node_destroy(folder->node);
2415         folder->node = node;
2416         folder_add(folder);
2417         FOLDER_ITEM(node->data)->collapsed = collapsed;
2418         FOLDER_ITEM(node->data)->thread_collapsed = thread_collapsed;
2419         FOLDER_ITEM(node->data)->threaded  = threaded;
2420         FOLDER_ITEM(node->data)->account   = account;
2421         FOLDER_ITEM(node->data)->apply_sub = apply_sub;
2422         FOLDER_ITEM(node->data)->ret_rcpt  = ret_rcpt;
2423
2424         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2425                         folder_build_tree, folder);
2426
2427         return FALSE;
2428 }
2429
2430 static gchar *folder_get_list_path(void)
2431 {
2432         static gchar *filename = NULL;
2433
2434         if (!filename)
2435                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2436                                         FOLDER_LIST, NULL);
2437
2438         return filename;
2439 }
2440
2441 #define PUT_ESCAPE_STR(fp, attr, str)                   \
2442 {                                                       \
2443         fputs(" " attr "=\"", fp);                      \
2444         xml_file_put_escape_str(fp, str);               \
2445         fputs("\"", fp);                                \
2446 }
2447
2448 static void folder_write_list_recursive(GNode *node, gpointer data)
2449 {
2450         FILE *fp = (FILE *)data;
2451         FolderItem *item;
2452         gint i, depth;
2453         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
2454                                                  "draft", "queue", "trash"};
2455         static gchar *sort_key_str[] = {"none", "number", "size", "date",
2456                                         "from", "subject", "score", "label",
2457                                         "mark", "unread", "mime", "to", 
2458                                         "locked"};
2459         g_return_if_fail(node != NULL);
2460         g_return_if_fail(fp != NULL);
2461
2462         item = FOLDER_ITEM(node->data);
2463         g_return_if_fail(item != NULL);
2464
2465         depth = g_node_depth(node);
2466         for (i = 0; i < depth; i++)
2467                 fputs("    ", fp);
2468         if (depth == 1) {
2469                 Folder *folder = item->folder;
2470
2471                 fprintf(fp, "<folder type=\"%s\"", folder->klass->idstr);
2472                 if (folder->name)
2473                         PUT_ESCAPE_STR(fp, "name", folder->name);
2474                 if (FOLDER_TYPE(folder) == F_MH || FOLDER_TYPE(folder) == F_MBOX)
2475                         PUT_ESCAPE_STR(fp, "path",
2476                                        LOCAL_FOLDER(folder)->rootpath);
2477                 if (item->collapsed && node->children)
2478                         fputs(" collapsed=\"1\"", fp);
2479                 if (folder->account)
2480                         fprintf(fp, " account_id=\"%d\"",
2481                                 folder->account->account_id);
2482                 if (item->apply_sub)
2483                         fputs(" apply_sub=\"1\"", fp);
2484                 if (item->ret_rcpt) 
2485                         fputs(" reqretrcpt=\"1\"", fp);
2486         } else {
2487                 fprintf(fp, "<folderitem type=\"%s\"",
2488                         folder_item_stype_str[item->stype]);
2489                 if (item->name)
2490                         PUT_ESCAPE_STR(fp, "name", item->name);
2491                 if (item->path)
2492                         PUT_ESCAPE_STR(fp, "path", item->path);
2493
2494                 if (item->no_sub)
2495                         fputs(" no_sub=\"1\"", fp);
2496                 if (item->no_select)
2497                         fputs(" no_select=\"1\"", fp);
2498                 if (item->collapsed && node->children)
2499                         fputs(" collapsed=\"1\"", fp);
2500                 else
2501                         fputs(" collapsed=\"0\"", fp);
2502                 if (item->thread_collapsed)
2503                         fputs(" thread_collapsed=\"1\"", fp);
2504                 else
2505                         fputs(" thread_collapsed=\"0\"", fp);
2506                 if (item->threaded)
2507                         fputs(" threaded=\"1\"", fp);
2508                 else
2509                         fputs(" threaded=\"0\"", fp);
2510                 if (item->hide_read_msgs)
2511                         fputs(" hidereadmsgs=\"1\"", fp);
2512                 else
2513                         fputs(" hidereadmsgs=\"0\"", fp);
2514                 if (item->ret_rcpt)
2515                         fputs(" reqretrcpt=\"1\"", fp);
2516
2517                 if (item->sort_key != SORT_BY_NONE) {
2518                         fprintf(fp, " sort_key=\"%s\"",
2519                                 sort_key_str[item->sort_key]);
2520                         if (item->sort_type == SORT_ASCENDING)
2521                                 fprintf(fp, " sort_type=\"ascending\"");
2522                         else
2523                                 fprintf(fp, " sort_type=\"descending\"");
2524                 }
2525
2526                 fprintf(fp,
2527                         " mtime=\"%lu\" new=\"%d\" unread=\"%d\" unreadmarked=\"%d\" total=\"%d\"",
2528                         item->mtime, item->new_msgs, item->unread_msgs, item->unreadmarked_msgs, item->total_msgs);
2529
2530                 if (item->account)
2531                         fprintf(fp, " account_id=\"%d\"",
2532                                 item->account->account_id);
2533                 if (item->apply_sub)
2534                         fputs(" apply_sub=\"1\"", fp);
2535         }
2536
2537         if (node->children) {
2538                 GNode *child;
2539                 fputs(">\n", fp);
2540
2541                 child = node->children;
2542                 while (child) {
2543                         GNode *cur;
2544
2545                         cur = child;
2546                         child = cur->next;
2547                         folder_write_list_recursive(cur, data);
2548                 }
2549
2550                 for (i = 0; i < depth; i++)
2551                         fputs("    ", fp);
2552                 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
2553         } else
2554                 fputs(" />\n", fp);
2555 }
2556
2557 static void folder_update_op_count_rec(GNode *node)
2558 {
2559         FolderItem *fitem = FOLDER_ITEM(node->data);
2560
2561         if (g_node_depth(node) > 0) {
2562                 if (fitem->op_count > 0) {
2563                         fitem->op_count = 0;
2564                         folder_item_update(fitem, F_ITEM_UPDATE_MSGCNT);
2565                 }
2566                 if (node->children) {
2567                         GNode *child;
2568
2569                         child = node->children;
2570                         while (child) {
2571                                 GNode *cur;
2572
2573                                 cur = child;
2574                                 child = cur->next;
2575                                 folder_update_op_count_rec(cur);
2576                         }
2577                 }
2578         }
2579 }
2580
2581 void folder_update_op_count(void) 
2582 {
2583         GList *cur;
2584         Folder *folder;
2585
2586         for (cur = folder_list; cur != NULL; cur = cur->next) {
2587                 folder = cur->data;
2588                 folder_update_op_count_rec(folder->node);
2589         }
2590 }
2591
2592 typedef struct _type_str {
2593         gchar * str;
2594         gint type;
2595 } type_str;
2596
2597
2598 /*
2599 static gchar * folder_item_get_tree_identifier(FolderItem * item)
2600 {
2601         if (item->parent != NULL) {
2602                 gchar * path;
2603                 gchar * id;
2604
2605                 path = folder_item_get_tree_identifier(item->parent);
2606                 if (path == NULL)
2607                         return NULL;
2608
2609                 id = g_strconcat(path, "/", item->name, NULL);
2610                 g_free(path);
2611
2612                 return id;
2613         }
2614         else {
2615                 return g_strconcat("/", item->name, NULL);
2616         }
2617 }
2618 */
2619
2620 /* CLAWS: temporary local folder for filtering */
2621 #define TEMP_FOLDER "TEMP_FOLDER"
2622 #define PROCESSING_FOLDER_ITEM "processing"     
2623
2624 static FolderItem *processing_folder_item;
2625
2626 static void folder_create_processing_folder(void)
2627 {
2628         Folder *processing_folder;
2629         gchar      *tmpname;
2630
2631         if ((processing_folder = folder_find_from_name(TEMP_FOLDER, mh_get_class())) == NULL) {
2632                 gchar *tmppath;
2633
2634                 tmppath =
2635                     g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2636                                 "tempfolder", NULL);
2637                 processing_folder =
2638                     folder_new(mh_get_class(), TEMP_FOLDER, tmppath);
2639                 g_free(tmppath);
2640         }
2641         g_assert(processing_folder != NULL);
2642
2643         debug_print("tmpparentroot %s\n", LOCAL_FOLDER(processing_folder)->rootpath);
2644         if (LOCAL_FOLDER(processing_folder)->rootpath[0] == '/')
2645                 tmpname = g_strconcat(LOCAL_FOLDER(processing_folder)->rootpath,
2646                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
2647                                       NULL);
2648         else
2649                 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2650                                       LOCAL_FOLDER(processing_folder)->rootpath,
2651                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
2652                                       NULL);
2653
2654         if (!is_dir_exist(tmpname)) {
2655                 debug_print("*TMP* creating %s\n", tmpname);
2656                 processing_folder_item = processing_folder->klass->create_folder(processing_folder,
2657                                                                                  processing_folder->node->data,
2658                                                                                  PROCESSING_FOLDER_ITEM);
2659         } else {
2660                 debug_print("*TMP* already created\n");
2661                 processing_folder_item = folder_item_new(processing_folder, PROCESSING_FOLDER_ITEM, PROCESSING_FOLDER_ITEM);
2662                 g_assert(processing_folder_item);
2663                 folder_item_append(processing_folder->node->data, processing_folder_item);
2664         }
2665         g_assert(processing_folder_item != NULL);
2666         g_free(tmpname);
2667 }
2668
2669 FolderItem *folder_get_default_processing(void)
2670 {
2671         if (!processing_folder_item) {
2672                 folder_create_processing_folder();
2673         }
2674         return processing_folder_item;
2675 }
2676
2677 /* folder_persist_prefs_new() - return hash table with persistent
2678  * settings (and folder name as key). 
2679  * (note that in claws other options are in the PREFS_FOLDER_ITEM_RC
2680  * file, so those don't need to be included in PersistPref yet) 
2681  */
2682 GHashTable *folder_persist_prefs_new(Folder *folder)
2683 {
2684         GHashTable *pptable;
2685
2686         g_return_val_if_fail(folder, NULL);
2687         pptable = g_hash_table_new(g_str_hash, g_str_equal);
2688         folder_get_persist_prefs_recursive(folder->node, pptable);
2689         return pptable;
2690 }
2691
2692 void folder_persist_prefs_free(GHashTable *pptable)
2693 {
2694         g_return_if_fail(pptable);
2695         g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
2696         g_hash_table_destroy(pptable);
2697 }
2698
2699 const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
2700 {
2701         if (pptable == NULL || name == NULL) return NULL;
2702         return g_hash_table_lookup(pptable, name);
2703 }
2704
2705 void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
2706 {
2707         const PersistPrefs *pp;
2708         gchar *id = folder_item_get_identifier(item);
2709
2710         pp = folder_get_persist_prefs(pptable, id); 
2711         g_free(id);
2712
2713         if (!pp) return;
2714
2715         /* CLAWS: since not all folder properties have been migrated to 
2716          * folderlist.xml, we need to call the old stuff first before
2717          * setting things that apply both to Main and Claws. */
2718         prefs_folder_item_read_config(item); 
2719          
2720         item->collapsed = pp->collapsed;
2721         item->thread_collapsed = pp->thread_collapsed;
2722         item->threaded  = pp->threaded;
2723         item->ret_rcpt  = pp->ret_rcpt;
2724         item->hide_read_msgs = pp->hide_read_msgs;
2725         item->sort_key  = pp->sort_key;
2726         item->sort_type = pp->sort_type;
2727 }
2728
2729 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
2730 {
2731         FolderItem *item = FOLDER_ITEM(node->data);
2732         PersistPrefs *pp;
2733         GNode *child, *cur;
2734         gchar *id;
2735
2736         g_return_if_fail(node != NULL);
2737         g_return_if_fail(item != NULL);
2738
2739         /* NOTE: item->path == NULL means top level folder; not interesting
2740          * to store preferences of that one.  */
2741         if (item->path) {
2742                 id = folder_item_get_identifier(item);
2743                 pp = g_new0(PersistPrefs, 1);
2744                 g_return_if_fail(pp != NULL);
2745                 pp->collapsed = item->collapsed;
2746                 pp->thread_collapsed = item->thread_collapsed;
2747                 pp->threaded  = item->threaded;
2748                 pp->ret_rcpt  = item->ret_rcpt; 
2749                 pp->hide_read_msgs = item->hide_read_msgs;
2750                 pp->sort_key  = item->sort_key;
2751                 pp->sort_type = item->sort_type;
2752                 g_hash_table_insert(pptable, id, pp);
2753         }
2754
2755         if (node->children) {
2756                 child = node->children;
2757                 while (child) {
2758                         cur = child;
2759                         child = cur->next;
2760                         folder_get_persist_prefs_recursive(cur, pptable);
2761                 }
2762         }       
2763 }
2764
2765 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
2766 {
2767         if (key) 
2768                 g_free(key);
2769         if (val) 
2770                 g_free(val);
2771         return TRUE;    
2772 }
2773
2774 void folder_item_apply_processing(FolderItem *item)
2775 {
2776         GSList *processing_list;
2777         GSList *mlist, *cur;
2778         
2779         g_return_if_fail(item != NULL);
2780         
2781         processing_list = item->prefs->processing;
2782         if (processing_list == NULL)
2783                 return;
2784
2785         folder_item_update_freeze();
2786
2787         mlist = folder_item_get_msg_list(item);
2788         for (cur = mlist ; cur != NULL ; cur = cur->next) {
2789                 MsgInfo * msginfo;
2790
2791                 msginfo = (MsgInfo *) cur->data;
2792                 filter_message_by_msginfo(processing_list, msginfo);
2793                 procmsg_msginfo_free(msginfo);
2794         }
2795         g_slist_free(mlist);
2796
2797         folder_item_update_thaw();
2798 }
2799
2800 /*
2801  *  functions for handling FolderItem content changes
2802  */
2803 static gint folder_item_update_freeze_cnt = 0;
2804
2805 /**
2806  * Notify the folder system about changes to a folder. If the
2807  * update system is not frozen the FOLDER_ITEM_UPDATE_HOOKLIST will
2808  * be invoked, otherwise the changes will be remebered until
2809  * the folder system is thawed.
2810  *
2811  * \param item The FolderItem that was changed
2812  * \param update_flags Type of changed that was made
2813  */
2814 void folder_item_update(FolderItem *item, FolderItemUpdateFlags update_flags)
2815 {
2816         if (folder_item_update_freeze_cnt == 0) {
2817                 FolderItemUpdateData source;
2818         
2819                 source.item = item;
2820                 source.update_flags = update_flags;
2821                 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
2822         } else {
2823                 item->update_flags |= update_flags;
2824         }
2825 }
2826
2827 void folder_item_update_recursive(FolderItem *item, FolderItemUpdateFlags update_flags)
2828 {
2829         GNode *node = item->folder->node;       
2830
2831         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2832         node = node->children;
2833
2834         folder_item_update(item, update_flags);
2835         while (node != NULL) {
2836                 if (node && node->data) {
2837                         FolderItem *next_item = (FolderItem*) node->data;
2838
2839                         folder_item_update(next_item, update_flags);
2840                 }
2841                 node = node->next;
2842         }
2843 }
2844
2845 void folder_item_update_freeze(void)
2846 {
2847         folder_item_update_freeze_cnt++;
2848 }
2849
2850 static void folder_item_update_func(FolderItem *item, gpointer data)
2851 {
2852         FolderItemUpdateData source;
2853     
2854         if (item->update_flags) {
2855                 source.item = item;
2856                 source.update_flags = item->update_flags;
2857                 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);                             
2858                 item->update_flags = 0;
2859         }
2860 }
2861
2862 void folder_item_update_thaw(void)
2863 {
2864         if (folder_item_update_freeze_cnt > 0)
2865                 folder_item_update_freeze_cnt--;
2866         if (folder_item_update_freeze_cnt == 0) {
2867                 /* Update all folders */
2868                 folder_func_to_all_folders(folder_item_update_func, NULL);
2869         }
2870 }
2871
2872 #undef PUT_ESCAPE_STR