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