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