0.9.4claws75
[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 || FOLDER_TYPE(folder) == F_MBOX) &&
704                     !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
705                         return folder;
706         }
707
708         return NULL;
709 }
710
711 Folder *folder_find_from_name(const gchar *name, FolderClass *klass)
712 {
713         GList *list;
714         Folder *folder;
715
716         for (list = folder_list; list != NULL; list = list->next) {
717                 folder = list->data;
718                 if (folder->klass == klass && strcmp2(name, folder->name) == 0)
719                         return folder;
720         }
721
722         return NULL;
723 }
724
725 static gboolean folder_item_find_func(GNode *node, gpointer data)
726 {
727         FolderItem *item = node->data;
728         gpointer *d = data;
729         const gchar *path = d[0];
730
731         if (path_cmp(path, item->path) != 0)
732                 return FALSE;
733
734         d[1] = item;
735
736         return TRUE;
737 }
738
739 FolderItem *folder_find_item_from_path(const gchar *path)
740 {
741         Folder *folder;
742         gpointer d[2];
743
744         folder = folder_get_default_folder();
745         g_return_val_if_fail(folder != NULL, NULL);
746
747         d[0] = (gpointer)path;
748         d[1] = NULL;
749         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
750                         folder_item_find_func, d);
751         return d[1];
752 }
753
754 FolderClass *folder_get_class_from_string(const gchar *str)
755 {
756         GSList *classlist;
757
758         classlist = folder_get_class_list();
759         for (; classlist != NULL; classlist = g_slist_next(classlist)) {
760                 FolderClass *class = (FolderClass *) classlist->data;
761                 if (g_strcasecmp(class->idstr, str) == 0)
762                         return class;
763         }
764
765         return NULL;
766 }
767
768 gchar *folder_get_identifier(Folder *folder)
769 {
770         gchar *type_str;
771
772         g_return_val_if_fail(folder != NULL, NULL);
773
774         type_str = folder->klass->idstr;
775         return g_strconcat("#", type_str, "/", folder->name, NULL);
776 }
777
778 gchar *folder_item_get_identifier(FolderItem *item)
779 {
780         gchar *id;
781         gchar *folder_id;
782
783         g_return_val_if_fail(item != NULL, NULL);
784         g_return_val_if_fail(item->path != NULL, NULL);
785
786         folder_id = folder_get_identifier(item->folder);
787         id = g_strconcat(folder_id, "/", item->path, NULL);
788         g_free(folder_id);
789
790         return id;
791 }
792
793 FolderItem *folder_find_item_from_identifier(const gchar *identifier)
794 {
795         Folder *folder;
796         gpointer d[2];
797         gchar *str;
798         gchar *p;
799         gchar *name;
800         gchar *path;
801         FolderClass *class;
802
803         g_return_val_if_fail(identifier != NULL, NULL);
804
805         if (*identifier != '#')
806                 return folder_find_item_from_path(identifier);
807
808         Xstrdup_a(str, identifier, return NULL);
809
810         p = strchr(str, '/');
811         if (!p)
812                 return folder_find_item_from_path(identifier);
813         *p = '\0';
814         p++;
815         class = folder_get_class_from_string(&str[1]);
816         if (class == NULL)
817                 return folder_find_item_from_path(identifier);
818
819         name = p;
820         p = strchr(p, '/');
821         if (!p)
822                 return folder_find_item_from_path(identifier);
823         *p = '\0';
824         p++;
825
826         folder = folder_find_from_name(name, class);
827         if (!folder)
828                 return folder_find_item_from_path(identifier);
829
830         path = p;
831
832         d[0] = (gpointer)path;
833         d[1] = NULL;
834         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
835                         folder_item_find_func, d);
836         return d[1];
837 }
838
839 /**
840  * Get a displayable name for a FolderItem
841  *
842  * \param item FolderItem for that a name should be created
843  * \return Displayable name for item, returned string has to
844  *         be freed
845  */
846 gchar *folder_item_get_name(FolderItem *item)
847 {
848         gchar *name = NULL;
849
850         g_return_val_if_fail(item != NULL, g_strdup(""));
851
852         switch (item->stype) {
853         case F_INBOX:
854                 name = g_strdup(!strcmp2(item->name, INBOX_DIR) ? _("Inbox") :
855                                 item->name);
856                 break;
857         case F_OUTBOX:
858                 name = g_strdup(!strcmp2(item->name, OUTBOX_DIR) ? _("Sent") :
859                                 item->name);
860                 break;
861         case F_QUEUE:
862                 name = g_strdup(!strcmp2(item->name, QUEUE_DIR) ? _("Queue") :
863                                 item->name);
864                 break;
865         case F_TRASH:
866                 name = g_strdup(!strcmp2(item->name, TRASH_DIR) ? _("Trash") :
867                                 item->name);
868                 break;
869         case F_DRAFT:
870                 name = g_strdup(!strcmp2(item->name, DRAFT_DIR) ? _("Drafts") :
871                                 item->name);
872                 break;
873         default:
874                 break;
875         }
876
877         if (name == NULL) {
878                 /*
879                  * should probably be done by a virtual function,
880                  * the folder knows the ui string and how to abbrev
881                 */
882                 if (!item->parent) {
883                         name = g_strconcat(item->name, " (", item->folder->klass->uistr, ")", NULL);
884                 } else {
885                         if (FOLDER_CLASS(item->folder) == news_get_class() &&
886                             item->path && !strcmp2(item->name, item->path))
887                                 name = get_abbrev_newsgroup_name
888                                         (item->path,
889                                          prefs_common.ng_abbrev_len);
890                         else
891                                 name = g_strdup(item->name);
892                 }
893         }
894
895         if (name == NULL)
896                 name = g_strdup("");
897
898         return name;
899 }
900
901 Folder *folder_get_default_folder(void)
902 {
903         return folder_list ? FOLDER(folder_list->data) : NULL;
904 }
905
906 FolderItem *folder_get_default_inbox(void)
907 {
908         Folder *folder;
909
910         if (!folder_list) return NULL;
911         folder = FOLDER(folder_list->data);
912         g_return_val_if_fail(folder != NULL, NULL);
913         return folder->inbox;
914 }
915
916 FolderItem *folder_get_default_outbox(void)
917 {
918         Folder *folder;
919
920         if (!folder_list) return NULL;
921         folder = FOLDER(folder_list->data);
922         g_return_val_if_fail(folder != NULL, NULL);
923         return folder->outbox;
924 }
925
926 FolderItem *folder_get_default_draft(void)
927 {
928         Folder *folder;
929
930         if (!folder_list) return NULL;
931         folder = FOLDER(folder_list->data);
932         g_return_val_if_fail(folder != NULL, NULL);
933         return folder->draft;
934 }
935
936 FolderItem *folder_get_default_queue(void)
937 {
938         Folder *folder;
939
940         if (!folder_list) return NULL;
941         folder = FOLDER(folder_list->data);
942         g_return_val_if_fail(folder != NULL, NULL);
943         return folder->queue;
944 }
945
946 FolderItem *folder_get_default_trash(void)
947 {
948         Folder *folder;
949
950         if (!folder_list) return NULL;
951         folder = FOLDER(folder_list->data);
952         g_return_val_if_fail(folder != NULL, NULL);
953         return folder->trash;
954 }
955
956 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type)           \
957 {                                                               \
958         if (!folder->member) {                                  \
959                 item = folder_item_new(folder, dir, dir);       \
960                 item->stype = type;                             \
961                 folder_item_append(rootitem, item);             \
962                 folder->member = item;                          \
963         }                                                       \
964 }
965
966 void folder_set_missing_folders(void)
967 {
968         Folder *folder;
969         FolderItem *rootitem;
970         FolderItem *item;
971         GList *list;
972
973         for (list = folder_list; list != NULL; list = list->next) {
974                 folder = list->data;
975                 if (FOLDER_TYPE(folder) != F_MH) continue;
976                 rootitem = FOLDER_ITEM(folder->node->data);
977                 g_return_if_fail(rootitem != NULL);
978
979                 if (folder->inbox && folder->outbox && folder->draft &&
980                     folder->queue && folder->trash)
981                         continue;
982
983                 if (folder->klass->create_tree(folder) < 0) {
984                         g_warning("%s: can't create the folder tree.\n",
985                                   LOCAL_FOLDER(folder)->rootpath);
986                         continue;
987                 }
988
989                 CREATE_FOLDER_IF_NOT_EXIST(inbox,  INBOX_DIR,  F_INBOX);
990                 CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
991                 CREATE_FOLDER_IF_NOT_EXIST(draft,  DRAFT_DIR,  F_DRAFT);
992                 CREATE_FOLDER_IF_NOT_EXIST(queue,  QUEUE_DIR,  F_QUEUE);
993                 CREATE_FOLDER_IF_NOT_EXIST(trash,  TRASH_DIR,  F_TRASH);
994         }
995 }
996
997 static gboolean folder_unref_account_func(GNode *node, gpointer data)
998 {
999         FolderItem *item = node->data;
1000         PrefsAccount *account = data;
1001
1002         if (item->account == account)
1003                 item->account = NULL;
1004
1005         return FALSE;
1006 }
1007
1008 void folder_unref_account_all(PrefsAccount *account)
1009 {
1010         Folder *folder;
1011         GList *list;
1012
1013         if (!account) return;
1014
1015         for (list = folder_list; list != NULL; list = list->next) {
1016                 folder = list->data;
1017                 if (folder->account == account)
1018                         folder->account = NULL;
1019                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1020                                 folder_unref_account_func, account);
1021         }
1022 }
1023
1024 #undef CREATE_FOLDER_IF_NOT_EXIST
1025
1026 gchar *folder_item_get_path(FolderItem *item)
1027 {
1028         Folder *folder;
1029
1030         g_return_val_if_fail(item != NULL, NULL);
1031         folder = item->folder;
1032         g_return_val_if_fail(folder != NULL, NULL);
1033
1034         return folder->klass->item_get_path(folder, item);
1035 }
1036
1037 void folder_item_set_default_flags(FolderItem *dest, MsgFlags *flags)
1038 {
1039         if (!(dest->stype == F_OUTBOX ||
1040               dest->stype == F_QUEUE  ||
1041               dest->stype == F_DRAFT  ||
1042               dest->stype == F_TRASH)) {
1043                 flags->perm_flags = MSG_NEW|MSG_UNREAD;
1044         } else {
1045                 flags->perm_flags = 0;
1046         }
1047         flags->tmp_flags = MSG_CACHED;
1048         if (FOLDER_TYPE(dest->folder) == F_MH) {
1049                 if (dest->stype == F_QUEUE) {
1050                         MSG_SET_TMP_FLAGS(*flags, MSG_QUEUED);
1051                 } else if (dest->stype == F_DRAFT) {
1052                         MSG_SET_TMP_FLAGS(*flags, MSG_DRAFT);
1053                 }
1054         }
1055 }
1056
1057 static gint folder_sort_cache_list_by_msgnum(gconstpointer a, gconstpointer b)
1058 {
1059         MsgInfo *msginfo_a = (MsgInfo *) a;
1060         MsgInfo *msginfo_b = (MsgInfo *) b;
1061
1062         return (msginfo_a->msgnum - msginfo_b->msgnum);
1063 }
1064
1065 static gint folder_sort_folder_list(gconstpointer a, gconstpointer b)
1066 {
1067         guint gint_a = GPOINTER_TO_INT(a);
1068         guint gint_b = GPOINTER_TO_INT(b);
1069         
1070         return (gint_a - gint_b);
1071 }
1072
1073 gint folder_item_open(FolderItem *item)
1074 {
1075         if((item->folder->klass->scan_required != NULL) && (item->folder->klass->scan_required(item->folder, item))) {
1076                 folder_item_scan_full(item, TRUE);
1077         }
1078
1079         /* Processing */
1080         if(item->prefs->processing != NULL) {
1081                 gchar *buf;
1082                 
1083                 buf = g_strdup_printf(_("Processing (%s)...\n"), item->path);
1084                 debug_print("%s\n", buf);
1085                 g_free(buf);
1086         
1087                 folder_item_apply_processing(item);
1088
1089                 debug_print("done.\n");
1090         }
1091
1092         return 0;
1093 }
1094
1095 gint folder_item_close(FolderItem *item)
1096 {
1097         GSList *mlist, *cur;
1098         Folder *folder;
1099         
1100         g_return_val_if_fail(item != NULL, -1);
1101
1102         if (item->new_msgs) {
1103                 folder_item_update_freeze();
1104                 mlist = folder_item_get_msg_list(item);
1105                 for (cur = mlist ; cur != NULL ; cur = cur->next) {
1106                         MsgInfo * msginfo;
1107
1108                         msginfo = (MsgInfo *) cur->data;
1109                         if (MSG_IS_NEW(msginfo->flags))
1110                                 procmsg_msginfo_unset_flags(msginfo, MSG_NEW, 0);
1111                         procmsg_msginfo_free(msginfo);
1112                 }
1113                 g_slist_free(mlist);
1114                 folder_item_update_thaw();
1115         }               
1116
1117         folder_item_write_cache(item);
1118         
1119         folder_item_update(item, F_ITEM_UPDATE_MSGCNT);
1120
1121         item->opened = FALSE;
1122         folder = item->folder;
1123
1124         if (folder->klass->close == NULL)
1125                 return 0;
1126
1127         return folder->klass->close(folder, item);
1128 }
1129
1130 gint folder_item_scan_full(FolderItem *item, gboolean filtering)
1131 {
1132         Folder *folder;
1133         GSList *folder_list = NULL, *cache_list = NULL;
1134         GSList *folder_list_cur, *cache_list_cur, *new_list = NULL;
1135         GSList *exists_list = NULL, *elem;
1136         GSList *newmsg_list = NULL;
1137         guint newcnt = 0, unreadcnt = 0, totalcnt = 0, unreadmarkedcnt = 0;
1138         guint cache_max_num, folder_max_num, cache_cur_num, folder_cur_num;
1139         gboolean update_flags = 0, old_uids_valid = FALSE;
1140     
1141         g_return_val_if_fail(item != NULL, -1);
1142         if (item->path == NULL) return -1;
1143
1144         folder = item->folder;
1145
1146         g_return_val_if_fail(folder != NULL, -1);
1147         g_return_val_if_fail(folder->klass->get_num_list != NULL, -1);
1148
1149         debug_print("Scanning folder %s for cache changes.\n", item->path);
1150
1151         /* Get list of messages for folder and cache */
1152         if (folder->klass->get_num_list(item->folder, item, &folder_list, &old_uids_valid) < 0) {
1153                 debug_print("Error fetching list of message numbers\n");
1154                 return(-1);
1155         }
1156
1157         if (old_uids_valid) {
1158                 if (!item->cache)
1159                         folder_item_read_cache(item);
1160                 cache_list = msgcache_get_msg_list(item->cache);
1161         } else {
1162                 if (item->cache)
1163                         msgcache_destroy(item->cache);
1164                 item->cache = msgcache_new();
1165                 cache_list = NULL;
1166         }
1167
1168         /* Sort both lists */
1169         cache_list = g_slist_sort(cache_list, folder_sort_cache_list_by_msgnum);
1170         folder_list = g_slist_sort(folder_list, folder_sort_folder_list);
1171
1172         cache_list_cur = cache_list;
1173         folder_list_cur = folder_list;
1174
1175         if (cache_list_cur != NULL) {
1176                 GSList *cache_list_last;
1177         
1178                 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1179                 cache_list_last = g_slist_last(cache_list);
1180                 cache_max_num = ((MsgInfo *)cache_list_last->data)->msgnum;
1181         } else {
1182                 cache_cur_num = G_MAXINT;
1183                 cache_max_num = 0;
1184         }
1185
1186         if (folder_list_cur != NULL) {
1187                 GSList *folder_list_last;
1188         
1189                 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1190                 folder_list_last = g_slist_last(folder_list);
1191                 folder_max_num = GPOINTER_TO_INT(folder_list_last->data);
1192         } else {
1193                 folder_cur_num = G_MAXINT;
1194                 folder_max_num = 0;
1195         }
1196
1197         while ((cache_cur_num != G_MAXINT) || (folder_cur_num != G_MAXINT)) {
1198                 /*
1199                  *  Message only exists in the folder
1200                  *  Remember message for fetching
1201                  */
1202                 if (folder_cur_num < cache_cur_num) {
1203                         gboolean add = FALSE;
1204
1205                         switch(FOLDER_TYPE(folder)) {
1206                                 case F_NEWS:
1207                                         if (folder_cur_num < cache_max_num)
1208                                                 break;
1209                                         
1210                                         if (folder->account->max_articles == 0) {
1211                                                 add = TRUE;
1212                                         }
1213
1214                                         if (folder_max_num <= folder->account->max_articles) {
1215                                                 add = TRUE;
1216                                         } else if (folder_cur_num > (folder_max_num - folder->account->max_articles)) {
1217                                                 add = TRUE;
1218                                         }
1219                                         break;
1220                                 default:
1221                                         add = TRUE;
1222                                         break;
1223                         }
1224                         
1225                         if (add) {
1226                                 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(folder_cur_num));
1227                                 debug_print("Remembered message %d for fetching\n", folder_cur_num);
1228                         }
1229
1230                         /* Move to next folder number */
1231                         folder_list_cur = folder_list_cur->next;
1232
1233                         if (folder_list_cur != NULL)
1234                                 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1235                         else
1236                                 folder_cur_num = G_MAXINT;
1237
1238                         continue;
1239                 }
1240
1241                 /*
1242                  *  Message only exists in the cache
1243                  *  Remove the message from the cache
1244                  */
1245                 if (cache_cur_num < folder_cur_num) {
1246                         msgcache_remove_msg(item->cache, cache_cur_num);
1247                         debug_print("Removed message %d from cache.\n", cache_cur_num);
1248
1249                         /* Move to next cache number */
1250                         cache_list_cur = cache_list_cur->next;
1251
1252                         if (cache_list_cur != NULL)
1253                                 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1254                         else
1255                                 cache_cur_num = G_MAXINT;
1256
1257                         update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
1258
1259                         continue;
1260                 }
1261
1262                 /*
1263                  *  Message number exists in folder and cache!
1264                  *  Check if the message has been modified
1265                  */
1266                 if (cache_cur_num == folder_cur_num) {
1267                         MsgInfo *msginfo;
1268
1269                         msginfo = msgcache_get_msg(item->cache, folder_cur_num);
1270                         if (folder->klass->is_msg_changed && folder->klass->is_msg_changed(folder, item, msginfo)) {
1271                                 msgcache_remove_msg(item->cache, msginfo->msgnum);
1272                                 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(msginfo->msgnum));
1273                                 procmsg_msginfo_free(msginfo);
1274
1275                                 debug_print("Remembering message %d to update...\n", folder_cur_num);
1276                         } else
1277                                 exists_list = g_slist_prepend(exists_list, msginfo);
1278
1279                         /* Move to next folder and cache number */
1280                         cache_list_cur = cache_list_cur->next;
1281                         folder_list_cur = folder_list_cur->next;
1282
1283                         if (cache_list_cur != NULL)
1284                                 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1285                         else
1286                                 cache_cur_num = G_MAXINT;
1287
1288                         if (folder_list_cur != NULL)
1289                                 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1290                         else
1291                                 folder_cur_num = G_MAXINT;
1292
1293                         continue;
1294                 }
1295         }
1296         
1297         for(cache_list_cur = cache_list; cache_list_cur != NULL; cache_list_cur = g_slist_next(cache_list_cur))
1298                 procmsg_msginfo_free((MsgInfo *) cache_list_cur->data);
1299
1300         g_slist_free(cache_list);
1301         g_slist_free(folder_list);
1302
1303         if (new_list != NULL) {
1304                 if (folder->klass->get_msginfos) {
1305                         newmsg_list = folder->klass->get_msginfos(folder, item, new_list);
1306                 } else if (folder->klass->get_msginfo) {
1307                         GSList *elem;
1308         
1309                         for (elem = new_list; elem != NULL; elem = g_slist_next(elem)) {
1310                                 MsgInfo *msginfo;
1311                                 guint num;
1312
1313                                 num = GPOINTER_TO_INT(elem->data);
1314                                 msginfo = folder->klass->get_msginfo(folder, item, num);
1315                                 if (msginfo != NULL) {
1316                                         newmsg_list = g_slist_prepend(newmsg_list, msginfo);
1317                                         debug_print("Added newly found message %d to cache.\n", num);
1318                                 }
1319                         }
1320                 }
1321                 g_slist_free(new_list);
1322         }
1323
1324         folder_item_update_freeze();
1325         if (newmsg_list != NULL) {
1326                 GSList *elem;
1327
1328                 for (elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) {
1329                         MsgInfo *msginfo = (MsgInfo *) elem->data;
1330
1331                         msgcache_add_msg(item->cache, msginfo);
1332                         if ((filtering == TRUE) &&
1333                             (item->stype == F_INBOX) &&
1334                             (item->folder->account != NULL) && 
1335                             (item->folder->account->filter_on_recv) &&
1336                             procmsg_msginfo_filter(msginfo))
1337                                 procmsg_msginfo_free(msginfo);
1338                         else
1339                                 exists_list = g_slist_prepend(exists_list, msginfo);
1340                 }
1341                 g_slist_free(newmsg_list);
1342
1343                 update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
1344         }
1345
1346         for (elem = exists_list; elem != NULL; elem = g_slist_next(elem)) {
1347                 MsgInfo *msginfo;
1348
1349                 msginfo = elem->data;
1350                 if (MSG_IS_IGNORE_THREAD(msginfo->flags) && (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
1351                         procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1352                 if (!MSG_IS_IGNORE_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD)) {
1353                         procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1354                         procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
1355                 }
1356                 if ((item->stype == F_OUTBOX ||
1357                      item->stype == F_QUEUE  ||
1358                      item->stype == F_DRAFT  ||
1359                      item->stype == F_TRASH) &&
1360                     (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
1361                         procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1362                 if (MSG_IS_NEW(msginfo->flags))
1363                         newcnt++;
1364                 if (MSG_IS_UNREAD(msginfo->flags))
1365                         unreadcnt++;
1366                 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
1367                         unreadmarkedcnt++;
1368                 totalcnt++;
1369
1370                 procmsg_msginfo_free(msginfo);
1371         }
1372         g_slist_free(exists_list);
1373
1374         item->new_msgs = newcnt;
1375         item->unread_msgs = unreadcnt;
1376         item->total_msgs = totalcnt;
1377         item->unreadmarked_msgs = unreadmarkedcnt;
1378
1379         update_flags |= F_ITEM_UPDATE_MSGCNT;
1380
1381         folder_item_update(item, update_flags);
1382         folder_item_update_thaw();
1383
1384         return 0;
1385 }
1386
1387 gint folder_item_scan(FolderItem *item)
1388 {
1389         return folder_item_scan_full(item, TRUE);
1390 }
1391
1392 static gboolean folder_scan_all_items_func(GNode *node, gpointer data)
1393 {
1394         FolderItem *item = node->data;
1395
1396         folder_item_scan(item);
1397
1398         return FALSE;
1399 }
1400
1401 void folder_scan_all_items(Folder * folder)
1402 {
1403         g_node_traverse(folder->node, G_PRE_ORDER,
1404                         G_TRAVERSE_ALL, -1, folder_scan_all_items_func, NULL);
1405 }
1406
1407 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
1408                                           gpointer data)
1409 {
1410         folder_item_scan(FOLDER_ITEM(key));
1411 }
1412
1413 void folder_item_scan_foreach(GHashTable *table)
1414 {
1415         g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
1416 }
1417
1418 void folder_count_total_cache_memusage(FolderItem *item, gpointer data)
1419 {
1420         gint *memusage = (gint *)data;
1421
1422         if (item->cache == NULL)
1423                 return;
1424         
1425         *memusage += msgcache_get_memory_usage(item->cache);
1426 }
1427
1428 gint folder_cache_time_compare_func(gconstpointer a, gconstpointer b)
1429 {
1430         FolderItem *fa = (FolderItem *)a;
1431         FolderItem *fb = (FolderItem *)b;
1432         
1433         return (gint) (msgcache_get_last_access_time(fa->cache) - msgcache_get_last_access_time(fb->cache));
1434 }
1435
1436 void folder_find_expired_caches(FolderItem *item, gpointer data)
1437 {
1438         GSList **folder_item_list = (GSList **)data;
1439         gint difftime, expiretime;
1440         
1441         if (item->cache == NULL)
1442                 return;
1443
1444         if (item->opened > 0)
1445                 return;
1446
1447         difftime = (gint) (time(NULL) - msgcache_get_last_access_time(item->cache));
1448         expiretime = prefs_common.cache_min_keep_time * 60;
1449         debug_print("Cache unused time: %d (Expire time: %d)\n", difftime, expiretime);
1450         if (difftime > expiretime) {
1451                 *folder_item_list = g_slist_insert_sorted(*folder_item_list, item, folder_cache_time_compare_func);
1452         }
1453 }
1454
1455 void folder_item_free_cache(FolderItem *item)
1456 {
1457         g_return_if_fail(item != NULL);
1458         
1459         if (item->cache == NULL)
1460                 return;
1461         
1462         if (item->opened > 0)
1463                 return;
1464
1465         folder_item_write_cache(item);
1466         msgcache_destroy(item->cache);
1467         item->cache = NULL;
1468 }
1469
1470 void folder_clean_cache_memory(void)
1471 {
1472         gint memusage = 0;
1473
1474         folder_func_to_all_folders(folder_count_total_cache_memusage, &memusage);       
1475         debug_print("Total cache memory usage: %d\n", memusage);
1476         
1477         if (memusage > (prefs_common.cache_max_mem_usage * 1024)) {
1478                 GSList *folder_item_list = NULL, *listitem;
1479                 
1480                 debug_print("Trying to free cache memory\n");
1481
1482                 folder_func_to_all_folders(folder_find_expired_caches, &folder_item_list);      
1483                 listitem = folder_item_list;
1484                 while((listitem != NULL) && (memusage > (prefs_common.cache_max_mem_usage * 1024))) {
1485                         FolderItem *item = (FolderItem *)(listitem->data);
1486
1487                         debug_print("Freeing cache memory for %s\n", item->path);
1488                         memusage -= msgcache_get_memory_usage(item->cache);
1489                         folder_item_free_cache(item);
1490                         listitem = listitem->next;
1491                 }
1492                 g_slist_free(folder_item_list);
1493         }
1494 }
1495
1496 void folder_item_read_cache(FolderItem *item)
1497 {
1498         gchar *cache_file, *mark_file;
1499         
1500         g_return_if_fail(item != NULL);
1501
1502         cache_file = folder_item_get_cache_file(item);
1503         mark_file = folder_item_get_mark_file(item);
1504         item->cache = msgcache_read_cache(item, cache_file);
1505         if (!item->cache) {
1506                 item->cache = msgcache_new();
1507                 folder_item_scan_full(item, TRUE);
1508         }
1509         msgcache_read_mark(item->cache, mark_file);
1510         g_free(cache_file);
1511         g_free(mark_file);
1512
1513         folder_clean_cache_memory();
1514 }
1515
1516 void folder_item_write_cache(FolderItem *item)
1517 {
1518         gchar *cache_file, *mark_file;
1519         FolderItemPrefs *prefs;
1520         gint filemode = 0;
1521         gchar *id;
1522         
1523         if (!item || !item->path || !item->cache)
1524                 return;
1525
1526         id = folder_item_get_identifier(item);
1527         debug_print("Save cache for folder %s\n", id);
1528         g_free(id);
1529
1530         cache_file = folder_item_get_cache_file(item);
1531         mark_file = folder_item_get_mark_file(item);
1532         if (msgcache_write(cache_file, mark_file, item->cache) < 0) {
1533                 prefs = item->prefs;
1534                 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
1535                         /* for cache file */
1536                         filemode = prefs->folder_chmod;
1537                         if (filemode & S_IRGRP) filemode |= S_IWGRP;
1538                         if (filemode & S_IROTH) filemode |= S_IWOTH;
1539                         chmod(cache_file, filemode);
1540                 }
1541         }
1542
1543         g_free(cache_file);
1544         g_free(mark_file);
1545 }
1546
1547 MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
1548 {
1549         Folder *folder;
1550         MsgInfo *msginfo;
1551         
1552         g_return_val_if_fail(item != NULL, NULL);
1553         
1554         folder = item->folder;
1555         if (!item->cache)
1556                 folder_item_read_cache(item);
1557         
1558         if ((msginfo = msgcache_get_msg(item->cache, num)) != NULL)
1559                 return msginfo;
1560         
1561         g_return_val_if_fail(folder->klass->get_msginfo, NULL);
1562         if ((msginfo = folder->klass->get_msginfo(folder, item, num)) != NULL) {
1563                 msgcache_add_msg(item->cache, msginfo);
1564                 return msginfo;
1565         }
1566         
1567         return NULL;
1568 }
1569
1570 MsgInfo *folder_item_get_msginfo_by_msgid(FolderItem *item, const gchar *msgid)
1571 {
1572         Folder *folder;
1573         MsgInfo *msginfo;
1574         
1575         g_return_val_if_fail(item != NULL, NULL);
1576         
1577         folder = item->folder;
1578         if (!item->cache)
1579                 folder_item_read_cache(item);
1580         
1581         if ((msginfo = msgcache_get_msg_by_id(item->cache, msgid)) != NULL)
1582                 return msginfo;
1583
1584         return NULL;
1585 }
1586
1587 GSList *folder_item_get_msg_list(FolderItem *item)
1588 {
1589         g_return_val_if_fail(item != NULL, NULL);
1590         
1591         if (item->cache == 0)
1592                 folder_item_read_cache(item);
1593
1594         g_return_val_if_fail(item->cache != NULL, NULL);
1595         
1596         return msgcache_get_msg_list(item->cache);
1597 }
1598
1599 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
1600 {
1601         Folder *folder;
1602
1603         g_return_val_if_fail(item != NULL, NULL);
1604
1605         folder = item->folder;
1606
1607         g_return_val_if_fail(folder->klass->fetch_msg != NULL, NULL);
1608
1609         return folder->klass->fetch_msg(folder, item, num);
1610 }
1611
1612 static gint folder_item_get_msg_num_by_file(FolderItem *dest, const gchar *file)
1613 {
1614         static HeaderEntry hentry[] = {{"Message-ID:",  NULL, TRUE},
1615                                        {NULL,           NULL, FALSE}};
1616         FILE *fp;
1617         MsgInfo *msginfo;
1618         gint msgnum = 0;
1619         gchar buf[BUFFSIZE];
1620
1621         if ((fp = fopen(file, "rb")) == NULL)
1622                 return 0;
1623
1624         if ((dest->stype == F_QUEUE) || (dest->stype == F_DRAFT))
1625                 while (fgets(buf, sizeof(buf), fp) != NULL)
1626                         if (buf[0] == '\r' || buf[0] == '\n') break;
1627
1628         procheader_get_header_fields(fp, hentry);
1629         if (hentry[0].body) {
1630                 extract_parenthesis(hentry[0].body, '<', '>');
1631                 remove_space(hentry[0].body);
1632                 if ((msginfo = msgcache_get_msg_by_id(dest->cache, hentry[0].body)) != NULL) {
1633                         msgnum = msginfo->msgnum;
1634                         procmsg_msginfo_free(msginfo);
1635
1636                         debug_print("found message as uid %d\n", msgnum);
1637                 }
1638         }
1639         
1640         g_free(hentry[0].body);
1641         hentry[0].body = NULL;
1642         fclose(fp);
1643
1644         return msgnum;
1645 }
1646
1647 static void copy_msginfo_flags(MsgInfo *source, MsgInfo *dest)
1648 {
1649         MsgPermFlags perm_flags = 0;
1650         MsgTmpFlags tmp_flags = 0;
1651
1652         /* create new flags */
1653         if (source != NULL) {
1654                 /* copy original flags */
1655                 perm_flags = source->flags.perm_flags;
1656                 tmp_flags = source->flags.tmp_flags;
1657         } else {
1658                 perm_flags = dest->flags.perm_flags;
1659                 tmp_flags = dest->flags.tmp_flags;
1660         }
1661
1662         /* remove new, unread and deleted in special folders */
1663         if (dest->folder->stype == F_OUTBOX ||
1664             dest->folder->stype == F_QUEUE  ||
1665             dest->folder->stype == F_DRAFT  ||
1666             dest->folder->stype == F_TRASH)
1667                 perm_flags &= ~(MSG_NEW | MSG_UNREAD | MSG_DELETED);
1668
1669         /* set ignore flag of ignored parent exists */
1670         if (procmsg_msg_has_flagged_parent(dest, MSG_IGNORE_THREAD))
1671                 perm_flags |= MSG_IGNORE_THREAD;
1672
1673         /* Unset tmp flags that should not be copied */
1674         tmp_flags &= ~(MSG_MOVE | MSG_COPY);
1675
1676         /* unset flags that are set but should not */
1677         procmsg_msginfo_unset_flags(dest,
1678                                     dest->flags.perm_flags & ~perm_flags,
1679                                     dest->flags.tmp_flags  & ~tmp_flags);
1680         /* set new flags */
1681         procmsg_msginfo_set_flags(dest,
1682                                   ~dest->flags.perm_flags & perm_flags,
1683                                   ~dest->flags.tmp_flags  & tmp_flags);
1684
1685         folder_item_update(dest->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
1686 }
1687
1688 static void add_msginfo_to_cache(FolderItem *item, MsgInfo *newmsginfo, MsgInfo *flagsource)
1689 {
1690         /* update folder stats */
1691         if (MSG_IS_NEW(newmsginfo->flags))
1692                 item->new_msgs++;
1693         if (MSG_IS_UNREAD(newmsginfo->flags))
1694                 item->unread_msgs++;
1695         if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
1696                 item->unreadmarked_msgs++;
1697         item->total_msgs++;
1698
1699         copy_msginfo_flags(flagsource, newmsginfo);
1700
1701         msgcache_add_msg(item->cache, newmsginfo);
1702 }
1703
1704 static void remove_msginfo_from_cache(FolderItem *item, MsgInfo *msginfo)
1705 {
1706         MsgInfoUpdate msginfo_update;
1707
1708         if (!item->cache)
1709                 folder_item_read_cache(item);
1710
1711         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1712                 msginfo->folder->new_msgs--;
1713         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1714                 msginfo->folder->unread_msgs--;
1715         if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
1716                 msginfo->folder->unreadmarked_msgs--;
1717         msginfo->folder->total_msgs--;
1718
1719         msginfo_update.msginfo = msginfo;
1720         msginfo_update.flags = MSGINFO_UPDATE_DELETED;
1721         hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1722
1723         msgcache_remove_msg(item->cache, msginfo->msgnum);
1724         folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
1725 }
1726
1727 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
1728                          MsgFlags *flags, gboolean remove_source)
1729 {
1730         GSList file_list;
1731         MsgFileInfo fileinfo;
1732
1733         g_return_val_if_fail(dest != NULL, -1);
1734         g_return_val_if_fail(file != NULL, -1);
1735  
1736         fileinfo.msginfo = NULL;
1737         fileinfo.file = (gchar *)file;
1738         fileinfo.flags = flags;
1739         file_list.data = &fileinfo;
1740         file_list.next = NULL;
1741
1742         return folder_item_add_msgs(dest, &file_list, remove_source);
1743 }
1744
1745 gint folder_item_add_msgs(FolderItem *dest, GSList *file_list,
1746                           gboolean remove_source)
1747 {
1748         Folder *folder;
1749         gint ret, num, lastnum = -1;
1750         GSList *file_cur;
1751         GRelation *relation;
1752         MsgFileInfo *fileinfo = NULL;
1753         gboolean folderscan = FALSE;
1754
1755         g_return_val_if_fail(dest != NULL, -1);
1756         g_return_val_if_fail(file_list != NULL, -1);
1757         g_return_val_if_fail(dest->folder != NULL, -1);
1758
1759         folder = dest->folder;
1760
1761         relation = g_relation_new(2);
1762         g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
1763
1764         if (folder->klass->add_msgs != NULL) {
1765                 ret = folder->klass->add_msgs(folder, dest, file_list, relation);
1766                 if (ret < 0) {
1767                         g_relation_destroy(relation);
1768                         return ret;
1769                 }
1770         } else {
1771                 for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
1772                         fileinfo = (MsgFileInfo *) file_cur->data;
1773
1774                         ret = folder->klass->add_msg(folder, dest, fileinfo->file, fileinfo->flags);
1775                         if (ret < 0) {
1776                                 g_relation_destroy(relation);
1777                                 return ret;
1778                         }
1779                         g_relation_insert(relation, fileinfo, GINT_TO_POINTER(ret));
1780                 }
1781         }
1782
1783         for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
1784                 GTuples *tuples;
1785
1786                 fileinfo = (MsgFileInfo *) file_cur->data;
1787                 tuples = g_relation_select(relation, fileinfo, 0);
1788                 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1789                 g_tuples_destroy(tuples);
1790
1791                 if (num >= 0) {
1792                         MsgInfo *newmsginfo;
1793
1794                         if (num == 0) {
1795                                 if (!folderscan) {
1796                                         folder_item_scan_full(dest, FALSE);
1797                                         folderscan = TRUE;
1798                                 }
1799                                 num = folder_item_get_msg_num_by_file(dest, fileinfo->file);
1800                         }
1801
1802                         if (num > lastnum)
1803                                 lastnum = num;
1804
1805                         if (num >= 0 && remove_source) {
1806                                 if (unlink(fileinfo->file) < 0)
1807                                         FILE_OP_ERROR(fileinfo->file, "unlink");
1808                         }
1809
1810                         if (num == 0)
1811                                 continue;
1812
1813                         if (!folderscan && 
1814                             ((newmsginfo = folder->klass->get_msginfo(folder, dest, num)) != NULL)) {
1815                                 add_msginfo_to_cache(dest, newmsginfo, NULL);
1816                                 procmsg_msginfo_free(newmsginfo);
1817                         } else if ((newmsginfo = msgcache_get_msg(dest->cache, num)) != NULL) {
1818                                 /* TODO: set default flags */
1819                                 procmsg_msginfo_free(newmsginfo);
1820                         }
1821                 }
1822         }
1823
1824         g_relation_destroy(relation);
1825
1826         return lastnum;
1827 }
1828
1829 /*
1830 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1831 {
1832         Folder *folder;
1833         gint num;
1834
1835         g_return_val_if_fail(dest != NULL, -1);
1836         g_return_val_if_fail(msginfo != NULL, -1);
1837
1838         folder = dest->folder;
1839         if (dest->last_num < 0) folder->scan(folder, dest);
1840
1841         num = folder->move_msg(folder, dest, msginfo);
1842         if (num > 0) dest->last_num = num;
1843
1844         return num;
1845 }
1846 */
1847                 
1848 FolderItem *folder_item_move_recursive(FolderItem *src, FolderItem *dest) 
1849 {
1850         GSList *mlist;
1851         FolderItem *new_item;
1852         FolderItem *next_item;
1853         GNode *srcnode;
1854         gchar *old_id, *new_id;
1855
1856         mlist = folder_item_get_msg_list(src);
1857
1858         /* move messages */
1859         debug_print("Moving %s to %s\n", src->path, dest->path);
1860         new_item = folder_create_folder(dest, g_basename(src->path));
1861         if (new_item == NULL) {
1862                 printf("Can't create folder\n");
1863                 return NULL;
1864         }
1865         
1866         if (new_item->folder == NULL)
1867                 new_item->folder = dest->folder;
1868
1869         /* move messages */
1870         log_message(_("Moving %s to %s...\n"), 
1871                         src->name, new_item->path);
1872         folder_item_move_msgs(new_item, mlist);
1873         
1874         /*copy prefs*/
1875         folder_item_prefs_copy_prefs(src, new_item);
1876         new_item->collapsed = src->collapsed;
1877         new_item->thread_collapsed = src->thread_collapsed;
1878         new_item->threaded  = src->threaded;
1879         new_item->ret_rcpt  = src->ret_rcpt;
1880         new_item->hide_read_msgs = src->hide_read_msgs;
1881         new_item->sort_key  = src->sort_key;
1882         new_item->sort_type = src->sort_type;
1883
1884         prefs_matcher_write_config();
1885         
1886         /* recurse */
1887         srcnode = src->folder->node;    
1888         srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, src);
1889         srcnode = srcnode->children;
1890         while (srcnode != NULL) {
1891                 if (srcnode && srcnode->data) {
1892                         next_item = (FolderItem*) srcnode->data;
1893                         srcnode = srcnode->next;
1894                         if (folder_item_move_recursive(next_item, new_item) == NULL)
1895                                 return NULL;
1896                 }
1897         }
1898         old_id = folder_item_get_identifier(src);
1899         new_id = folder_item_get_identifier(new_item);
1900         debug_print("updating rules : %s => %s\n", old_id, new_id);
1901         
1902         src->folder->klass->remove_folder(src->folder, src);
1903         folder_write_list();
1904
1905         if (old_id != NULL && new_id != NULL)
1906                 prefs_filtering_rename_path(old_id, new_id);
1907         g_free(old_id);
1908         g_free(new_id);
1909
1910         return new_item;
1911 }
1912
1913 gint folder_item_move_to(FolderItem *src, FolderItem *dest, FolderItem **new_item)
1914 {
1915         FolderItem *tmp = dest->parent;
1916         gchar * src_identifier, * dst_identifier;
1917         gchar * phys_srcpath, * phys_dstpath;
1918         
1919         while (tmp) {
1920                 if (tmp == src) {
1921                         return F_MOVE_FAILED_DEST_IS_CHILD;
1922                 }
1923                 tmp = tmp->parent;
1924         }
1925         
1926         tmp = src->parent;
1927         
1928         src_identifier = folder_item_get_identifier(src);
1929         dst_identifier = folder_item_get_identifier(dest);
1930         
1931         if(dst_identifier == NULL && dest->folder && dest->parent == NULL) {
1932                 /* dest can be a root folder */
1933                 dst_identifier = folder_get_identifier(dest->folder);
1934         }
1935         if (src_identifier == NULL || dst_identifier == NULL) {
1936                 debug_print("Can't get identifiers\n");
1937                 return F_MOVE_FAILED;
1938         }
1939
1940         if (src->folder != dest->folder) {
1941                 return F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX;
1942         }
1943
1944         phys_srcpath = folder_item_get_path(src);
1945         phys_dstpath = g_strconcat(folder_item_get_path(dest),G_DIR_SEPARATOR_S,g_basename(phys_srcpath),NULL);
1946
1947         if (src->parent == dest || src == dest) {
1948                 g_free(src_identifier);
1949                 g_free(dst_identifier);
1950                 g_free(phys_srcpath);
1951                 g_free(phys_dstpath);
1952                 return F_MOVE_FAILED_DEST_IS_PARENT;
1953         }
1954         debug_print("moving \"%s\" to \"%s\"\n", phys_srcpath, phys_dstpath);
1955         if ((tmp = folder_item_move_recursive(src, dest)) == NULL) {
1956                 return F_MOVE_FAILED;
1957         }
1958         
1959         g_free(src_identifier);
1960         g_free(dst_identifier);
1961         g_free(phys_srcpath);
1962         g_free(phys_dstpath);
1963
1964         *new_item = tmp;
1965
1966         return F_MOVE_OK;
1967 }
1968
1969 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1970 {
1971         GSList *list = NULL;
1972         gint ret;
1973
1974         list = g_slist_append(list, msginfo);
1975         ret = folder_item_move_msgs(dest, list);
1976         g_slist_free(list);
1977         
1978         return ret;
1979 }
1980
1981 /*
1982 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
1983 {
1984         Folder *folder;
1985         gint num;
1986
1987         g_return_val_if_fail(dest != NULL, -1);
1988         g_return_val_if_fail(msglist != NULL, -1);
1989
1990         folder = dest->folder;
1991         if (dest->last_num < 0) folder->scan(folder, dest);
1992
1993         num = folder->move_msgs_with_dest(folder, dest, msglist);
1994         if (num > 0) dest->last_num = num;
1995         else dest->op_count = 0;
1996
1997         return num;
1998 }
1999 */
2000
2001 /**
2002  * Copy a list of messages to a new folder.
2003  *
2004  * \param dest Destination folder
2005  * \param msglist List of messages
2006  */
2007 gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
2008 {
2009         Folder *folder;
2010         GSList *l;
2011         gint num, lastnum = -1;
2012         gboolean folderscan = FALSE;
2013         GRelation *relation;
2014
2015         g_return_val_if_fail(dest != NULL, -1);
2016         g_return_val_if_fail(msglist != NULL, -1);
2017
2018         folder = dest->folder;
2019
2020         g_return_val_if_fail(folder->klass->copy_msg != NULL, -1);
2021
2022         relation = g_relation_new(2);
2023         g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
2024
2025         /* 
2026          * Copy messages to destination folder and 
2027          * store new message numbers in newmsgnums
2028          */
2029         if (folder->klass->copy_msgs != NULL) {
2030                 if (folder->klass->copy_msgs(folder, dest, msglist, relation) < 0) {
2031                         g_relation_destroy(relation);
2032                         return -1;
2033                 }
2034         } else {
2035                 for (l = msglist ; l != NULL ; l = g_slist_next(l)) {
2036                         MsgInfo * msginfo = (MsgInfo *) l->data;
2037
2038                         num = folder->klass->copy_msg(folder, dest, msginfo);
2039                         g_relation_insert(relation, msginfo, GINT_TO_POINTER(num));
2040                 }
2041         }
2042
2043         /* Read cache for dest folder */
2044         if (!dest->cache) folder_item_read_cache(dest);
2045
2046         /* 
2047          * Fetch new MsgInfos for new messages in dest folder,
2048          * add them to the msgcache and update folder message counts
2049          */
2050         for (l = msglist; l != NULL; l = g_slist_next(l)) {
2051                 MsgInfo *msginfo = (MsgInfo *) l->data;
2052                 GTuples *tuples;
2053
2054                 tuples = g_relation_select(relation, msginfo, 0);
2055                 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
2056                 g_tuples_destroy(tuples);
2057
2058                 if (num >= 0) {
2059                         MsgInfo *newmsginfo;
2060
2061                         if (num == 0) {
2062                                 gchar *file;
2063
2064                                 if (!folderscan) {
2065                                         folder_item_scan_full(dest, FALSE);
2066                                         folderscan = TRUE;
2067                                 }
2068                                 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
2069                                 num = folder_item_get_msg_num_by_file(dest, file);
2070                                 g_free(file);
2071                         }
2072
2073                         if (num > lastnum)
2074                                 lastnum = num;
2075
2076                         if (num == 0)
2077                                 continue;
2078
2079                         if (!folderscan && 
2080                             ((newmsginfo = folder->klass->get_msginfo(folder, dest, num)) != NULL)) {
2081                                 add_msginfo_to_cache(dest, newmsginfo, msginfo);
2082                                 procmsg_msginfo_free(newmsginfo);
2083                         } else if ((newmsginfo = msgcache_get_msg(dest->cache, num)) != NULL) {
2084                                 copy_msginfo_flags(msginfo, newmsginfo);
2085                                 procmsg_msginfo_free(newmsginfo);
2086                         }
2087                 }
2088         }
2089
2090         /*
2091          * Remove source messages from their folders if
2092          * copying was successfull and update folder
2093          * message counts
2094          */
2095         for (l = msglist; l != NULL; l = g_slist_next(l)) {
2096                 MsgInfo *msginfo = (MsgInfo *) l->data;
2097                 FolderItem *item = msginfo->folder;
2098                 GTuples *tuples;
2099
2100                 tuples = g_relation_select(relation, msginfo, 0);
2101                 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
2102                 g_tuples_destroy(tuples);
2103
2104                 if ((num >= 0) && (item->folder->klass->remove_msg != NULL)) {
2105                         item->folder->klass->remove_msg(item->folder,
2106                                                         msginfo->folder,
2107                                                         msginfo->msgnum);
2108                         remove_msginfo_from_cache(item, msginfo);
2109                 }
2110         }
2111
2112         if (folder->klass->finished_copy)
2113                 folder->klass->finished_copy(folder, dest);
2114
2115         g_relation_destroy(relation);
2116         return lastnum;
2117 }
2118
2119 /*
2120 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
2121 {
2122         Folder *folder;
2123         gint num;
2124
2125         g_return_val_if_fail(dest != NULL, -1);
2126         g_return_val_if_fail(msginfo != NULL, -1);
2127
2128         folder = dest->folder;
2129         if (dest->last_num < 0) folder->scan(folder, dest);
2130
2131         num = folder->copy_msg(folder, dest, msginfo);
2132         if (num > 0) dest->last_num = num;
2133
2134         return num;
2135 }
2136 */
2137
2138 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
2139 {
2140         GSList msglist;
2141
2142         g_return_val_if_fail(dest != NULL, -1);
2143         g_return_val_if_fail(msginfo != NULL, -1);
2144     
2145         msglist.data = msginfo;
2146         msglist.next = NULL;
2147         
2148         return folder_item_copy_msgs(dest, &msglist);
2149 }
2150
2151 /*
2152 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
2153 {
2154         Folder *folder;
2155         gint num;
2156
2157         g_return_val_if_fail(dest != NULL, -1);
2158         g_return_val_if_fail(msglist != NULL, -1);
2159
2160         folder = dest->folder;
2161         if (dest->last_num < 0) folder->scan(folder, dest);
2162
2163         num = folder->copy_msgs_with_dest(folder, dest, msglist);
2164         if (num > 0) dest->last_num = num;
2165         else dest->op_count = 0;
2166
2167         return num;
2168 }
2169 */
2170
2171 gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
2172 {
2173         Folder *folder;
2174         gint num, lastnum = -1;
2175         GSList *l;
2176         gboolean folderscan = FALSE;
2177         GRelation *relation;
2178
2179         g_return_val_if_fail(dest != NULL, -1);
2180         g_return_val_if_fail(msglist != NULL, -1);
2181
2182         folder = dest->folder;
2183  
2184         g_return_val_if_fail(folder->klass->copy_msg != NULL, -1);
2185
2186         relation = g_relation_new(2);
2187         g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
2188
2189         /*
2190          * Copy messages to destination folder and 
2191          * store new message numbers in newmsgnums
2192          */
2193         if (folder->klass->copy_msgs != NULL) {
2194                 if (folder->klass->copy_msgs(folder, dest, msglist, relation) < 0) {
2195                         g_relation_destroy(relation);
2196                         return -1;
2197                 }
2198         } else {
2199                 for (l = msglist ; l != NULL ; l = g_slist_next(l)) {
2200                         MsgInfo * msginfo = (MsgInfo *) l->data;
2201
2202                         num = folder->klass->copy_msg(folder, dest, msginfo);
2203                         g_relation_insert(relation, msginfo, GINT_TO_POINTER(num));
2204                 }
2205         }
2206
2207         /* Read cache for dest folder */
2208         if (!dest->cache) folder_item_read_cache(dest);
2209
2210         /* 
2211          * Fetch new MsgInfos for new messages in dest folder,
2212          * add them to the msgcache and update folder message counts
2213          */
2214         for (l = msglist; l != NULL; l = g_slist_next(l)) {
2215                 MsgInfo *msginfo = (MsgInfo *) l->data;
2216                 GTuples *tuples;
2217
2218                 tuples = g_relation_select(relation, msginfo, 0);
2219                 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
2220                 g_tuples_destroy(tuples);
2221
2222                 if (num >= 0) {
2223                         MsgInfo *newmsginfo;
2224
2225                         if (num == 0) {
2226                                 gchar *file;
2227
2228                                 if (!folderscan) {
2229                                         folder_item_scan_full(dest, FALSE);
2230                                         folderscan = TRUE;
2231                                 }
2232                                 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
2233                                 num = folder_item_get_msg_num_by_file(dest, file);
2234                                 g_free(file);
2235                         }
2236         
2237                         if (num > lastnum)
2238                                 lastnum = num;
2239
2240                         if (num == 0)
2241                                 continue;
2242
2243                         if (!folderscan && 
2244                             ((newmsginfo = folder->klass->get_msginfo(folder, dest, num)) != NULL)) {
2245                                 newmsginfo = folder->klass->get_msginfo(folder, dest, num);
2246                                 add_msginfo_to_cache(dest, newmsginfo, msginfo);
2247                                 procmsg_msginfo_free(newmsginfo);
2248                         } else if ((newmsginfo = msgcache_get_msg(dest->cache, num)) != NULL) {
2249                                 copy_msginfo_flags(msginfo, newmsginfo);
2250                                 procmsg_msginfo_free(newmsginfo);
2251                         }
2252                 }
2253         }
2254         
2255         if (folder->klass->finished_copy)
2256                 folder->klass->finished_copy(folder, dest);
2257
2258         g_relation_destroy(relation);
2259
2260         return lastnum;
2261 }
2262
2263 gint folder_item_remove_msg(FolderItem *item, gint num)
2264 {
2265         Folder *folder;
2266         gint ret;
2267         MsgInfo *msginfo;
2268
2269         g_return_val_if_fail(item != NULL, -1);
2270         folder = item->folder;
2271         g_return_val_if_fail(folder->klass->remove_msg != NULL, -1);
2272
2273         if (!item->cache) folder_item_read_cache(item);
2274
2275         ret = folder->klass->remove_msg(folder, item, num);
2276
2277         msginfo = msgcache_get_msg(item->cache, num);
2278         if (msginfo != NULL) {
2279                 remove_msginfo_from_cache(item, msginfo);
2280                 procmsg_msginfo_free(msginfo);
2281         }
2282         folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
2283
2284         return ret;
2285 }
2286
2287 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
2288 {
2289         Folder *folder;
2290         gint ret = 0;
2291
2292         g_return_val_if_fail(item != NULL, -1);
2293         folder = item->folder;
2294         g_return_val_if_fail(folder != NULL, -1);
2295
2296         if (!item->cache) folder_item_read_cache(item);
2297
2298         while (msglist != NULL) {
2299                 MsgInfo *msginfo = (MsgInfo *)msglist->data;
2300
2301                 ret = folder_item_remove_msg(item, msginfo->msgnum);
2302                 if (ret != 0) break;
2303                 msgcache_remove_msg(item->cache, msginfo->msgnum);
2304                 msglist = msglist->next;
2305         }
2306
2307         return ret;
2308 }
2309
2310 gint folder_item_remove_all_msg(FolderItem *item)
2311 {
2312         Folder *folder;
2313         gint result;
2314
2315         g_return_val_if_fail(item != NULL, -1);
2316
2317         folder = item->folder;
2318
2319         g_return_val_if_fail(folder->klass->remove_all_msg != NULL, -1);
2320
2321         result = folder->klass->remove_all_msg(folder, item);
2322
2323         if (result == 0) {
2324                 if (folder->klass->finished_remove)
2325                         folder->klass->finished_remove(folder, item);
2326
2327                 folder_item_free_cache(item);
2328                 item->cache = msgcache_new();
2329
2330                 item->new_msgs = 0;
2331                 item->unread_msgs = 0;
2332                 item->unreadmarked_msgs = 0;
2333                 item->total_msgs = 0;
2334                 folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
2335         }
2336
2337         return result;
2338 }
2339
2340 void folder_item_change_msg_flags(FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
2341 {
2342         g_return_if_fail(item != NULL);
2343         g_return_if_fail(msginfo != NULL);
2344         
2345         if (item->folder->klass->change_flags != NULL) {
2346                 item->folder->klass->change_flags(item->folder, item, msginfo, newflags);
2347         } else {
2348                 msginfo->flags.perm_flags = newflags;
2349         }
2350 }
2351
2352 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
2353 {
2354         Folder *folder;
2355
2356         g_return_val_if_fail(item != NULL, FALSE);
2357
2358         folder = item->folder;
2359
2360         g_return_val_if_fail(folder->klass->is_msg_changed != NULL, -1);
2361
2362         return folder->klass->is_msg_changed(folder, item, msginfo);
2363 }
2364
2365 gchar *folder_item_get_cache_file(FolderItem *item)
2366 {
2367         gchar *path;
2368         gchar *file;
2369
2370         g_return_val_if_fail(item != NULL, NULL);
2371         g_return_val_if_fail(item->path != NULL, NULL);
2372
2373         path = folder_item_get_path(item);
2374         g_return_val_if_fail(path != NULL, NULL);
2375         if (!is_dir_exist(path))
2376                 make_dir_hier(path);
2377         file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
2378         g_free(path);
2379
2380         return file;
2381 }
2382
2383 gchar *folder_item_get_mark_file(FolderItem *item)
2384 {
2385         gchar *path;
2386         gchar *file;
2387
2388         g_return_val_if_fail(item != NULL, NULL);
2389         g_return_val_if_fail(item->path != NULL, NULL);
2390
2391         path = folder_item_get_path(item);
2392         g_return_val_if_fail(path != NULL, NULL);
2393         if (!is_dir_exist(path))
2394                 make_dir_hier(path);
2395         file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
2396         g_free(path);
2397
2398         return file;
2399 }
2400
2401 static gboolean folder_build_tree(GNode *node, gpointer data)
2402 {
2403         Folder *folder = FOLDER(data);
2404         FolderItem *item;
2405         XMLNode *xmlnode;
2406         GList *list;
2407         SpecialFolderItemType stype = F_NORMAL;
2408         const gchar *name = NULL;
2409         const gchar *path = NULL;
2410         PrefsAccount *account = NULL;
2411         gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE, 
2412                  threaded = TRUE, apply_sub = FALSE;
2413         gboolean ret_rcpt = FALSE, hidereadmsgs = FALSE,
2414                  thread_collapsed = FALSE; /* CLAWS */
2415         FolderSortKey sort_key = SORT_BY_NONE;
2416         FolderSortType sort_type = SORT_ASCENDING;
2417         gint new = 0, unread = 0, total = 0, unreadmarked = 0;
2418         time_t mtime = 0;
2419
2420         g_return_val_if_fail(node->data != NULL, FALSE);
2421         if (!node->parent) return FALSE;
2422
2423         xmlnode = node->data;
2424         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
2425                 g_warning("tag name != \"folderitem\"\n");
2426                 return FALSE;
2427         }
2428
2429         list = xmlnode->tag->attr;
2430         for (; list != NULL; list = list->next) {
2431                 XMLAttr *attr = list->data;
2432
2433                 if (!attr || !attr->name || !attr->value) continue;
2434                 if (!strcmp(attr->name, "type")) {
2435                         if (!strcasecmp(attr->value, "normal"))
2436                                 stype = F_NORMAL;
2437                         else if (!strcasecmp(attr->value, "inbox"))
2438                                 stype = F_INBOX;
2439                         else if (!strcasecmp(attr->value, "outbox"))
2440                                 stype = F_OUTBOX;
2441                         else if (!strcasecmp(attr->value, "draft"))
2442                                 stype = F_DRAFT;
2443                         else if (!strcasecmp(attr->value, "queue"))
2444                                 stype = F_QUEUE;
2445                         else if (!strcasecmp(attr->value, "trash"))
2446                                 stype = F_TRASH;
2447                 } else if (!strcmp(attr->name, "name"))
2448                         name = attr->value;
2449                 else if (!strcmp(attr->name, "path"))
2450                         path = attr->value;
2451                 else if (!strcmp(attr->name, "mtime"))
2452                         mtime = strtoul(attr->value, NULL, 10);
2453                 else if (!strcmp(attr->name, "new"))
2454                         new = atoi(attr->value);
2455                 else if (!strcmp(attr->name, "unread"))
2456                         unread = atoi(attr->value);
2457                 else if (!strcmp(attr->name, "unreadmarked"))
2458                         unreadmarked = atoi(attr->value);
2459                 else if (!strcmp(attr->name, "total"))
2460                         total = atoi(attr->value);
2461                 else if (!strcmp(attr->name, "no_sub"))
2462                         no_sub = *attr->value == '1' ? TRUE : FALSE;
2463                 else if (!strcmp(attr->name, "no_select"))
2464                         no_select = *attr->value == '1' ? TRUE : FALSE;
2465                 else if (!strcmp(attr->name, "collapsed"))
2466                         collapsed = *attr->value == '1' ? TRUE : FALSE;
2467                 else if (!strcmp(attr->name, "thread_collapsed"))
2468                         thread_collapsed =  *attr->value == '1' ? TRUE : FALSE;
2469                 else if (!strcmp(attr->name, "threaded"))
2470                         threaded =  *attr->value == '1' ? TRUE : FALSE;
2471                 else if (!strcmp(attr->name, "hidereadmsgs"))
2472                         hidereadmsgs =  *attr->value == '1' ? TRUE : FALSE;
2473                 else if (!strcmp(attr->name, "reqretrcpt"))
2474                         ret_rcpt =  *attr->value == '1' ? TRUE : FALSE;
2475                 else if (!strcmp(attr->name, "sort_key")) {
2476                         if (!strcmp(attr->value, "none"))
2477                                 sort_key = SORT_BY_NONE;
2478                         else if (!strcmp(attr->value, "number"))
2479                                 sort_key = SORT_BY_NUMBER;
2480                         else if (!strcmp(attr->value, "size"))
2481                                 sort_key = SORT_BY_SIZE;
2482                         else if (!strcmp(attr->value, "date"))
2483                                 sort_key = SORT_BY_DATE;
2484                         else if (!strcmp(attr->value, "from"))
2485                                 sort_key = SORT_BY_FROM;
2486                         else if (!strcmp(attr->value, "subject"))
2487                                 sort_key = SORT_BY_SUBJECT;
2488                         else if (!strcmp(attr->value, "score"))
2489                                 sort_key = SORT_BY_SCORE;
2490                         else if (!strcmp(attr->value, "label"))
2491                                 sort_key = SORT_BY_LABEL;
2492                         else if (!strcmp(attr->value, "mark"))
2493                                 sort_key = SORT_BY_MARK;
2494                         else if (!strcmp(attr->value, "unread"))
2495                                 sort_key = SORT_BY_STATUS;
2496                         else if (!strcmp(attr->value, "mime"))
2497                                 sort_key = SORT_BY_MIME;
2498                         else if (!strcmp(attr->value, "to"))
2499                                 sort_key = SORT_BY_TO;
2500                         else if (!strcmp(attr->value, "locked"))
2501                                 sort_key = SORT_BY_LOCKED;
2502                 } else if (!strcmp(attr->name, "sort_type")) {
2503                         if (!strcmp(attr->value, "ascending"))
2504                                 sort_type = SORT_ASCENDING;
2505                         else
2506                                 sort_type = SORT_DESCENDING;
2507                 } else if (!strcmp(attr->name, "account_id")) {
2508                         account = account_find_from_id(atoi(attr->value));
2509                         if (!account) g_warning("account_id: %s not found\n",
2510                                                 attr->value);
2511                 } else if (!strcmp(attr->name, "apply_sub"))
2512                         apply_sub = *attr->value == '1' ? TRUE : FALSE;
2513         }
2514
2515         item = folder_item_new(folder, name, path);
2516         item->stype = stype;
2517         item->mtime = mtime;
2518         item->new_msgs = new;
2519         item->unread_msgs = unread;
2520         item->unreadmarked_msgs = unreadmarked;
2521         item->total_msgs = total;
2522         item->no_sub = no_sub;
2523         item->no_select = no_select;
2524         item->collapsed = collapsed;
2525         item->thread_collapsed = thread_collapsed;
2526         item->threaded  = threaded;
2527         item->hide_read_msgs  = hidereadmsgs;
2528         item->ret_rcpt  = ret_rcpt;
2529         item->sort_key  = sort_key;
2530         item->sort_type = sort_type;
2531         item->node = node;
2532         item->parent = FOLDER_ITEM(node->parent->data);
2533         item->folder = folder;
2534         switch (stype) {
2535         case F_INBOX:  folder->inbox  = item; break;
2536         case F_OUTBOX: folder->outbox = item; break;
2537         case F_DRAFT:  folder->draft  = item; break;
2538         case F_QUEUE:  folder->queue  = item; break;
2539         case F_TRASH:  folder->trash  = item; break;
2540         default:       break;
2541         }
2542         item->account = account;
2543         item->apply_sub = apply_sub;
2544         folder_item_prefs_read_config(item);
2545
2546         node->data = item;
2547         xml_free_node(xmlnode);
2548
2549         return FALSE;
2550 }
2551
2552 static gboolean folder_read_folder_func(GNode *node, gpointer data)
2553 {
2554         Folder *folder;
2555         FolderItem *item;
2556         XMLNode *xmlnode;
2557         GList *list;
2558         FolderClass *class = NULL;
2559         const gchar *name = NULL;
2560         const gchar *path = NULL;
2561         PrefsAccount *account = NULL;
2562         gboolean collapsed = FALSE, threaded = TRUE, apply_sub = FALSE;
2563         gboolean ret_rcpt = FALSE, thread_collapsed = FALSE; /* CLAWS */
2564
2565         if (g_node_depth(node) != 2) return FALSE;
2566         g_return_val_if_fail(node->data != NULL, FALSE);
2567
2568         xmlnode = node->data;
2569         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
2570                 g_warning("tag name != \"folder\"\n");
2571                 return TRUE;
2572         }
2573         g_node_unlink(node);
2574         list = xmlnode->tag->attr;
2575         for (; list != NULL; list = list->next) {
2576                 XMLAttr *attr = list->data;
2577
2578                 if (!attr || !attr->name || !attr->value) continue;
2579                 if (!strcmp(attr->name, "type"))
2580                         class = folder_get_class_from_string(attr->value);
2581                 else if (!strcmp(attr->name, "name"))
2582                         name = attr->value;
2583                 else if (!strcmp(attr->name, "path"))
2584                         path = attr->value;
2585                 else if (!strcmp(attr->name, "collapsed"))
2586                         collapsed = *attr->value == '1' ? TRUE : FALSE;
2587                 else if (!strcmp(attr->name, "thread_collapsed"))
2588                         thread_collapsed = *attr->value == '1' ? TRUE : FALSE;
2589                 else if (!strcmp(attr->name, "threaded"))
2590                         threaded = *attr->value == '1' ? TRUE : FALSE;
2591                 else if (!strcmp(attr->name, "account_id")) {
2592                         account = account_find_from_id(atoi(attr->value));
2593                         if (!account) g_warning("account_id: %s not found\n",
2594                                                 attr->value);
2595                 } else if (!strcmp(attr->name, "apply_sub"))
2596                         apply_sub = *attr->value == '1' ? TRUE : FALSE;
2597                 else if (!strcmp(attr->name, "reqretrcpt"))
2598                         ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
2599         }
2600
2601         folder = folder_new(class, name, path);
2602         g_return_val_if_fail(folder != NULL, FALSE);
2603         folder->account = account;
2604         if (account != NULL)
2605                 account->folder = REMOTE_FOLDER(folder);
2606         item = FOLDER_ITEM(folder->node->data);
2607         node->data = item;
2608         item->node = node;
2609         g_node_destroy(folder->node);
2610         folder->node = node;
2611         folder_add(folder);
2612         item->collapsed = collapsed;
2613         item->thread_collapsed = thread_collapsed;
2614         item->threaded  = threaded;
2615         item->account   = account;
2616         item->apply_sub = apply_sub;
2617         item->ret_rcpt  = ret_rcpt;
2618
2619         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2620                         folder_build_tree, folder);
2621
2622         return FALSE;
2623 }
2624
2625 static gchar *folder_get_list_path(void)
2626 {
2627         static gchar *filename = NULL;
2628
2629         if (!filename)
2630                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2631                                         FOLDER_LIST, NULL);
2632
2633         return filename;
2634 }
2635
2636 #define PUT_ESCAPE_STR(fp, attr, str)                   \
2637 {                                                       \
2638         fputs(" " attr "=\"", fp);                      \
2639         xml_file_put_escape_str(fp, str);               \
2640         fputs("\"", fp);                                \
2641 }
2642
2643 static void folder_write_list_recursive(GNode *node, gpointer data)
2644 {
2645         FILE *fp = (FILE *)data;
2646         FolderItem *item;
2647         gint i, depth;
2648         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
2649                                                  "draft", "queue", "trash"};
2650         static gchar *sort_key_str[] = {"none", "number", "size", "date",
2651                                         "from", "subject", "score", "label",
2652                                         "mark", "unread", "mime", "to", 
2653                                         "locked"};
2654         g_return_if_fail(node != NULL);
2655         g_return_if_fail(fp != NULL);
2656
2657         item = FOLDER_ITEM(node->data);
2658         g_return_if_fail(item != NULL);
2659
2660         depth = g_node_depth(node);
2661         for (i = 0; i < depth; i++)
2662                 fputs("    ", fp);
2663         if (depth == 1) {
2664                 Folder *folder = item->folder;
2665
2666                 fprintf(fp, "<folder type=\"%s\"", folder->klass->idstr);
2667                 if (folder->name)
2668                         PUT_ESCAPE_STR(fp, "name", folder->name);
2669                 if (FOLDER_TYPE(folder) == F_MH || FOLDER_TYPE(folder) == F_MBOX || FOLDER_TYPE(folder) == F_MAILDIR)
2670                         PUT_ESCAPE_STR(fp, "path",
2671                                        LOCAL_FOLDER(folder)->rootpath);
2672                 if (item->collapsed && node->children)
2673                         fputs(" collapsed=\"1\"", fp);
2674                 if (folder->account)
2675                         fprintf(fp, " account_id=\"%d\"",
2676                                 folder->account->account_id);
2677                 if (item->apply_sub)
2678                         fputs(" apply_sub=\"1\"", fp);
2679                 if (item->ret_rcpt) 
2680                         fputs(" reqretrcpt=\"1\"", fp);
2681         } else {
2682                 fprintf(fp, "<folderitem type=\"%s\"",
2683                         folder_item_stype_str[item->stype]);
2684                 if (item->name)
2685                         PUT_ESCAPE_STR(fp, "name", item->name);
2686                 if (item->path)
2687                         PUT_ESCAPE_STR(fp, "path", item->path);
2688
2689                 if (item->no_sub)
2690                         fputs(" no_sub=\"1\"", fp);
2691                 if (item->no_select)
2692                         fputs(" no_select=\"1\"", fp);
2693                 if (item->collapsed && node->children)
2694                         fputs(" collapsed=\"1\"", fp);
2695                 else
2696                         fputs(" collapsed=\"0\"", fp);
2697                 if (item->thread_collapsed)
2698                         fputs(" thread_collapsed=\"1\"", fp);
2699                 else
2700                         fputs(" thread_collapsed=\"0\"", fp);
2701                 if (item->threaded)
2702                         fputs(" threaded=\"1\"", fp);
2703                 else
2704                         fputs(" threaded=\"0\"", fp);
2705                 if (item->hide_read_msgs)
2706                         fputs(" hidereadmsgs=\"1\"", fp);
2707                 else
2708                         fputs(" hidereadmsgs=\"0\"", fp);
2709                 if (item->ret_rcpt)
2710                         fputs(" reqretrcpt=\"1\"", fp);
2711
2712                 if (item->sort_key != SORT_BY_NONE) {
2713                         fprintf(fp, " sort_key=\"%s\"",
2714                                 sort_key_str[item->sort_key]);
2715                         if (item->sort_type == SORT_ASCENDING)
2716                                 fprintf(fp, " sort_type=\"ascending\"");
2717                         else
2718                                 fprintf(fp, " sort_type=\"descending\"");
2719                 }
2720
2721                 fprintf(fp,
2722                         " mtime=\"%lu\" new=\"%d\" unread=\"%d\" unreadmarked=\"%d\" total=\"%d\"",
2723                         item->mtime, item->new_msgs, item->unread_msgs, item->unreadmarked_msgs, item->total_msgs);
2724
2725                 if (item->account)
2726                         fprintf(fp, " account_id=\"%d\"",
2727                                 item->account->account_id);
2728                 if (item->apply_sub)
2729                         fputs(" apply_sub=\"1\"", fp);
2730         }
2731
2732         if (node->children) {
2733                 GNode *child;
2734                 fputs(">\n", fp);
2735
2736                 child = node->children;
2737                 while (child) {
2738                         GNode *cur;
2739
2740                         cur = child;
2741                         child = cur->next;
2742                         folder_write_list_recursive(cur, data);
2743                 }
2744
2745                 for (i = 0; i < depth; i++)
2746                         fputs("    ", fp);
2747                 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
2748         } else
2749                 fputs(" />\n", fp);
2750 }
2751
2752 static void folder_update_op_count_rec(GNode *node)
2753 {
2754         FolderItem *fitem = FOLDER_ITEM(node->data);
2755
2756         if (g_node_depth(node) > 0) {
2757                 if (fitem->op_count > 0) {
2758                         fitem->op_count = 0;
2759                         folder_item_update(fitem, F_ITEM_UPDATE_MSGCNT);
2760                 }
2761                 if (node->children) {
2762                         GNode *child;
2763
2764                         child = node->children;
2765                         while (child) {
2766                                 GNode *cur;
2767
2768                                 cur = child;
2769                                 child = cur->next;
2770                                 folder_update_op_count_rec(cur);
2771                         }
2772                 }
2773         }
2774 }
2775
2776 void folder_update_op_count(void) 
2777 {
2778         GList *cur;
2779         Folder *folder;
2780
2781         for (cur = folder_list; cur != NULL; cur = cur->next) {
2782                 folder = cur->data;
2783                 folder_update_op_count_rec(folder->node);
2784         }
2785 }
2786
2787 typedef struct _type_str {
2788         gchar * str;
2789         gint type;
2790 } type_str;
2791
2792
2793 /*
2794 static gchar * folder_item_get_tree_identifier(FolderItem * item)
2795 {
2796         if (item->parent != NULL) {
2797                 gchar * path;
2798                 gchar * id;
2799
2800                 path = folder_item_get_tree_identifier(item->parent);
2801                 if (path == NULL)
2802                         return NULL;
2803
2804                 id = g_strconcat(path, "/", item->name, NULL);
2805                 g_free(path);
2806
2807                 return id;
2808         }
2809         else {
2810                 return g_strconcat("/", item->name, NULL);
2811         }
2812 }
2813 */
2814
2815 /* CLAWS: temporary local folder for filtering */
2816 #define TEMP_FOLDER "TEMP_FOLDER"
2817 #define PROCESSING_FOLDER_ITEM "processing"     
2818
2819 static FolderItem *processing_folder_item;
2820
2821 static void folder_create_processing_folder(void)
2822 {
2823         Folder *processing_folder;
2824         gchar      *tmpname;
2825
2826         if ((processing_folder = folder_find_from_name(TEMP_FOLDER, mh_get_class())) == NULL) {
2827                 gchar *tmppath;
2828
2829                 tmppath =
2830                     g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2831                                 "tempfolder", NULL);
2832                 processing_folder =
2833                     folder_new(mh_get_class(), TEMP_FOLDER, tmppath);
2834                 g_free(tmppath);
2835         }
2836         g_assert(processing_folder != NULL);
2837
2838         debug_print("tmpparentroot %s\n", LOCAL_FOLDER(processing_folder)->rootpath);
2839         if (LOCAL_FOLDER(processing_folder)->rootpath[0] == '/')
2840                 tmpname = g_strconcat(LOCAL_FOLDER(processing_folder)->rootpath,
2841                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
2842                                       NULL);
2843         else
2844                 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2845                                       LOCAL_FOLDER(processing_folder)->rootpath,
2846                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
2847                                       NULL);
2848
2849         if (!is_dir_exist(tmpname)) {
2850                 debug_print("*TMP* creating %s\n", tmpname);
2851                 processing_folder_item = processing_folder->klass->create_folder(processing_folder,
2852                                                                                  processing_folder->node->data,
2853                                                                                  PROCESSING_FOLDER_ITEM);
2854         } else {
2855                 debug_print("*TMP* already created\n");
2856                 processing_folder_item = folder_item_new(processing_folder, PROCESSING_FOLDER_ITEM, PROCESSING_FOLDER_ITEM);
2857                 g_assert(processing_folder_item);
2858                 folder_item_append(processing_folder->node->data, processing_folder_item);
2859         }
2860         g_assert(processing_folder_item != NULL);
2861         g_free(tmpname);
2862 }
2863
2864 FolderItem *folder_get_default_processing(void)
2865 {
2866         if (!processing_folder_item) {
2867                 folder_create_processing_folder();
2868         }
2869         return processing_folder_item;
2870 }
2871
2872 /* folder_persist_prefs_new() - return hash table with persistent
2873  * settings (and folder name as key). 
2874  * (note that in claws other options are in the folder_item_prefs_RC
2875  * file, so those don't need to be included in PersistPref yet) 
2876  */
2877 GHashTable *folder_persist_prefs_new(Folder *folder)
2878 {
2879         GHashTable *pptable;
2880
2881         g_return_val_if_fail(folder, NULL);
2882         pptable = g_hash_table_new(g_str_hash, g_str_equal);
2883         folder_get_persist_prefs_recursive(folder->node, pptable);
2884         return pptable;
2885 }
2886
2887 void folder_persist_prefs_free(GHashTable *pptable)
2888 {
2889         g_return_if_fail(pptable);
2890         g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
2891         g_hash_table_destroy(pptable);
2892 }
2893
2894 const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
2895 {
2896         if (pptable == NULL || name == NULL) return NULL;
2897         return g_hash_table_lookup(pptable, name);
2898 }
2899
2900 void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
2901 {
2902         const PersistPrefs *pp;
2903         gchar *id = folder_item_get_identifier(item);
2904
2905         pp = folder_get_persist_prefs(pptable, id); 
2906         g_free(id);
2907
2908         if (!pp) return;
2909
2910         /* CLAWS: since not all folder properties have been migrated to 
2911          * folderlist.xml, we need to call the old stuff first before
2912          * setting things that apply both to Main and Claws. */
2913         folder_item_prefs_read_config(item); 
2914          
2915         item->collapsed = pp->collapsed;
2916         item->thread_collapsed = pp->thread_collapsed;
2917         item->threaded  = pp->threaded;
2918         item->ret_rcpt  = pp->ret_rcpt;
2919         item->hide_read_msgs = pp->hide_read_msgs;
2920         item->sort_key  = pp->sort_key;
2921         item->sort_type = pp->sort_type;
2922 }
2923
2924 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
2925 {
2926         FolderItem *item = FOLDER_ITEM(node->data);
2927         PersistPrefs *pp;
2928         GNode *child, *cur;
2929         gchar *id;
2930
2931         g_return_if_fail(node != NULL);
2932         g_return_if_fail(item != NULL);
2933
2934         /* NOTE: item->path == NULL means top level folder; not interesting
2935          * to store preferences of that one.  */
2936         if (item->path) {
2937                 id = folder_item_get_identifier(item);
2938                 pp = g_new0(PersistPrefs, 1);
2939                 g_return_if_fail(pp != NULL);
2940                 pp->collapsed = item->collapsed;
2941                 pp->thread_collapsed = item->thread_collapsed;
2942                 pp->threaded  = item->threaded;
2943                 pp->ret_rcpt  = item->ret_rcpt; 
2944                 pp->hide_read_msgs = item->hide_read_msgs;
2945                 pp->sort_key  = item->sort_key;
2946                 pp->sort_type = item->sort_type;
2947                 g_hash_table_insert(pptable, id, pp);
2948         }
2949
2950         if (node->children) {
2951                 child = node->children;
2952                 while (child) {
2953                         cur = child;
2954                         child = cur->next;
2955                         folder_get_persist_prefs_recursive(cur, pptable);
2956                 }
2957         }       
2958 }
2959
2960 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
2961 {
2962         if (key) 
2963                 g_free(key);
2964         if (val) 
2965                 g_free(val);
2966         return TRUE;    
2967 }
2968
2969 void folder_item_apply_processing(FolderItem *item)
2970 {
2971         GSList *processing_list;
2972         GSList *mlist, *cur;
2973         
2974         g_return_if_fail(item != NULL);
2975         
2976         processing_list = item->prefs->processing;
2977         if (processing_list == NULL)
2978                 return;
2979
2980         folder_item_update_freeze();
2981
2982         mlist = folder_item_get_msg_list(item);
2983         for (cur = mlist ; cur != NULL ; cur = cur->next) {
2984                 MsgInfo * msginfo;
2985
2986                 msginfo = (MsgInfo *) cur->data;
2987                 filter_message_by_msginfo(processing_list, msginfo);
2988                 procmsg_msginfo_free(msginfo);
2989         }
2990         g_slist_free(mlist);
2991
2992         folder_item_update_thaw();
2993 }
2994
2995 /*
2996  *  functions for handling FolderItem content changes
2997  */
2998 static gint folder_item_update_freeze_cnt = 0;
2999
3000 /**
3001  * Notify the folder system about changes to a folder. If the
3002  * update system is not frozen the FOLDER_ITEM_UPDATE_HOOKLIST will
3003  * be invoked, otherwise the changes will be remebered until
3004  * the folder system is thawed.
3005  *
3006  * \param item The FolderItem that was changed
3007  * \param update_flags Type of changed that was made
3008  */
3009 void folder_item_update(FolderItem *item, FolderItemUpdateFlags update_flags)
3010 {
3011         if (folder_item_update_freeze_cnt == 0) {
3012                 FolderItemUpdateData source;
3013         
3014                 source.item = item;
3015                 source.update_flags = update_flags;
3016                 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
3017         } else {
3018                 item->update_flags |= update_flags;
3019         }
3020 }
3021
3022 void folder_item_update_recursive(FolderItem *item, FolderItemUpdateFlags update_flags)
3023 {
3024         GNode *node = item->folder->node;       
3025
3026         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
3027         node = node->children;
3028
3029         folder_item_update(item, update_flags);
3030         while (node != NULL) {
3031                 if (node && node->data) {
3032                         FolderItem *next_item = (FolderItem*) node->data;
3033
3034                         folder_item_update(next_item, update_flags);
3035                 }
3036                 node = node->next;
3037         }
3038 }
3039
3040 void folder_item_update_freeze(void)
3041 {
3042         folder_item_update_freeze_cnt++;
3043 }
3044
3045 static void folder_item_update_func(FolderItem *item, gpointer data)
3046 {
3047         FolderItemUpdateData source;
3048     
3049         if (item->update_flags) {
3050                 source.item = item;
3051                 source.update_flags = item->update_flags;
3052                 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);                             
3053                 item->update_flags = 0;
3054         }
3055 }
3056
3057 void folder_item_update_thaw(void)
3058 {
3059         if (folder_item_update_freeze_cnt > 0)
3060                 folder_item_update_freeze_cnt--;
3061         if (folder_item_update_freeze_cnt == 0) {
3062                 /* Update all folders */
3063                 folder_func_to_all_folders(folder_item_update_func, NULL);
3064         }
3065 }
3066
3067 #undef PUT_ESCAPE_STR