047b1bca12a91165ddb739528a0d78f7d582a8e6
[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 "prefs_folder_item.h"
50 #include "procheader.h"
51
52 static GList *folder_list = NULL;
53
54 static void folder_init         (Folder         *folder,
55                                  const gchar    *name);
56
57 static gboolean folder_read_folder_func (GNode          *node,
58                                          gpointer        data);
59 static gchar *folder_get_list_path      (void);
60 static void folder_write_list_recursive (GNode          *node,
61                                          gpointer        data);
62 static void folder_update_op_count_rec  (GNode          *node);
63
64
65 static void folder_get_persist_prefs_recursive
66                                         (GNode *node, GHashTable *pptable);
67 static gboolean persist_prefs_free      (gpointer key, gpointer val, gpointer data);
68 void folder_item_read_cache             (FolderItem *item);
69 void folder_item_write_cache            (FolderItem *item);
70
71
72 Folder *folder_new(FolderType type, const gchar *name, const gchar *path)
73 {
74         Folder *folder = NULL;
75
76         name = name ? name : path;
77         switch (type) {
78         case F_MBOX:
79                 folder = mbox_folder_new(name, path);
80                 break;
81         case F_MH:
82                 folder = mh_folder_new(name, path);
83                 break;
84         case F_IMAP:
85                 folder = imap_folder_new(name, path);
86                 break;
87         case F_NEWS:
88                 folder = news_folder_new(name, path);
89                 break;
90         default:
91                 return NULL;
92         }
93
94         return folder;
95 }
96
97 static void folder_init(Folder *folder, const gchar *name)
98 {
99         FolderItem *item;
100
101         g_return_if_fail(folder != NULL);
102
103         folder_set_name(folder, name);
104
105         /* Init folder data */
106         folder->type = F_UNKNOWN;
107         folder->account = NULL;
108         folder->inbox = NULL;
109         folder->outbox = NULL;
110         folder->draft = NULL;
111         folder->queue = NULL;
112         folder->trash = NULL;
113
114         /* Init Folder functions */
115         folder->fetch_msg = NULL;
116         folder->fetch_msginfo = NULL;
117         folder->fetch_msginfos = NULL;
118         folder->get_num_list = NULL;
119         folder->ui_func = NULL;
120         folder->ui_func_data = NULL;
121         folder->check_msgnum_validity = NULL;
122
123         /* Create root folder item */
124         item = folder_item_new(name, NULL);
125         item->folder = folder;
126         folder->node = g_node_new(item);
127         folder->data = NULL;
128 }
129
130 void folder_local_folder_init(Folder *folder, const gchar *name,
131                               const gchar *path)
132 {
133         folder_init(folder, name);
134         LOCAL_FOLDER(folder)->rootpath = g_strdup(path);
135 }
136
137 void folder_remote_folder_init(Folder *folder, const gchar *name,
138                                const gchar *path)
139 {
140         folder_init(folder, name);
141         REMOTE_FOLDER(folder)->session = NULL;
142 }
143
144 void folder_destroy(Folder *folder)
145 {
146         g_return_if_fail(folder != NULL);
147
148         switch (folder->type) {
149         case F_MBOX:
150                 mbox_folder_destroy(MBOX_FOLDER(folder));
151         case F_MH:
152                 mh_folder_destroy(MH_FOLDER(folder));
153                 break;
154         case F_IMAP:
155                 imap_folder_destroy(IMAP_FOLDER(folder));
156                 break;
157         case F_NEWS:
158                 news_folder_destroy(NEWS_FOLDER(folder));
159                 break;
160         default:
161                 break;
162         }
163
164         folder_list = g_list_remove(folder_list, folder);
165
166         folder_tree_destroy(folder);
167         g_free(folder->name);
168         g_free(folder);
169 }
170
171 void folder_local_folder_destroy(LocalFolder *lfolder)
172 {
173         g_return_if_fail(lfolder != NULL);
174
175         g_free(lfolder->rootpath);
176 }
177
178 void folder_remote_folder_destroy(RemoteFolder *rfolder)
179 {
180         g_return_if_fail(rfolder != NULL);
181
182         if (rfolder->session)
183                 session_destroy(rfolder->session);
184 }
185
186 #if 0
187 Folder *mbox_folder_new(const gchar *name, const gchar *path)
188 {
189         /* not yet implemented */
190         return NULL;
191 }
192
193 Folder *maildir_folder_new(const gchar *name, const gchar *path)
194 {
195         /* not yet implemented */
196         return NULL;
197 }
198 #endif
199
200 FolderItem *folder_item_new(const gchar *name, const gchar *path)
201 {
202         FolderItem *item;
203
204         item = g_new0(FolderItem, 1);
205
206         item->stype = F_NORMAL;
207         item->name = g_strdup(name);
208         item->path = g_strdup(path);
209         item->account = NULL;
210         item->mtime = 0;
211         item->new = 0;
212         item->unread = 0;
213         item->total = 0;
214         item->last_num = -1;
215         item->cache = NULL;
216         item->no_sub = FALSE;
217         item->no_select = FALSE;
218         item->collapsed = FALSE;
219         item->threaded  = TRUE;
220         item->ret_rcpt  = FALSE;
221         item->opened    = FALSE;
222         item->parent = NULL;
223         item->folder = NULL;
224         item->mark_queue = NULL;
225         item->data = NULL;
226
227         item->prefs = prefs_folder_item_new();
228
229         return item;
230 }
231
232 void folder_item_append(FolderItem *parent, FolderItem *item)
233 {
234         GNode *node;
235
236         g_return_if_fail(parent != NULL);
237         g_return_if_fail(parent->folder != NULL);
238         g_return_if_fail(item != NULL);
239
240         node = parent->folder->node;
241         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, parent);
242         g_return_if_fail(node != NULL);
243
244         item->parent = parent;
245         item->folder = parent->folder;
246         g_node_append_data(node, item);
247 }
248
249 void folder_item_remove(FolderItem *item)
250 {
251         GNode *node;
252
253         g_return_if_fail(item != NULL);
254         g_return_if_fail(item->folder != NULL);
255
256         node = item->folder->node;
257         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
258         g_return_if_fail(node != NULL);
259
260         /* TODO: free all FolderItem's first */
261         if (item->folder->node == node)
262                 item->folder->node = NULL;
263         g_node_destroy(node);
264 }
265
266 void folder_item_destroy(FolderItem *item)
267 {
268         g_return_if_fail(item != NULL);
269
270         g_free(item->name);
271         g_free(item->path);
272         msgcache_destroy(item->cache);
273         g_free(item);
274 }
275
276 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
277 {
278         g_return_if_fail(folder != NULL);
279
280         folder->ui_func = func;
281         folder->ui_func_data = data;
282 }
283
284 void folder_set_name(Folder *folder, const gchar *name)
285 {
286         g_return_if_fail(folder != NULL);
287
288         g_free(folder->name);
289         folder->name = name ? g_strdup(name) : NULL;
290         if (folder->node && folder->node->data) {
291                 FolderItem *item = (FolderItem *)folder->node->data;
292
293                 g_free(item->name);
294                 item->name = name ? g_strdup(name) : NULL;
295         }
296 }
297
298 void folder_tree_destroy(Folder *folder)
299 {
300         /* TODO: destroy all FolderItem before */
301         g_node_destroy(folder->node);
302
303         folder->inbox = NULL;
304         folder->outbox = NULL;
305         folder->draft = NULL;
306         folder->queue = NULL;
307         folder->trash = NULL;
308         folder->node = NULL;
309 }
310
311 void folder_add(Folder *folder)
312 {
313         Folder *cur_folder;
314         GList *cur;
315         gint i;
316
317         g_return_if_fail(folder != NULL);
318
319         for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
320                 cur_folder = FOLDER(cur->data);
321                 if (folder->type == F_MH) {
322                         if (cur_folder->type != F_MH) break;
323                 } else if (folder->type == F_MBOX) {
324                         if (cur_folder->type != F_MH &&
325                             cur_folder->type != F_MBOX) break;
326                 } else if (folder->type == F_IMAP) {
327                         if (cur_folder->type != F_MH &&
328                             cur_folder->type != F_MBOX &&
329                             cur_folder->type != F_IMAP) break;
330                 } else if (folder->type == F_NEWS) {
331                         if (cur_folder->type != F_MH &&
332                             cur_folder->type != F_MBOX &&
333                             cur_folder->type != F_IMAP &&
334                             cur_folder->type != F_NEWS) break;
335                 }
336         }
337
338         folder_list = g_list_insert(folder_list, folder, i);
339 }
340
341 GList *folder_get_list(void)
342 {
343         return folder_list;
344 }
345
346 gint folder_read_list(void)
347 {
348         GNode *node;
349         XMLNode *xmlnode;
350         gchar *path;
351
352         path = folder_get_list_path();
353         if (!is_file_exist(path)) return -1;
354         node = xml_parse_file(path);
355         if (!node) return -1;
356
357         xmlnode = node->data;
358         if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
359                 g_warning("wrong folder list\n");
360                 xml_free_tree(node);
361                 return -1;
362         }
363
364         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
365                         folder_read_folder_func, NULL);
366
367         xml_free_tree(node);
368         if (folder_list)
369                 return 0;
370         else
371                 return -1;
372 }
373
374 void folder_write_list(void)
375 {
376         GList *list;
377         Folder *folder;
378         gchar *path;
379         PrefFile *pfile;
380
381         path = folder_get_list_path();
382         if ((pfile = prefs_write_open(path)) == NULL) return;
383
384         fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
385                 conv_get_current_charset_str());
386         fputs("\n<folderlist>\n", pfile->fp);
387
388         for (list = folder_list; list != NULL; list = list->next) {
389                 folder = list->data;
390                 folder_write_list_recursive(folder->node, pfile->fp);
391         }
392
393         fputs("</folderlist>\n", pfile->fp);
394
395         if (prefs_write_close(pfile) < 0)
396                 g_warning("failed to write folder list.\n");
397 }
398
399 struct TotalMsgCount
400 {
401         guint new;
402         guint unread;
403         guint total;
404 };
405
406 struct FuncToAllFoldersData
407 {
408         FolderItemFunc  function;
409         gpointer        data;
410 };
411
412 static gboolean folder_func_to_all_folders_func(GNode *node, gpointer data)
413 {
414         FolderItem *item;
415         struct FuncToAllFoldersData *function_data = (struct FuncToAllFoldersData *) data;
416
417         g_return_val_if_fail(node->data != NULL, FALSE);
418
419         item = FOLDER_ITEM(node->data);
420         g_return_val_if_fail(item != NULL, FALSE);
421
422         function_data->function(item, function_data->data);
423
424         return FALSE;
425 }
426
427 void folder_func_to_all_folders(FolderItemFunc function, gpointer data)
428 {
429         GList *list;
430         Folder *folder;
431         struct FuncToAllFoldersData function_data;
432         
433         function_data.function = function;
434         function_data.data = data;
435
436         for (list = folder_list; list != NULL; list = list->next) {
437                 folder = FOLDER(list->data);
438                 if (folder->node)
439                         g_node_traverse(folder->node, G_PRE_ORDER,
440                                         G_TRAVERSE_ALL, -1,
441                                         folder_func_to_all_folders_func,
442                                         &function_data);
443         }
444 }
445
446 static void folder_count_total_msgs_func(FolderItem *item, gpointer data)
447 {
448         struct TotalMsgCount *count = (struct TotalMsgCount *)data;
449
450         count->new += item->new;
451         count->unread += item->unread;
452         count->total += item->total;
453 }
454
455 void folder_count_total_msgs(guint *new, guint *unread, guint *total)
456 {
457         struct TotalMsgCount count;
458
459         count.new = count.unread = count.total = 0;
460
461         debug_print(_("Counting total number of messages...\n"));
462
463         folder_func_to_all_folders(folder_count_total_msgs_func, &count);
464
465         *new = count.new;
466         *unread = count.unread;
467         *total = count.total;
468 }
469
470 Folder *folder_find_from_path(const gchar *path)
471 {
472         GList *list;
473         Folder *folder;
474
475         for (list = folder_list; list != NULL; list = list->next) {
476                 folder = list->data;
477                 if ((folder->type == F_MH || folder->type == F_MBOX) &&
478                     !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
479                         return folder;
480         }
481
482         return NULL;
483 }
484
485 Folder *folder_find_from_name(const gchar *name, FolderType type)
486 {
487         GList *list;
488         Folder *folder;
489
490         for (list = folder_list; list != NULL; list = list->next) {
491                 folder = list->data;
492                 if (folder->type == type && strcmp2(name, folder->name) == 0)
493                         return folder;
494         }
495
496         return NULL;
497 }
498
499 static gboolean folder_item_find_func(GNode *node, gpointer data)
500 {
501         FolderItem *item = node->data;
502         gpointer *d = data;
503         const gchar *path = d[0];
504
505         if (path_cmp(path, item->path) != 0)
506                 return FALSE;
507
508         d[1] = item;
509
510         return TRUE;
511 }
512
513 FolderItem *folder_find_item_from_path(const gchar *path)
514 {
515         Folder *folder;
516         gpointer d[2];
517
518         folder = folder_get_default_folder();
519         g_return_val_if_fail(folder != NULL, NULL);
520
521         d[0] = (gpointer)path;
522         d[1] = NULL;
523         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
524                         folder_item_find_func, d);
525         return d[1];
526 }
527
528 static const struct {
529         gchar *str;
530         FolderType type;
531 } type_str_table[] = {
532         {"#mh"     , F_MH},
533         {"#mbox"   , F_MBOX},
534         {"#maildir", F_MAILDIR},
535         {"#imap"   , F_IMAP},
536         {"#news"   , F_NEWS}
537 };
538
539 static gchar *folder_get_type_string(FolderType type)
540 {
541         gint i;
542
543         for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
544              i++) {
545                 if (type_str_table[i].type == type)
546                         return type_str_table[i].str;
547         }
548
549         return NULL;
550 }
551
552 static FolderType folder_get_type_from_string(const gchar *str)
553 {
554         gint i;
555
556         for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
557              i++) {
558                 if (g_strcasecmp(type_str_table[i].str, str) == 0)
559                         return type_str_table[i].type;
560         }
561
562         return F_UNKNOWN;
563 }
564
565 gchar *folder_get_identifier(Folder *folder)
566 {
567         gchar *type_str;
568
569         g_return_val_if_fail(folder != NULL, NULL);
570
571         type_str = folder_get_type_string(folder->type);
572         return g_strconcat(type_str, "/", folder->name, NULL);
573 }
574
575 gchar *folder_item_get_identifier(FolderItem *item)
576 {
577         gchar *id;
578         gchar *folder_id;
579
580         g_return_val_if_fail(item != NULL, NULL);
581         g_return_val_if_fail(item->path != NULL, NULL);
582
583         folder_id = folder_get_identifier(item->folder);
584         id = g_strconcat(folder_id, "/", item->path, NULL);
585         g_free(folder_id);
586
587         return id;
588 }
589
590 FolderItem *folder_find_item_from_identifier(const gchar *identifier)
591 {
592         Folder *folder;
593         gpointer d[2];
594         gchar *str;
595         gchar *p;
596         gchar *name;
597         gchar *path;
598         FolderType type;
599
600         g_return_val_if_fail(identifier != NULL, NULL);
601
602         if (*identifier != '#')
603                 return folder_find_item_from_path(identifier);
604
605         Xstrdup_a(str, identifier, return NULL);
606
607         p = strchr(str, '/');
608         if (!p)
609                 return folder_find_item_from_path(identifier);
610         *p = '\0';
611         p++;
612         type = folder_get_type_from_string(str);
613         if (type == F_UNKNOWN)
614                 return folder_find_item_from_path(identifier);
615
616         name = p;
617         p = strchr(p, '/');
618         if (!p)
619                 return folder_find_item_from_path(identifier);
620         *p = '\0';
621         p++;
622
623         folder = folder_find_from_name(name, type);
624         if (!folder)
625                 return folder_find_item_from_path(identifier);
626
627         path = p;
628
629         d[0] = (gpointer)path;
630         d[1] = NULL;
631         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
632                         folder_item_find_func, d);
633         return d[1];
634 }
635
636 Folder *folder_get_default_folder(void)
637 {
638         return folder_list ? FOLDER(folder_list->data) : NULL;
639 }
640
641 FolderItem *folder_get_default_inbox(void)
642 {
643         Folder *folder;
644
645         if (!folder_list) return NULL;
646         folder = FOLDER(folder_list->data);
647         g_return_val_if_fail(folder != NULL, NULL);
648         return folder->inbox;
649 }
650
651 FolderItem *folder_get_default_outbox(void)
652 {
653         Folder *folder;
654
655         if (!folder_list) return NULL;
656         folder = FOLDER(folder_list->data);
657         g_return_val_if_fail(folder != NULL, NULL);
658         return folder->outbox;
659 }
660
661 FolderItem *folder_get_default_draft(void)
662 {
663         Folder *folder;
664
665         if (!folder_list) return NULL;
666         folder = FOLDER(folder_list->data);
667         g_return_val_if_fail(folder != NULL, NULL);
668         return folder->draft;
669 }
670
671 FolderItem *folder_get_default_queue(void)
672 {
673         Folder *folder;
674
675         if (!folder_list) return NULL;
676         folder = FOLDER(folder_list->data);
677         g_return_val_if_fail(folder != NULL, NULL);
678         return folder->queue;
679 }
680
681 FolderItem *folder_get_default_trash(void)
682 {
683         Folder *folder;
684
685         if (!folder_list) return NULL;
686         folder = FOLDER(folder_list->data);
687         g_return_val_if_fail(folder != NULL, NULL);
688         return folder->trash;
689 }
690
691 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type)   \
692 {                                                       \
693         if (!folder->member) {                          \
694                 item = folder_item_new(dir, dir);       \
695                 item->stype = type;                     \
696                 folder_item_append(rootitem, item);     \
697                 folder->member = item;                  \
698         }                                               \
699 }
700
701 void folder_set_missing_folders(void)
702 {
703         Folder *folder;
704         FolderItem *rootitem;
705         FolderItem *item;
706         GList *list;
707
708         for (list = folder_list; list != NULL; list = list->next) {
709                 folder = list->data;
710                 if (folder->type != F_MH) continue;
711                 rootitem = FOLDER_ITEM(folder->node->data);
712                 g_return_if_fail(rootitem != NULL);
713
714                 if (folder->inbox && folder->outbox && folder->draft &&
715                     folder->queue && folder->trash)
716                         continue;
717
718                 if (folder->create_tree(folder) < 0) {
719                         g_warning("%s: can't create the folder tree.\n",
720                                   LOCAL_FOLDER(folder)->rootpath);
721                         continue;
722                 }
723
724                 CREATE_FOLDER_IF_NOT_EXIST(inbox,  INBOX_DIR,  F_INBOX);
725                 CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
726                 CREATE_FOLDER_IF_NOT_EXIST(draft,  DRAFT_DIR,  F_DRAFT);
727                 CREATE_FOLDER_IF_NOT_EXIST(queue,  QUEUE_DIR,  F_QUEUE);
728                 CREATE_FOLDER_IF_NOT_EXIST(trash,  TRASH_DIR,  F_TRASH);
729         }
730 }
731
732 #undef CREATE_FOLDER_IF_NOT_EXIST
733
734 gchar *folder_item_get_path(FolderItem *item)
735 {
736         gchar *folder_path;
737         gchar *path;
738
739         g_return_val_if_fail(item != NULL, NULL);
740
741         if (FOLDER_TYPE(item->folder) == F_MH)
742                 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
743         else if (FOLDER_TYPE(item->folder) == F_MBOX) {
744                 path = mbox_get_virtual_path(item);
745                 if (path == NULL)
746                         return NULL;
747                 folder_path = g_strconcat(get_mbox_cache_dir(),
748                                           G_DIR_SEPARATOR_S, path, NULL);
749                 g_free(path);
750
751                 return folder_path;
752         }
753         else if (FOLDER_TYPE(item->folder) == F_IMAP) {
754                 g_return_val_if_fail(item->folder->account != NULL, NULL);
755                 folder_path = g_strconcat(get_imap_cache_dir(),
756                                           G_DIR_SEPARATOR_S,
757                                           item->folder->account->recv_server,
758                                           G_DIR_SEPARATOR_S,
759                                           item->folder->account->userid,
760                                           NULL);
761         } else if (FOLDER_TYPE(item->folder) == F_NEWS) {
762                 g_return_val_if_fail(item->folder->account != NULL, NULL);
763                 folder_path = g_strconcat(get_news_cache_dir(),
764                                           G_DIR_SEPARATOR_S,
765                                           item->folder->account->nntp_server,
766                                           NULL);
767         } else
768                 return NULL;
769
770         g_return_val_if_fail(folder_path != NULL, NULL);
771
772         if (folder_path[0] == G_DIR_SEPARATOR) {
773                 if (item->path)
774                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
775                                            item->path, NULL);
776                 else
777                         path = g_strdup(folder_path);
778         } else {
779                 if (item->path)
780                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
781                                            folder_path, G_DIR_SEPARATOR_S,
782                                            item->path, NULL);
783                 else
784                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
785                                            folder_path, NULL);
786         }
787
788         g_free(folder_path);
789         return path;
790 }
791
792 void folder_item_set_default_flags(FolderItem *dest, MsgFlags *flags)
793 {
794         if (!(dest->stype == F_OUTBOX ||
795               dest->stype == F_QUEUE  ||
796               dest->stype == F_DRAFT  ||
797               dest->stype == F_TRASH)) {
798                 flags->perm_flags = MSG_NEW|MSG_UNREAD;
799         } else {
800                 flags->perm_flags = 0;
801         }
802         flags->tmp_flags = MSG_CACHED;
803         if (dest->folder->type == F_MH) {
804                 if (dest->stype == F_QUEUE) {
805                         MSG_SET_TMP_FLAGS(*flags, MSG_QUEUED);
806                 } else if (dest->stype == F_DRAFT) {
807                         MSG_SET_TMP_FLAGS(*flags, MSG_DRAFT);
808                 }
809         } else if (dest->folder->type == F_IMAP) {
810                 MSG_SET_TMP_FLAGS(*flags, MSG_IMAP);
811         } else if (dest->folder->type == F_NEWS) {
812                 MSG_SET_TMP_FLAGS(*flags, MSG_NEWS);
813         }
814 }
815
816 typedef enum {
817     IN_CACHE  = 1 << 0,
818     IN_FOLDER = 1 << 1,
819 } FolderScanInfo;
820
821 gint folder_item_scan(FolderItem *item)
822 {
823         Folder *folder;
824         GSList *folder_list, *cache_list, *elem, *new_list = NULL;
825         gint i;
826         guint min = 0xffffffff, max = 0, cache_max = 0, maxgetcount = 0;
827         FolderScanInfo *folderscaninfo;
828         guint newcnt = 0, unreadcnt = 0, totalcnt = 0;
829         
830         g_return_val_if_fail(item != NULL, -1);
831         if(item->path == NULL) return -1;
832
833         folder = item->folder;
834
835         g_return_val_if_fail(folder != NULL, -1);
836         g_return_val_if_fail(folder->get_num_list != NULL, -1);
837
838         debug_print(_("Scanning folder %s for cache changes.\n"), item->path);
839
840         /* Get list of messages for folder and cache */
841         if(!folder->check_msgnum_validity || 
842            folder->check_msgnum_validity(folder, item)) {
843                 if(!item->cache)
844                         folder_item_read_cache(item);
845                 cache_list = msgcache_get_msg_list(item->cache);
846         } else {
847                 if(item->cache)
848                         msgcache_destroy(item->cache);
849                 item->cache = msgcache_new();
850                 cache_list = NULL;
851         }
852         folder_list = folder->get_num_list(item->folder, item);
853
854         /* Get min und max number in folder */
855         for(elem = cache_list; elem != NULL; elem = elem->next) {
856                 MsgInfo *msginfo = (MsgInfo *)elem->data;
857
858                 min = MIN(msginfo->msgnum, min);
859                 max = MAX(msginfo->msgnum, max);
860         }
861         cache_max = max;
862         for(elem = folder_list; elem != NULL; elem = elem->next) {
863                 guint num = GPOINTER_TO_INT(elem->data);
864
865                 min = MIN(num, min);
866                 max = MAX(num, max);
867         }
868
869         debug_print("Folder message number range from %d to %d\n", min, max);
870
871         if(max == 0) {
872                 for(elem = cache_list; elem != NULL; elem = elem->next) {
873                         MsgInfo *msginfo = (MsgInfo *)elem->data;
874
875                         procmsg_msginfo_free(msginfo);
876                 }
877                 g_slist_free(folder_list);
878                 g_slist_free(cache_list);
879
880                 return 0;
881         }
882
883         folderscaninfo = g_new0(FolderScanInfo, max - min + 1);
884
885         for(elem = folder_list; elem != NULL; elem = elem->next) {
886                 guint num = GPOINTER_TO_INT(elem->data);
887
888                 folderscaninfo[num - min] |= IN_FOLDER;
889         }
890         for(elem = cache_list; elem != NULL; elem = elem->next) {
891                 MsgInfo *msginfo = (MsgInfo *)elem->data;
892
893                 folderscaninfo[msginfo->msgnum - min] |= IN_CACHE;
894                 procmsg_msginfo_free(msginfo);
895         }
896
897         for(i = max - min; i >= 0; i--) {
898                 guint num;
899
900                 num = i + min;
901                 /* Add message to cache if in folder and not in cache */
902                 if( (folderscaninfo[i] & IN_FOLDER) && 
903                    !(folderscaninfo[i] & IN_CACHE) && 
904                     (folder->type != F_NEWS ||
905                         ((prefs_common.max_articles == 0) || (num > (max - prefs_common.max_articles))) &&
906                         (num > cache_max))
907                     ) {
908                         new_list = g_slist_prepend(new_list, GINT_TO_POINTER(num));
909                         debug_print(_("Remembered message %d for fetching\n"), num);
910                 }
911                 /* Remove message from cache if not in folder and in cache */
912                 if(!(folderscaninfo[i] & IN_FOLDER) && 
913                     (folderscaninfo[i] & IN_CACHE)) {
914                         msgcache_remove_msg(item->cache, i + min);
915                         debug_print(_("Removed message %d from cache.\n"), num);
916                 }
917                 /* Check if msginfo needs update if in cache and in folder */
918                 if((folderscaninfo[i] & IN_FOLDER) && 
919                    (folderscaninfo[i] & IN_CACHE) &&
920                    (folder->is_msg_changed != NULL)) {
921                         MsgInfo *msginfo;
922
923                         msginfo = msgcache_get_msg(item->cache, num);
924                         if(folder->is_msg_changed(folder, item, msginfo)) {
925                                 MsgInfo *newmsginfo;
926
927                                 msgcache_remove_msg(item->cache, msginfo->msgnum);
928
929                                 newmsginfo = folder->fetch_msginfo(folder, item, num);
930                                 msgcache_add_msg(item->cache, newmsginfo);
931                                 if(MSG_IS_NEW(newmsginfo->flags) && !MSG_IS_IGNORE_THREAD(newmsginfo->flags))
932                                         newcnt++;
933                                 if(MSG_IS_UNREAD(newmsginfo->flags) && !MSG_IS_IGNORE_THREAD(newmsginfo->flags))
934                                         unreadcnt++;
935                                 procmsg_msginfo_free(newmsginfo);
936
937                                 debug_print(_("Updated msginfo for message %d.\n"), num);
938                         } else {
939                                 if(MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
940                                         newcnt++;
941                                 if(MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
942                                         unreadcnt++;
943                         }
944                         totalcnt++;
945                         procmsg_msginfo_free(msginfo);
946                 }
947         }
948
949         if(folder->fetch_msginfos) {
950                 GSList *newmsg_list;
951                 MsgInfo *msginfo;
952                 
953                 if(new_list) {
954                         newmsg_list = folder->fetch_msginfos(folder, item, new_list);
955                         for(elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) {
956                                 msginfo = (MsgInfo *) elem->data;
957                                 msgcache_add_msg(item->cache, msginfo);
958                                 if(MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
959                                         newcnt++;
960                                 if(MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
961                                         unreadcnt++;
962                                 totalcnt++;
963                                 procmsg_msginfo_free(msginfo);
964                         }
965                         g_slist_free(newmsg_list);
966                         folderview_update_item(item, FALSE);
967                 }
968         } else if (folder->fetch_msginfo) {
969                 for(elem = new_list; elem != NULL; elem = g_slist_next(elem)) {
970                         MsgFlags flags;
971                         MsgInfo *msginfo;
972                         guint num;
973
974                         num = GPOINTER_TO_INT(elem->data);
975                         msginfo = folder->fetch_msginfo(folder, item, num);
976                         if(msginfo != NULL) {
977                                 msgcache_add_msg(item->cache, msginfo);
978                                 if(MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
979                                     newcnt++;
980                                 if(MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
981                                     unreadcnt++;
982                                 totalcnt++;
983                                 procmsg_msginfo_free(msginfo);
984                                 debug_print(_("Added newly found message %d to cache.\n"), num);
985                         }
986                 }
987                 folderview_update_item(item, FALSE);
988         }
989
990         item->new = newcnt;
991         item->unread = unreadcnt;
992         item->total = totalcnt;
993         
994         g_slist_free(folder_list);
995         g_slist_free(cache_list);
996         g_slist_free(new_list);
997         g_free(folderscaninfo);
998
999         return 0;
1000 }
1001
1002 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
1003                                           gpointer data)
1004 {
1005         folder_item_scan(FOLDER_ITEM(key));
1006 }
1007
1008 void folder_item_scan_foreach(GHashTable *table)
1009 {
1010         g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
1011 }
1012
1013 void folder_count_total_cache_memusage(FolderItem *item, gpointer data)
1014 {
1015         gint *memusage = (gint *)data;
1016
1017         if(item->cache == NULL)
1018                 return;
1019         
1020         *memusage += msgcache_get_memory_usage(item->cache);
1021 }
1022
1023 gint folder_cache_time_compare_func(gconstpointer a, gconstpointer b)
1024 {
1025         FolderItem *fa = (FolderItem *)a;
1026         FolderItem *fb = (FolderItem *)b;
1027         
1028         return (gint) (msgcache_get_last_access_time(fa->cache) - msgcache_get_last_access_time(fb->cache));
1029 }
1030
1031 void folder_find_expired_caches(FolderItem *item, gpointer data)
1032 {
1033         GSList **folder_item_list = (GSList **)data;
1034         gint difftime, expiretime;
1035         
1036         if(item->cache == NULL)
1037                 return;
1038
1039         difftime = (gint) (time(NULL) - msgcache_get_last_access_time(item->cache));
1040         expiretime = prefs_common.cache_min_keep_time * 60;
1041         debug_print(_("Cache unused time: %d (Expire time: %d)\n"), difftime, expiretime);
1042         if(difftime > expiretime) {
1043                 *folder_item_list = g_slist_insert_sorted(*folder_item_list, item, folder_cache_time_compare_func);
1044         }
1045 }
1046
1047 void folder_item_free_cache(FolderItem *item)
1048 {
1049         g_return_if_fail(item != NULL);
1050         g_return_if_fail(item->cache != NULL);
1051         
1052         folder_item_write_cache(item);
1053         msgcache_destroy(item->cache);
1054         item->cache = NULL;
1055 }
1056
1057 void folder_clean_cache_memory()
1058 {
1059         gint memusage = 0;
1060
1061         folder_func_to_all_folders(folder_count_total_cache_memusage, &memusage);       
1062         debug_print(_("Total cache memory usage: %d\n"), memusage);
1063         
1064         if(memusage > (prefs_common.cache_max_mem_usage * 1024)) {
1065                 GSList *folder_item_list = NULL, *listitem;
1066                 
1067                 debug_print(_("Trying to free cache memory\n"));
1068
1069                 folder_func_to_all_folders(folder_find_expired_caches, &folder_item_list);      
1070                 listitem = folder_item_list;
1071                 while((listitem != NULL) && (memusage > (prefs_common.cache_max_mem_usage * 1024))) {
1072                         FolderItem *item = (FolderItem *)(listitem->data);
1073
1074                         debug_print(_("Freeing cache memory for %s\n"), item->path);
1075                         memusage -= msgcache_get_memory_usage(item->cache);
1076                         folder_item_free_cache(item);
1077                         listitem = listitem->next;
1078                 }
1079                 g_slist_free(folder_item_list);
1080         }
1081 }
1082
1083 void folder_item_read_cache(FolderItem *item)
1084 {
1085         gchar *cache_file, *mark_file;
1086         
1087         g_return_if_fail(item != NULL);
1088
1089         cache_file = folder_item_get_cache_file(item);
1090         mark_file = folder_item_get_mark_file(item);
1091         item->cache = msgcache_read_cache(item, cache_file);
1092         if(!item->cache) {
1093                 item->cache = msgcache_new();
1094                 folder_item_scan(item);
1095         }
1096         msgcache_read_mark(item->cache, mark_file);
1097         g_free(cache_file);
1098         g_free(mark_file);
1099
1100         folder_clean_cache_memory();
1101 }
1102
1103 void folder_item_write_cache(FolderItem *item)
1104 {
1105         gchar *cache_file, *mark_file;
1106         PrefsFolderItem *prefs;
1107         gint filemode = 0;
1108         gchar *id;
1109         
1110         if (!item || !item->path || !item->cache)
1111                 return;
1112
1113         id = folder_item_get_identifier(item);
1114         debug_print(_("Save cache for folder %s\n"), id);
1115         g_free(id);
1116
1117         cache_file = folder_item_get_cache_file(item);
1118         mark_file = folder_item_get_mark_file(item);
1119         if(msgcache_write(cache_file, mark_file, item->cache) < 0) {
1120                 prefs = item->prefs;
1121                 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
1122                         /* for cache file */
1123                         filemode = prefs->folder_chmod;
1124                         if (filemode & S_IRGRP) filemode |= S_IWGRP;
1125                         if (filemode & S_IROTH) filemode |= S_IWOTH;
1126                         chmod(cache_file, filemode);
1127                 }
1128         }
1129
1130         g_free(cache_file);
1131         g_free(mark_file);
1132 }
1133
1134 MsgInfo *folder_item_fetch_msginfo(FolderItem *item, gint num)
1135 {
1136         Folder *folder;
1137         MsgInfo *msginfo;
1138         
1139         g_return_val_if_fail(item != NULL, NULL);
1140         
1141         folder = item->folder;
1142         if(!item->cache)
1143                 folder_item_read_cache(item);
1144         
1145         if(msginfo = msgcache_get_msg(item->cache, num))
1146                 return msginfo;
1147         
1148         g_return_val_if_fail(folder->fetch_msginfo, NULL);
1149         msginfo = folder->fetch_msginfo(folder, item, num);
1150         return msginfo;
1151 }
1152
1153 GSList *folder_item_get_msg_list(FolderItem *item)
1154 {
1155         g_return_val_if_fail(item != NULL, NULL);
1156         
1157         if(item->cache == 0)
1158                 folder_item_read_cache(item);
1159
1160         g_return_val_if_fail(item->cache != NULL, NULL);
1161         
1162         return msgcache_get_msg_list(item->cache);
1163 }
1164
1165 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
1166 {
1167         Folder *folder;
1168
1169         g_return_val_if_fail(item != NULL, NULL);
1170
1171         folder = item->folder;
1172
1173         g_return_val_if_fail(folder->fetch_msg != NULL, NULL);
1174
1175         return folder->fetch_msg(folder, item, num);
1176 }
1177
1178 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
1179                          gboolean remove_source)
1180 {
1181         Folder *folder;
1182         gint num;
1183         MsgInfo *msginfo;
1184
1185         g_return_val_if_fail(dest != NULL, -1);
1186         g_return_val_if_fail(file != NULL, -1);
1187
1188         folder = dest->folder;
1189
1190         g_return_val_if_fail(folder->add_msg != NULL, -1);
1191
1192         if (!dest->cache)
1193                 folder_item_read_cache(dest);
1194
1195         num = folder->add_msg(folder, dest, file, remove_source);
1196
1197         if (num > 0) {
1198                 msginfo = folder->fetch_msginfo(folder, dest, num);
1199
1200                 if(MSG_IS_NEW(msginfo->flags))
1201                         dest->new++;
1202                 if(MSG_IS_UNREAD(msginfo->flags))
1203                         dest->unread++;
1204                 dest->total++;
1205
1206                 dest->last_num = num;
1207                 msgcache_add_msg(dest->cache, msginfo);
1208                 procmsg_msginfo_free(msginfo);
1209         }
1210
1211         return num;
1212 }
1213
1214 /*
1215 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1216 {
1217         Folder *folder;
1218         gint num;
1219
1220         g_return_val_if_fail(dest != NULL, -1);
1221         g_return_val_if_fail(msginfo != NULL, -1);
1222
1223         folder = dest->folder;
1224         if (dest->last_num < 0) folder->scan(folder, dest);
1225
1226         num = folder->move_msg(folder, dest, msginfo);
1227         if (num > 0) dest->last_num = num;
1228
1229         return num;
1230 }
1231 */
1232
1233 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1234 {
1235         Folder *folder;
1236         gint num;
1237         gchar * filename;
1238         Folder * src_folder;
1239
1240         g_return_val_if_fail(dest != NULL, -1);
1241         g_return_val_if_fail(msginfo != NULL, -1);
1242
1243         folder = dest->folder;
1244
1245         g_return_val_if_fail(folder->remove_msg != NULL, -1);
1246         g_return_val_if_fail(folder->copy_msg != NULL, -1);
1247
1248         if (!dest->cache) folder_item_read_cache(dest);
1249
1250         src_folder = msginfo->folder->folder;
1251
1252         num = folder->copy_msg(folder, dest, msginfo);
1253         
1254         if (num != -1) {
1255                 MsgInfo *newmsginfo;
1256
1257                 newmsginfo = folder->fetch_msginfo(folder, dest, num);
1258                 newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
1259                 if (dest->stype == F_OUTBOX ||
1260                     dest->stype == F_QUEUE  ||
1261                     dest->stype == F_DRAFT  ||
1262                     dest->stype == F_TRASH)
1263                         MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
1264                                              MSG_NEW|MSG_UNREAD|MSG_DELETED);
1265                 msgcache_add_msg(dest->cache, newmsginfo);
1266
1267                 /* CLAWS */
1268                 if(src_folder->remove_msg) {
1269                         src_folder->remove_msg(src_folder,
1270                                                msginfo->folder,
1271                                                msginfo->msgnum);
1272                 }
1273                 msgcache_remove_msg(msginfo->folder->cache, msginfo->msgnum);
1274
1275                 if (MSG_IS_NEW(msginfo->flags))
1276                         msginfo->folder->new--;
1277                 if (MSG_IS_NEW(newmsginfo->flags))
1278                         dest->new++;
1279                 if (MSG_IS_UNREAD(msginfo->flags))
1280                         msginfo->folder->unread--;
1281                 if (MSG_IS_UNREAD(newmsginfo->flags))
1282                         dest->unread++;
1283                 msginfo->folder->total--;
1284                 dest->total++;
1285
1286                 procmsg_msginfo_free(newmsginfo);
1287         }
1288         
1289         if (folder->finished_copy)
1290                 folder->finished_copy(folder, dest);
1291
1292         return num;
1293 }
1294
1295 /*
1296 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
1297 {
1298         Folder *folder;
1299         gint num;
1300
1301         g_return_val_if_fail(dest != NULL, -1);
1302         g_return_val_if_fail(msglist != NULL, -1);
1303
1304         folder = dest->folder;
1305         if (dest->last_num < 0) folder->scan(folder, dest);
1306
1307         num = folder->move_msgs_with_dest(folder, dest, msglist);
1308         if (num > 0) dest->last_num = num;
1309         else dest->op_count = 0;
1310
1311         return num;
1312 }
1313 */
1314
1315 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
1316 {
1317         Folder *folder;
1318         FolderItem * item;
1319         GSList * l;
1320         gchar * filename;
1321         gint num;
1322
1323         g_return_val_if_fail(dest != NULL, -1);
1324         g_return_val_if_fail(msglist != NULL, -1);
1325
1326         folder = dest->folder;
1327
1328         g_return_val_if_fail(folder->copy_msg != NULL, -1);
1329         g_return_val_if_fail(folder->remove_msg != NULL, -1);
1330
1331         if (!dest->cache) folder_item_read_cache(dest);
1332
1333         item = NULL;
1334         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1335                 MsgInfo * msginfo = (MsgInfo *) l->data;
1336
1337                 if (!item && msginfo->folder != NULL)
1338                         item = msginfo->folder;
1339                 if (!item->cache) folder_item_read_cache(dest);
1340
1341                 num = folder->copy_msg(folder, dest, msginfo);
1342                 if (num != -1) {
1343                         MsgInfo *newmsginfo;
1344
1345                         newmsginfo = folder->fetch_msginfo(folder, dest, num);
1346                         if(newmsginfo) {
1347                                 newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
1348                                 if (dest->stype == F_OUTBOX ||
1349                                     dest->stype == F_QUEUE  ||
1350                                     dest->stype == F_DRAFT  ||
1351                                     dest->stype == F_TRASH)
1352                                         MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
1353                                                              MSG_NEW|MSG_UNREAD|MSG_DELETED);
1354                                 msgcache_add_msg(dest->cache, newmsginfo);
1355
1356                                 if (MSG_IS_NEW(msginfo->flags))
1357                                         msginfo->folder->new--;
1358                                 if (MSG_IS_NEW(newmsginfo->flags))
1359                                         dest->new++;
1360                                 if (MSG_IS_UNREAD(msginfo->flags))
1361                                         msginfo->folder->unread--;
1362                                 if (MSG_IS_UNREAD(newmsginfo->flags))
1363                                         dest->unread++;
1364                                 msginfo->folder->total--;
1365                                 dest->total++;
1366
1367                                 procmsg_msginfo_free(newmsginfo);
1368                         }
1369                         item->folder->remove_msg(item->folder,
1370                                                  msginfo->folder,
1371                                                  msginfo->msgnum);
1372                         msgcache_remove_msg(item->cache, msginfo->msgnum);
1373                 }
1374         }
1375
1376         if (folder->finished_copy)
1377                 folder->finished_copy(folder, dest);
1378
1379         return dest->last_num;
1380 }
1381
1382 /*
1383 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
1384 {
1385         Folder *folder;
1386         gint num;
1387
1388         g_return_val_if_fail(dest != NULL, -1);
1389         g_return_val_if_fail(msginfo != NULL, -1);
1390
1391         folder = dest->folder;
1392         if (dest->last_num < 0) folder->scan(folder, dest);
1393
1394         num = folder->copy_msg(folder, dest, msginfo);
1395         if (num > 0) dest->last_num = num;
1396
1397         return num;
1398 }
1399 */
1400
1401 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
1402 {
1403         Folder *folder;
1404         gint num;
1405         gchar * filename;
1406         Folder * src_folder;
1407
1408         g_return_val_if_fail(dest != NULL, -1);
1409         g_return_val_if_fail(msginfo != NULL, -1);
1410
1411         folder = dest->folder;
1412
1413         g_return_val_if_fail(folder->copy_msg != NULL, -1);
1414
1415         if (!dest->cache) folder_item_read_cache(dest);
1416         
1417         num = folder->copy_msg(folder, dest, msginfo);
1418         if (num != -1) {
1419                 MsgInfo *newmsginfo;
1420
1421                 newmsginfo = folder->fetch_msginfo(folder, dest, num);
1422                 newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
1423                 if (dest->stype == F_OUTBOX ||
1424                     dest->stype == F_QUEUE  ||
1425                     dest->stype == F_DRAFT  ||
1426                     dest->stype == F_TRASH)
1427                         MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
1428                                              MSG_NEW|MSG_UNREAD|MSG_DELETED);
1429                 msgcache_add_msg(dest->cache, newmsginfo);
1430
1431                 if (MSG_IS_NEW(newmsginfo->flags))
1432                         dest->new++;
1433                 if (MSG_IS_UNREAD(newmsginfo->flags))
1434                         dest->unread++;
1435                 dest->total++;
1436
1437                 procmsg_msginfo_free(newmsginfo);
1438         }
1439
1440         if (folder->finished_copy)
1441                 folder->finished_copy(folder, dest);
1442
1443         return num;
1444 }
1445
1446 /*
1447 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
1448 {
1449         Folder *folder;
1450         gint num;
1451
1452         g_return_val_if_fail(dest != NULL, -1);
1453         g_return_val_if_fail(msglist != NULL, -1);
1454
1455         folder = dest->folder;
1456         if (dest->last_num < 0) folder->scan(folder, dest);
1457
1458         num = folder->copy_msgs_with_dest(folder, dest, msglist);
1459         if (num > 0) dest->last_num = num;
1460         else dest->op_count = 0;
1461
1462         return num;
1463 }
1464 */
1465
1466 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
1467 {
1468         Folder *folder;
1469         gint num;
1470         GSList * l;
1471         gchar * filename;
1472
1473         g_return_val_if_fail(dest != NULL, -1);
1474         g_return_val_if_fail(msglist != NULL, -1);
1475
1476         folder = dest->folder;
1477  
1478         g_return_val_if_fail(folder->copy_msg != NULL, -1);
1479
1480         if (!dest->cache) folder_item_read_cache(dest);
1481
1482         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1483                 MsgInfo * msginfo = (MsgInfo *) l->data;
1484
1485                 num = folder->copy_msg(folder, dest, msginfo);
1486                 if (num != -1) {
1487                         MsgInfo *newmsginfo;
1488
1489                         newmsginfo = folder->fetch_msginfo(folder, dest, num);
1490                         if(newmsginfo) {
1491                                 newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
1492                                 if (dest->stype == F_OUTBOX ||
1493                                     dest->stype == F_QUEUE  ||
1494                                     dest->stype == F_DRAFT  ||
1495                                     dest->stype == F_TRASH)
1496                                         MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
1497                                                              MSG_NEW|MSG_UNREAD|MSG_DELETED);
1498                                 msgcache_add_msg(dest->cache, newmsginfo);
1499
1500                                 if (MSG_IS_NEW(newmsginfo->flags))
1501                                         dest->new++;
1502                                 if (MSG_IS_UNREAD(newmsginfo->flags))
1503                                         dest->unread++;
1504                                 dest->total++;
1505
1506                                 procmsg_msginfo_free(newmsginfo);
1507                         }
1508                 }
1509         }
1510
1511         if (folder->finished_copy)
1512                 folder->finished_copy(folder, dest);
1513
1514         return dest->last_num;
1515 }
1516
1517 gint folder_item_remove_msg(FolderItem *item, gint num)
1518 {
1519         Folder *folder;
1520         gint ret;
1521         MsgInfo *msginfo;
1522
1523         g_return_val_if_fail(item != NULL, -1);
1524
1525         folder = item->folder;
1526         if (!item->cache) folder_item_read_cache(item);
1527
1528         ret = folder->remove_msg(folder, item, num);
1529
1530         msginfo = msgcache_get_msg(item->cache, num);
1531         if(MSG_IS_NEW(msginfo->flags))
1532                 item->new--;
1533         if(MSG_IS_UNREAD(msginfo->flags))
1534                 item->unread--;
1535         item->total--;
1536         procmsg_msginfo_free(msginfo);
1537         msgcache_remove_msg(item->cache, num);
1538
1539         return ret;
1540 }
1541
1542 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
1543 {
1544         gint ret = 0;
1545
1546         g_return_val_if_fail(item != NULL, -1);
1547
1548         if (!item->cache) folder_item_read_cache(item);
1549
1550         while (msglist != NULL) {
1551                 MsgInfo *msginfo = (MsgInfo *)msglist->data;
1552
1553                 ret = folder_item_remove_msg(item, msginfo->msgnum);
1554                 if (ret != 0) break;
1555                 msgcache_remove_msg(item->cache, msginfo->msgnum);
1556                 msglist = msglist->next;
1557         }
1558
1559         return ret;
1560 }
1561
1562 gint folder_item_remove_all_msg(FolderItem *item)
1563 {
1564         Folder *folder;
1565         gint result;
1566
1567         g_return_val_if_fail(item != NULL, -1);
1568
1569         folder = item->folder;
1570
1571         g_return_val_if_fail(folder->remove_all_msg != NULL, -1);
1572
1573         result = folder->remove_all_msg(folder, item);
1574
1575         if (result == 0) {
1576                 if (folder->finished_remove)
1577                         folder->finished_remove(folder, item);
1578         }
1579
1580         return result;
1581 }
1582
1583 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
1584 {
1585         Folder *folder;
1586
1587         g_return_val_if_fail(item != NULL, FALSE);
1588
1589         folder = item->folder;
1590
1591         g_return_val_if_fail(folder->is_msg_changed != NULL, -1);
1592
1593         return folder->is_msg_changed(folder, item, msginfo);
1594 }
1595
1596 gchar *folder_item_get_cache_file(FolderItem *item)
1597 {
1598         gchar *path;
1599         gchar *file;
1600
1601         g_return_val_if_fail(item != NULL, NULL);
1602         g_return_val_if_fail(item->path != NULL, NULL);
1603
1604         path = folder_item_get_path(item);
1605         g_return_val_if_fail(path != NULL, NULL);
1606         if (!is_dir_exist(path))
1607                 make_dir_hier(path);
1608         file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
1609         g_free(path);
1610
1611         return file;
1612 }
1613
1614 gchar *folder_item_get_mark_file(FolderItem *item)
1615 {
1616         gchar *path;
1617         gchar *file;
1618
1619         g_return_val_if_fail(item != NULL, NULL);
1620         g_return_val_if_fail(item->path != NULL, NULL);
1621
1622         path = folder_item_get_path(item);
1623         g_return_val_if_fail(path != NULL, NULL);
1624         if (!is_dir_exist(path))
1625                 make_dir_hier(path);
1626         file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
1627         g_free(path);
1628
1629         return file;
1630 }
1631
1632 static gboolean folder_build_tree(GNode *node, gpointer data)
1633 {
1634         Folder *folder = FOLDER(data);
1635         FolderItem *item;
1636         XMLNode *xmlnode;
1637         GList *list;
1638         SpecialFolderItemType stype = F_NORMAL;
1639         const gchar *name = NULL;
1640         const gchar *path = NULL;
1641         PrefsAccount *account = NULL;
1642         gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE, 
1643                  threaded = TRUE, ret_rcpt = FALSE, hidereadmsgs = FALSE;
1644         FolderSortKey sort_key = SORT_BY_NONE;
1645         FolderSortType sort_type = SORT_ASCENDING;
1646         gint new = 0, unread = 0, total = 0;
1647         time_t mtime = 0;
1648
1649         g_return_val_if_fail(node->data != NULL, FALSE);
1650         if (!node->parent) return FALSE;
1651
1652         xmlnode = node->data;
1653         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1654                 g_warning("tag name != \"folderitem\"\n");
1655                 return FALSE;
1656         }
1657
1658         list = xmlnode->tag->attr;
1659         for (; list != NULL; list = list->next) {
1660                 XMLAttr *attr = list->data;
1661
1662                 if (!attr || !attr->name || !attr->value) continue;
1663                 if (!strcmp(attr->name, "type")) {
1664                         if (!strcasecmp(attr->value, "normal"))
1665                                 stype = F_NORMAL;
1666                         else if (!strcasecmp(attr->value, "inbox"))
1667                                 stype = F_INBOX;
1668                         else if (!strcasecmp(attr->value, "outbox"))
1669                                 stype = F_OUTBOX;
1670                         else if (!strcasecmp(attr->value, "draft"))
1671                                 stype = F_DRAFT;
1672                         else if (!strcasecmp(attr->value, "queue"))
1673                                 stype = F_QUEUE;
1674                         else if (!strcasecmp(attr->value, "trash"))
1675                                 stype = F_TRASH;
1676                 } else if (!strcmp(attr->name, "name"))
1677                         name = attr->value;
1678                 else if (!strcmp(attr->name, "path"))
1679                         path = attr->value;
1680                 else if (!strcmp(attr->name, "account_id")) {
1681                         account = account_find_from_id(atoi(attr->value));
1682                         if (!account) g_warning("account_id: %s not found\n",
1683                                                 attr->value);
1684                 } else if (!strcmp(attr->name, "mtime"))
1685                         mtime = strtoul(attr->value, NULL, 10);
1686                 else if (!strcmp(attr->name, "new"))
1687                         new = atoi(attr->value);
1688                 else if (!strcmp(attr->name, "unread"))
1689                         unread = atoi(attr->value);
1690                 else if (!strcmp(attr->name, "total"))
1691                         total = atoi(attr->value);
1692                 else if (!strcmp(attr->name, "no_sub"))
1693                         no_sub = *attr->value == '1' ? TRUE : FALSE;
1694                 else if (!strcmp(attr->name, "no_select"))
1695                         no_select = *attr->value == '1' ? TRUE : FALSE;
1696                 else if (!strcmp(attr->name, "collapsed"))
1697                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1698                 else if (!strcmp(attr->name, "threaded"))
1699                         threaded =  *attr->value == '1' ? TRUE : FALSE;
1700                 else if (!strcmp(attr->name, "hidereadmsgs"))
1701                         hidereadmsgs =  *attr->value == '1' ? TRUE : FALSE;
1702                 else if (!strcmp(attr->name, "reqretrcpt"))
1703                         ret_rcpt =  *attr->value == '1' ? TRUE : FALSE;
1704                 else if (!strcmp(attr->name, "sort_key")) {
1705                         if (!strcmp(attr->value, "none"))
1706                                 sort_key = SORT_BY_NONE;
1707                         else if (!strcmp(attr->value, "number"))
1708                                 sort_key = SORT_BY_NUMBER;
1709                         else if (!strcmp(attr->value, "size"))
1710                                 sort_key = SORT_BY_SIZE;
1711                         else if (!strcmp(attr->value, "date"))
1712                                 sort_key = SORT_BY_DATE;
1713                         else if (!strcmp(attr->value, "from"))
1714                                 sort_key = SORT_BY_FROM;
1715                         else if (!strcmp(attr->value, "subject"))
1716                                 sort_key = SORT_BY_SUBJECT;
1717                         else if (!strcmp(attr->value, "score"))
1718                                 sort_key = SORT_BY_SCORE;
1719                         else if (!strcmp(attr->value, "label"))
1720                                 sort_key = SORT_BY_LABEL;
1721                         else if (!strcmp(attr->value, "mark"))
1722                                 sort_key = SORT_BY_MARK;
1723                         else if (!strcmp(attr->value, "unread"))
1724                                 sort_key = SORT_BY_UNREAD;
1725                         else if (!strcmp(attr->value, "mime"))
1726                                 sort_key = SORT_BY_MIME;
1727                         else if (!strcmp(attr->value, "locked"))
1728                                 sort_key = SORT_BY_LOCKED;
1729                 } else if (!strcmp(attr->name, "sort_type")) {
1730                         if (!strcmp(attr->value, "ascending"))
1731                                 sort_type = SORT_ASCENDING;
1732                         else
1733                                 sort_type = SORT_DESCENDING;
1734                 }
1735         }
1736
1737         item = folder_item_new(name, path);
1738         item->stype = stype;
1739         item->account = account;
1740         item->mtime = mtime;
1741         item->new = new;
1742         item->unread = unread;
1743         item->total = total;
1744         item->no_sub = no_sub;
1745         item->no_select = no_select;
1746         item->collapsed = collapsed;
1747         item->threaded  = threaded;
1748         item->hide_read_msgs  = hidereadmsgs;
1749         item->ret_rcpt  = ret_rcpt;
1750         item->sort_key  = sort_key;
1751         item->sort_type = sort_type;
1752         item->parent = FOLDER_ITEM(node->parent->data);
1753         item->folder = folder;
1754         switch (stype) {
1755         case F_INBOX:  folder->inbox  = item; break;
1756         case F_OUTBOX: folder->outbox = item; break;
1757         case F_DRAFT:  folder->draft  = item; break;
1758         case F_QUEUE:  folder->queue  = item; break;
1759         case F_TRASH:  folder->trash  = item; break;
1760         default:       break;
1761         }
1762
1763         prefs_folder_item_read_config(item);
1764
1765         node->data = item;
1766         xml_free_node(xmlnode);
1767
1768         return FALSE;
1769 }
1770
1771 static gboolean folder_read_folder_func(GNode *node, gpointer data)
1772 {
1773         Folder *folder;
1774         XMLNode *xmlnode;
1775         GList *list;
1776         FolderType type = F_UNKNOWN;
1777         const gchar *name = NULL;
1778         const gchar *path = NULL;
1779         PrefsAccount *account = NULL;
1780         gboolean collapsed = FALSE, threaded = TRUE, ret_rcpt = FALSE;
1781
1782         if (g_node_depth(node) != 2) return FALSE;
1783         g_return_val_if_fail(node->data != NULL, FALSE);
1784
1785         xmlnode = node->data;
1786         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1787                 g_warning("tag name != \"folder\"\n");
1788                 return TRUE;
1789         }
1790         g_node_unlink(node);
1791         list = xmlnode->tag->attr;
1792         for (; list != NULL; list = list->next) {
1793                 XMLAttr *attr = list->data;
1794
1795                 if (!attr || !attr->name || !attr->value) continue;
1796                 if (!strcmp(attr->name, "type")) {
1797                         if (!strcasecmp(attr->value, "mh"))
1798                                 type = F_MH;
1799                         else if (!strcasecmp(attr->value, "mbox"))
1800                                 type = F_MBOX;
1801                         else if (!strcasecmp(attr->value, "maildir"))
1802                                 type = F_MAILDIR;
1803                         else if (!strcasecmp(attr->value, "imap"))
1804                                 type = F_IMAP;
1805                         else if (!strcasecmp(attr->value, "news"))
1806                                 type = F_NEWS;
1807                 } else if (!strcmp(attr->name, "name"))
1808                         name = attr->value;
1809                 else if (!strcmp(attr->name, "path"))
1810                         path = attr->value;
1811                 else if (!strcmp(attr->name, "account_id")) {
1812                         account = account_find_from_id(atoi(attr->value));
1813                         if (!account) g_warning("account_id: %s not found\n",
1814                                                 attr->value);
1815                 } else if (!strcmp(attr->name, "collapsed"))
1816                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1817                 else if (!strcmp(attr->name, "threaded"))
1818                         threaded = *attr->value == '1' ? TRUE : FALSE;
1819                 else if (!strcmp(attr->name, "reqretrcpt"))
1820                         ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
1821         }
1822
1823         folder = folder_new(type, name, path);
1824         g_return_val_if_fail(folder != NULL, FALSE);
1825         folder->account = account;
1826         if (account && (type == F_IMAP || type == F_NEWS))
1827                 account->folder = REMOTE_FOLDER(folder);
1828         node->data = folder->node->data;
1829         g_node_destroy(folder->node);
1830         folder->node = node;
1831         folder_add(folder);
1832         FOLDER_ITEM(node->data)->collapsed = collapsed;
1833         FOLDER_ITEM(node->data)->threaded  = threaded;
1834         FOLDER_ITEM(node->data)->ret_rcpt  = ret_rcpt;
1835
1836         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1837                         folder_build_tree, folder);
1838
1839         return FALSE;
1840 }
1841
1842 static gchar *folder_get_list_path(void)
1843 {
1844         static gchar *filename = NULL;
1845
1846         if (!filename)
1847                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1848                                         FOLDER_LIST, NULL);
1849
1850         return filename;
1851 }
1852
1853 static void folder_write_list_recursive(GNode *node, gpointer data)
1854 {
1855         FILE *fp = (FILE *)data;
1856         FolderItem *item = FOLDER_ITEM(node->data);
1857         gint i, depth;
1858         static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1859                                            "news", "unknown"};
1860         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1861                                                  "draft", "queue", "trash"};
1862         static gchar *sort_key_str[] = {"none", "number", "size", "date",
1863                                         "from", "subject", "score", "label",
1864                                         "mark", "unread", "mime", "locked" };
1865
1866         g_return_if_fail(item != NULL);
1867
1868         depth = g_node_depth(node);
1869         for (i = 0; i < depth; i++)
1870                 fputs("    ", fp);
1871         if (depth == 1) {
1872                 Folder *folder = item->folder;
1873
1874                 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
1875                 if (folder->name) {
1876                         fputs(" name=\"", fp);
1877                         xml_file_put_escape_str(fp, folder->name);
1878                         fputs("\"", fp);
1879                 }
1880                 if ((folder->type == F_MH) || (folder->type == F_MBOX)) {
1881                         fputs(" path=\"", fp);
1882                         xml_file_put_escape_str
1883                                 (fp, LOCAL_FOLDER(folder)->rootpath);
1884                         fputs("\"", fp);
1885                 }
1886                 if (folder->account)
1887                         fprintf(fp, " account_id=\"%d\"",
1888                                 folder->account->account_id);
1889                 if (item->collapsed && node->children)
1890                         fputs(" collapsed=\"1\"", fp);
1891                 if (item->ret_rcpt) 
1892                         fputs(" reqretrcpt=\"1\"", fp);
1893         } else {
1894                 fprintf(fp, "<folderitem type=\"%s\"",
1895                         folder_item_stype_str[item->stype]);
1896                 if (item->name) {
1897                         fputs(" name=\"", fp);
1898                         xml_file_put_escape_str(fp, item->name);
1899                         fputs("\"", fp);
1900                 }
1901                 if (item->path) {
1902                         fputs(" path=\"", fp);
1903                         xml_file_put_escape_str(fp, item->path);
1904                         fputs("\"", fp);
1905                 }
1906                 if (item->account)
1907                         fprintf(fp, " account_id=\"%d\"",
1908                                 item->account->account_id);
1909                 if (item->no_sub)
1910                         fputs(" no_sub=\"1\"", fp);
1911                 if (item->no_select)
1912                         fputs(" no_select=\"1\"", fp);
1913                 if (item->collapsed && node->children)
1914                         fputs(" collapsed=\"1\"", fp);
1915                 if (item->threaded)
1916                         fputs(" threaded=\"1\"", fp);
1917                 else
1918                         fputs(" threaded=\"0\"", fp);
1919                 if (item->hide_read_msgs)
1920                         fputs(" hidereadmsgs=\"1\"", fp);
1921                 else
1922                         fputs(" hidereadmsgs=\"0\"", fp);
1923                 if (item->ret_rcpt)
1924                         fputs(" reqretrcpt=\"1\"", fp);
1925
1926                 if (item->sort_key != SORT_BY_NONE) {
1927                         fprintf(fp, " sort_key=\"%s\"",
1928                                 sort_key_str[item->sort_key]);
1929                         if (item->sort_type == SORT_ASCENDING)
1930                                 fprintf(fp, " sort_type=\"ascending\"");
1931                         else
1932                                 fprintf(fp, " sort_type=\"descending\"");
1933                 }
1934
1935                 fprintf(fp,
1936                         " mtime=\"%lu\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1937                         item->mtime, item->new, item->unread, item->total);
1938         }
1939
1940         if (node->children) {
1941                 GNode *child;
1942                 fputs(">\n", fp);
1943
1944                 child = node->children;
1945                 while (child) {
1946                         GNode *cur;
1947
1948                         cur = child;
1949                         child = cur->next;
1950                         folder_write_list_recursive(cur, data);
1951                 }
1952
1953                 for (i = 0; i < depth; i++)
1954                         fputs("    ", fp);
1955                 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1956         } else
1957                 fputs(" />\n", fp);
1958 }
1959
1960 static void folder_update_op_count_rec(GNode *node) {
1961         FolderItem *fitem = FOLDER_ITEM(node->data);
1962
1963         if (g_node_depth(node) > 0) {
1964                 if (fitem->op_count > 0) {
1965                         fitem->op_count = 0;
1966                         folderview_update_item(fitem, 0);
1967                 }
1968                 if (node->children) {
1969                         GNode *child;
1970
1971                         child = node->children;
1972                         while (child) {
1973                                 GNode *cur;
1974
1975                                 cur = child;
1976                                 child = cur->next;
1977                                 folder_update_op_count_rec(cur);
1978                         }
1979                 }
1980         }
1981 }
1982
1983 void folder_update_op_count() {
1984         GList *cur;
1985         Folder *folder;
1986
1987         for (cur = folder_list; cur != NULL; cur = cur->next) {
1988                 folder = cur->data;
1989                 folder_update_op_count_rec(folder->node);
1990         }
1991 }
1992
1993 typedef struct _type_str {
1994         gchar * str;
1995         gint type;
1996 } type_str;
1997
1998
1999 /*
2000 static gchar * folder_item_get_tree_identifier(FolderItem * item)
2001 {
2002         if (item->parent != NULL) {
2003                 gchar * path;
2004                 gchar * id;
2005
2006                 path = folder_item_get_tree_identifier(item->parent);
2007                 if (path == NULL)
2008                         return NULL;
2009
2010                 id = g_strconcat(path, "/", item->name, NULL);
2011                 g_free(path);
2012
2013                 return id;
2014         }
2015         else {
2016                 return g_strconcat("/", item->name, NULL);
2017         }
2018 }
2019 */
2020
2021 /* CLAWS: temporary local folder for filtering */
2022 static Folder *processing_folder;
2023 static FolderItem *processing_folder_item;
2024
2025 static void folder_create_processing_folder(void)
2026 {
2027 #define PROCESSING_FOLDER ".processing" 
2028         Folder     *tmpparent;
2029         FolderItem *tmpfolder;
2030         gchar      *tmpname;
2031
2032         tmpparent = folder_get_default_folder();
2033         g_assert(tmpparent);
2034         debug_print("tmpparentroot %s\n", LOCAL_FOLDER(tmpparent)->rootpath);
2035         if (LOCAL_FOLDER(tmpparent)->rootpath[0] == '/')
2036                 tmpname = g_strconcat(LOCAL_FOLDER(tmpparent)->rootpath,
2037                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
2038                                       NULL);
2039         else
2040                 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2041                                       LOCAL_FOLDER(tmpparent)->rootpath,
2042                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
2043                                       NULL);
2044
2045         processing_folder = folder_new(F_MH, "PROCESSING", LOCAL_FOLDER(tmpparent)->rootpath);
2046         g_assert(processing_folder);
2047
2048         if (!is_dir_exist(tmpname)) {
2049                 debug_print("*TMP* creating %s\n", tmpname);
2050                 processing_folder_item = processing_folder->create_folder(processing_folder,
2051                                                                           processing_folder->node->data,
2052                                                                           PROCESSING_FOLDER);
2053                 g_assert(processing_folder_item);                                                                         
2054         }
2055         else {
2056                 debug_print("*TMP* already created\n");
2057                 processing_folder_item = folder_item_new(".processing", ".processing");
2058                 g_assert(processing_folder_item);
2059                 folder_item_append(processing_folder->node->data, processing_folder_item);
2060         }
2061         g_free(tmpname);
2062 }
2063
2064 FolderItem *folder_get_default_processing(void)
2065 {
2066         if (!processing_folder_item) {
2067                 folder_create_processing_folder();
2068         }
2069         return processing_folder_item;
2070 }
2071
2072 /* folder_persist_prefs_new() - return hash table with persistent
2073  * settings (and folder name as key). 
2074  * (note that in claws other options are in the PREFS_FOLDER_ITEM_RC
2075  * file, so those don't need to be included in PersistPref yet) 
2076  */
2077 GHashTable *folder_persist_prefs_new(Folder *folder)
2078 {
2079         GHashTable *pptable;
2080
2081         g_return_val_if_fail(folder, NULL);
2082         pptable = g_hash_table_new(g_str_hash, g_str_equal);
2083         folder_get_persist_prefs_recursive(folder->node, pptable);
2084         return pptable;
2085 }
2086
2087 void folder_persist_prefs_free(GHashTable *pptable)
2088 {
2089         g_return_if_fail(pptable);
2090         g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
2091         g_hash_table_destroy(pptable);
2092 }
2093
2094 const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
2095 {
2096         if (pptable == NULL || name == NULL) return NULL;
2097         return g_hash_table_lookup(pptable, name);
2098 }
2099
2100 void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
2101 {
2102         const PersistPrefs *pp;
2103
2104         pp = folder_get_persist_prefs(pptable, item->path); 
2105         if (!pp) return;
2106
2107         /* CLAWS: since not all folder properties have been migrated to 
2108          * folderlist.xml, we need to call the old stuff first before
2109          * setting things that apply both to Main and Claws. */
2110         prefs_folder_item_read_config(item); 
2111          
2112         item->collapsed = pp->collapsed;
2113         item->threaded  = pp->threaded;
2114         item->ret_rcpt  = pp->ret_rcpt;
2115         item->hide_read_msgs = pp->hide_read_msgs;
2116         item->sort_key  = pp->sort_key;
2117         item->sort_type = pp->sort_type;
2118 }
2119
2120 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
2121 {
2122         FolderItem *item = FOLDER_ITEM(node->data);
2123         PersistPrefs *pp;
2124         GNode *child, *cur;
2125
2126         g_return_if_fail(node != NULL);
2127         g_return_if_fail(item != NULL);
2128
2129         /* FIXME: item->path == NULL for top level folder, so this means that 
2130          * properties of MH folder root will not be stored. Not quite important, 
2131          * because the top level folder properties are not special anyway. */
2132         if (item->path) {
2133                 pp = g_new0(PersistPrefs, 1);
2134                 g_return_if_fail(pp != NULL);
2135                 pp->collapsed = item->collapsed;
2136                 pp->threaded  = item->threaded;
2137                 pp->ret_rcpt  = item->ret_rcpt; 
2138                 pp->hide_read_msgs = item->hide_read_msgs;
2139                 pp->sort_key  = item->sort_key;
2140                 pp->sort_type = item->sort_type;
2141                 g_hash_table_insert(pptable, item->path, pp);
2142         }               
2143
2144         if (node->children) {
2145                 child = node->children;
2146                 while (child) {
2147                         cur = child;
2148                         child = cur->next;
2149                         folder_get_persist_prefs_recursive(cur, pptable);
2150                 }
2151         }       
2152 }
2153
2154 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
2155 {
2156         if (val) 
2157                 g_free(val);
2158         return TRUE;    
2159 }
2160