554ac6e4ed5f70cdb5a74fdc1c9ce2b7fad2fee6
[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
829         g_return_val_if_fail(item != NULL, -1);
830         if(item->path == NULL) return -1;
831
832         folder = item->folder;
833
834         g_return_val_if_fail(folder != NULL, -1);
835         g_return_val_if_fail(folder->get_num_list != NULL, -1);
836
837         debug_print(_("Scanning folder %s for cache changes.\n"), item->path);
838
839         /* Get list of messages for folder and cache */
840         if(!folder->check_msgnum_validity || 
841            folder->check_msgnum_validity(folder, item)) {
842                 if(!item->cache)
843                         folder_item_read_cache(item);
844                 cache_list = msgcache_get_msg_list(item->cache);
845         } else {
846                 if(item->cache)
847                         msgcache_destroy(item->cache);
848                 item->cache = msgcache_new();
849                 cache_list = NULL;
850         }
851         folder_list = folder->get_num_list(item->folder, item);
852
853         /* Get min und max number in folder */
854         for(elem = cache_list; elem != NULL; elem = elem->next) {
855                 MsgInfo *msginfo = (MsgInfo *)elem->data;
856
857                 min = MIN(msginfo->msgnum, min);
858                 max = MAX(msginfo->msgnum, max);
859         }
860         cache_max = max;
861         for(elem = folder_list; elem != NULL; elem = elem->next) {
862                 guint num = GPOINTER_TO_INT(elem->data);
863
864                 min = MIN(num, min);
865                 max = MAX(num, max);
866         }
867
868         debug_print("Folder message number range from %d to %d\n", min, max);
869
870         if(max == 0) {
871                 for(elem = cache_list; elem != NULL; elem = elem->next) {
872                         MsgInfo *msginfo = (MsgInfo *)elem->data;
873
874                         procmsg_msginfo_free(msginfo);
875                 }
876                 g_slist_free(folder_list);
877                 g_slist_free(cache_list);
878
879                 return 0;
880         }
881
882         folderscaninfo = g_new0(FolderScanInfo, max - min + 1);
883
884         for(elem = folder_list; elem != NULL; elem = elem->next) {
885                 guint num = GPOINTER_TO_INT(elem->data);
886
887                 folderscaninfo[num - min] |= IN_FOLDER;
888         }
889         for(elem = cache_list; elem != NULL; elem = elem->next) {
890                 MsgInfo *msginfo = (MsgInfo *)elem->data;
891
892                 folderscaninfo[msginfo->msgnum - min] |= IN_CACHE;
893                 procmsg_msginfo_free(msginfo);
894         }
895
896         for(i = max - min; i >= 0; i--) {
897                 guint num;
898
899                 num = i + min;
900                 /* Add message to cache if in folder and not in cache */
901                 if( (folderscaninfo[i] & IN_FOLDER) && 
902                    !(folderscaninfo[i] & IN_CACHE) && 
903                     (folder->type != F_NEWS ||
904                         ((prefs_common.max_articles == 0) || (num > (max - prefs_common.max_articles))) &&
905                         (num > cache_max))
906                     ) {
907                         new_list = g_slist_prepend(new_list, GINT_TO_POINTER(num));
908                         debug_print(_("Remembered message %d for fetching\n"), num);
909                 }
910                 /* Remove message from cache if not in folder and in cache */
911                 if(!(folderscaninfo[i] & IN_FOLDER) && 
912                     (folderscaninfo[i] & IN_CACHE)) {
913                         msgcache_remove_msg(item->cache, i + min);
914                         debug_print(_("Removed message %d from cache.\n"), num);
915                 }
916                 /* Check if msginfo needs update if in cache and in folder */
917                 if((folderscaninfo[i] & IN_FOLDER) && 
918                    (folderscaninfo[i] & IN_CACHE) &&
919                    (folder->is_msg_changed != NULL)) {
920                         MsgInfo *msginfo;
921
922                         msginfo = msgcache_get_msg(item->cache, num);
923                         if(folder->is_msg_changed(folder, item, msginfo)) {
924                                 MsgInfo *newmsginfo;
925
926                                 msgcache_remove_msg(item->cache, msginfo->msgnum);
927
928                                 newmsginfo = folder->fetch_msginfo(folder, item, num);
929                                 msgcache_add_msg(item->cache, newmsginfo);
930                                 procmsg_msginfo_free(newmsginfo);
931
932                                 debug_print(_("Updated msginfo for message %d.\n"), num);
933                         }
934                         procmsg_msginfo_free(msginfo);
935                 }
936         }
937
938         if(folder->fetch_msginfos) {
939                 GSList *newmsg_list;
940                 MsgInfo *msginfo;
941                 
942                 if(new_list) {
943                         newmsg_list = folder->fetch_msginfos(folder, item, new_list);
944                         for(elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) {
945                                 msginfo = (MsgInfo *) elem->data;
946                                 msgcache_add_msg(item->cache, msginfo);
947                                 if(MSG_IS_NEW(msginfo->flags))
948                                         item->new++;
949                                 if(MSG_IS_UNREAD(msginfo->flags))
950                                         item->unread++;
951                                 item->total++;
952                                 procmsg_msginfo_free(msginfo);
953                         }
954                         g_slist_free(newmsg_list);
955                         folderview_update_item(item, FALSE);
956                 }
957         } else if (folder->fetch_msginfo) {
958                 for(elem = new_list; elem != NULL; elem = g_slist_next(elem)) {
959                         MsgFlags flags;
960                         MsgInfo *msginfo;
961                         guint num;
962
963                         num = GPOINTER_TO_INT(elem->data);
964                         msginfo = folder->fetch_msginfo(folder, item, num);
965                         if(msginfo != NULL) {
966                                 msgcache_add_msg(item->cache, msginfo);
967                                 if(MSG_IS_NEW(msginfo->flags))
968                                     item->new++;
969                                 if(MSG_IS_UNREAD(msginfo->flags))
970                                     item->unread++;
971                                 item->total++;
972                                 procmsg_msginfo_free(msginfo);
973                                 debug_print(_("Added newly found message %d to cache.\n"), num);
974                         }
975                 }
976                 folderview_update_item(item, FALSE);
977         }
978
979         g_slist_free(folder_list);
980         g_slist_free(cache_list);
981         g_slist_free(new_list);
982         g_free(folderscaninfo);
983
984         return 0;
985 }
986
987 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
988                                           gpointer data)
989 {
990         folder_item_scan(FOLDER_ITEM(key));
991 }
992
993 void folder_item_scan_foreach(GHashTable *table)
994 {
995         g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
996 }
997
998 void folder_count_total_cache_memusage(FolderItem *item, gpointer data)
999 {
1000         gint *memusage = (gint *)data;
1001
1002         if(item->cache == NULL)
1003                 return;
1004         
1005         *memusage += msgcache_get_memory_usage(item->cache);
1006 }
1007
1008 gint folder_cache_time_compare_func(gconstpointer a, gconstpointer b)
1009 {
1010         FolderItem *fa = (FolderItem *)a;
1011         FolderItem *fb = (FolderItem *)b;
1012         
1013         return (gint) (msgcache_get_last_access_time(fa->cache) - msgcache_get_last_access_time(fb->cache));
1014 }
1015
1016 void folder_find_expired_caches(FolderItem *item, gpointer data)
1017 {
1018         GSList **folder_item_list = (GSList **)data;
1019         gint difftime, expiretime;
1020         
1021         if(item->cache == NULL)
1022                 return;
1023
1024         difftime = (gint) (time(NULL) - msgcache_get_last_access_time(item->cache));
1025         expiretime = prefs_common.cache_min_keep_time * 60;
1026         debug_print(_("Cache unused time: %d (Expire time: %d)\n"), difftime, expiretime);
1027         if(difftime > expiretime) {
1028                 *folder_item_list = g_slist_insert_sorted(*folder_item_list, item, folder_cache_time_compare_func);
1029         }
1030 }
1031
1032 void folder_item_free_cache(FolderItem *item)
1033 {
1034         g_return_if_fail(item != NULL);
1035         g_return_if_fail(item->cache != NULL);
1036         
1037         folder_item_write_cache(item);
1038         msgcache_destroy(item->cache);
1039         item->cache = NULL;
1040 }
1041
1042 void folder_clean_cache_memory()
1043 {
1044         gint memusage = 0;
1045
1046         folder_func_to_all_folders(folder_count_total_cache_memusage, &memusage);       
1047         debug_print(_("Total cache memory usage: %d\n"), memusage);
1048         
1049         if(memusage > (prefs_common.cache_max_mem_usage * 1024)) {
1050                 GSList *folder_item_list = NULL, *listitem;
1051                 
1052                 debug_print(_("Trying to free cache memory\n"));
1053
1054                 folder_func_to_all_folders(folder_find_expired_caches, &folder_item_list);      
1055                 listitem = folder_item_list;
1056                 while((listitem != NULL) && (memusage > (prefs_common.cache_max_mem_usage * 1024))) {
1057                         FolderItem *item = (FolderItem *)(listitem->data);
1058
1059                         debug_print(_("Freeing cache memory for %s\n"), item->path);
1060                         memusage -= msgcache_get_memory_usage(item->cache);
1061                         folder_item_free_cache(item);
1062                         listitem = listitem->next;
1063                 }
1064                 g_slist_free(folder_item_list);
1065         }
1066 }
1067
1068 void folder_item_read_cache(FolderItem *item)
1069 {
1070         gchar *cache_file, *mark_file;
1071         
1072         g_return_if_fail(item != NULL);
1073
1074         cache_file = folder_item_get_cache_file(item);
1075         mark_file = folder_item_get_mark_file(item);
1076         item->cache = msgcache_read_cache(item, cache_file);
1077         if(!item->cache) {
1078                 item->cache = msgcache_new();
1079                 folder_item_scan(item);
1080         }
1081         msgcache_read_mark(item->cache, mark_file);
1082         g_free(cache_file);
1083         g_free(mark_file);
1084
1085         folder_clean_cache_memory();
1086 }
1087
1088 void folder_item_write_cache(FolderItem *item)
1089 {
1090         gchar *cache_file, *mark_file;
1091         PrefsFolderItem *prefs;
1092         gint filemode = 0;
1093         gchar *id;
1094         
1095         if (!item || !item->path || !item->cache)
1096                 return;
1097
1098         id = folder_item_get_identifier(item);
1099         debug_print(_("Save cache for folder %s\n"), id);
1100         g_free(id);
1101
1102         cache_file = folder_item_get_cache_file(item);
1103         mark_file = folder_item_get_mark_file(item);
1104         if(msgcache_write(cache_file, mark_file, item->cache) < 0) {
1105                 prefs = item->prefs;
1106                 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
1107                         /* for cache file */
1108                         filemode = prefs->folder_chmod;
1109                         if (filemode & S_IRGRP) filemode |= S_IWGRP;
1110                         if (filemode & S_IROTH) filemode |= S_IWOTH;
1111                         chmod(cache_file, filemode);
1112                 }
1113         }
1114
1115         g_free(cache_file);
1116         g_free(mark_file);
1117 }
1118
1119 MsgInfo *folder_item_fetch_msginfo(FolderItem *item, gint num)
1120 {
1121         Folder *folder;
1122         MsgInfo *msginfo;
1123         
1124         g_return_val_if_fail(item != NULL, NULL);
1125         
1126         folder = item->folder;
1127         if(!item->cache)
1128                 folder_item_read_cache(item);
1129         
1130         if(msginfo = msgcache_get_msg(item->cache, num))
1131                 return msginfo;
1132         
1133         g_return_val_if_fail(folder->fetch_msginfo, NULL);
1134         msginfo = folder->fetch_msginfo(folder, item, num);
1135         return msginfo;
1136 }
1137
1138 GSList *folder_item_get_msg_list(FolderItem *item)
1139 {
1140         g_return_val_if_fail(item != NULL, NULL);
1141         
1142         if(item->cache == 0)
1143                 folder_item_read_cache(item);
1144
1145         g_return_val_if_fail(item->cache != NULL, NULL);
1146         
1147         return msgcache_get_msg_list(item->cache);
1148 }
1149
1150 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
1151 {
1152         Folder *folder;
1153
1154         g_return_val_if_fail(item != NULL, NULL);
1155
1156         folder = item->folder;
1157
1158         g_return_val_if_fail(folder->fetch_msg != NULL, NULL);
1159
1160         return folder->fetch_msg(folder, item, num);
1161 }
1162
1163 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
1164                          gboolean remove_source)
1165 {
1166         Folder *folder;
1167         gint num;
1168         MsgInfo *msginfo;
1169
1170         g_return_val_if_fail(dest != NULL, -1);
1171         g_return_val_if_fail(file != NULL, -1);
1172
1173         folder = dest->folder;
1174
1175         g_return_val_if_fail(folder->add_msg != NULL, -1);
1176
1177         if (!dest->cache)
1178                 folder_item_read_cache(dest);
1179
1180         num = folder->add_msg(folder, dest, file, remove_source);
1181
1182         if (num > 0) {
1183                 msginfo = folder->fetch_msginfo(folder, dest, num);
1184
1185                 if(MSG_IS_NEW(msginfo->flags))
1186                         dest->new++;
1187                 if(MSG_IS_UNREAD(msginfo->flags))
1188                         dest->unread++;
1189                 dest->total++;
1190
1191                 dest->last_num = num;
1192                 msgcache_add_msg(dest->cache, msginfo);
1193                 procmsg_msginfo_free(msginfo);
1194         }
1195
1196         return num;
1197 }
1198
1199 /*
1200 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1201 {
1202         Folder *folder;
1203         gint num;
1204
1205         g_return_val_if_fail(dest != NULL, -1);
1206         g_return_val_if_fail(msginfo != NULL, -1);
1207
1208         folder = dest->folder;
1209         if (dest->last_num < 0) folder->scan(folder, dest);
1210
1211         num = folder->move_msg(folder, dest, msginfo);
1212         if (num > 0) dest->last_num = num;
1213
1214         return num;
1215 }
1216 */
1217
1218 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1219 {
1220         Folder *folder;
1221         gint num;
1222         gchar * filename;
1223         Folder * src_folder;
1224
1225         g_return_val_if_fail(dest != NULL, -1);
1226         g_return_val_if_fail(msginfo != NULL, -1);
1227
1228         folder = dest->folder;
1229
1230         g_return_val_if_fail(folder->remove_msg != NULL, -1);
1231         g_return_val_if_fail(folder->copy_msg != NULL, -1);
1232
1233         if (!dest->cache) folder_item_read_cache(dest);
1234
1235         src_folder = msginfo->folder->folder;
1236
1237         num = folder->copy_msg(folder, dest, msginfo);
1238         
1239         if (num != -1) {
1240                 MsgInfo *newmsginfo;
1241
1242                 newmsginfo = folder->fetch_msginfo(folder, dest, num);
1243                 newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
1244                 if (dest->stype == F_OUTBOX ||
1245                     dest->stype == F_QUEUE  ||
1246                     dest->stype == F_DRAFT  ||
1247                     dest->stype == F_TRASH)
1248                         MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
1249                                              MSG_NEW|MSG_UNREAD|MSG_DELETED);
1250                 msgcache_add_msg(dest->cache, newmsginfo);
1251
1252                 /* CLAWS */
1253                 if(src_folder->remove_msg) {
1254                         src_folder->remove_msg(src_folder,
1255                                                msginfo->folder,
1256                                                msginfo->msgnum);
1257                 }
1258                 msgcache_remove_msg(msginfo->folder->cache, msginfo->msgnum);
1259
1260                 if (MSG_IS_NEW(msginfo->flags))
1261                         msginfo->folder->new--;
1262                 if (MSG_IS_NEW(newmsginfo->flags))
1263                         dest->new++;
1264                 if (MSG_IS_UNREAD(msginfo->flags))
1265                         msginfo->folder->unread--;
1266                 if (MSG_IS_UNREAD(newmsginfo->flags))
1267                         dest->unread++;
1268                 msginfo->folder->total--;
1269                 dest->total++;
1270
1271                 procmsg_msginfo_free(newmsginfo);
1272         }
1273         
1274         if (folder->finished_copy)
1275                 folder->finished_copy(folder, dest);
1276
1277         return num;
1278 }
1279
1280 /*
1281 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
1282 {
1283         Folder *folder;
1284         gint num;
1285
1286         g_return_val_if_fail(dest != NULL, -1);
1287         g_return_val_if_fail(msglist != NULL, -1);
1288
1289         folder = dest->folder;
1290         if (dest->last_num < 0) folder->scan(folder, dest);
1291
1292         num = folder->move_msgs_with_dest(folder, dest, msglist);
1293         if (num > 0) dest->last_num = num;
1294         else dest->op_count = 0;
1295
1296         return num;
1297 }
1298 */
1299
1300 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
1301 {
1302         Folder *folder;
1303         FolderItem * item;
1304         GSList * l;
1305         gchar * filename;
1306         gint num;
1307
1308         g_return_val_if_fail(dest != NULL, -1);
1309         g_return_val_if_fail(msglist != NULL, -1);
1310
1311         folder = dest->folder;
1312
1313         g_return_val_if_fail(folder->copy_msg != NULL, -1);
1314         g_return_val_if_fail(folder->remove_msg != NULL, -1);
1315
1316         if (!dest->cache) folder_item_read_cache(dest);
1317
1318         item = NULL;
1319         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1320                 MsgInfo * msginfo = (MsgInfo *) l->data;
1321
1322                 if (!item && msginfo->folder != NULL)
1323                         item = msginfo->folder;
1324                 if (!item->cache) folder_item_read_cache(dest);
1325
1326                 num = folder->copy_msg(folder, dest, msginfo);
1327                 if (num != -1) {
1328                         MsgInfo *newmsginfo;
1329
1330                         newmsginfo = folder->fetch_msginfo(folder, dest, num);
1331                         if(newmsginfo) {
1332                                 newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
1333                                 if (dest->stype == F_OUTBOX ||
1334                                     dest->stype == F_QUEUE  ||
1335                                     dest->stype == F_DRAFT  ||
1336                                     dest->stype == F_TRASH)
1337                                         MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
1338                                                              MSG_NEW|MSG_UNREAD|MSG_DELETED);
1339                                 msgcache_add_msg(dest->cache, newmsginfo);
1340
1341                                 if (MSG_IS_NEW(msginfo->flags))
1342                                         msginfo->folder->new--;
1343                                 if (MSG_IS_NEW(newmsginfo->flags))
1344                                         dest->new++;
1345                                 if (MSG_IS_UNREAD(msginfo->flags))
1346                                         msginfo->folder->unread--;
1347                                 if (MSG_IS_UNREAD(newmsginfo->flags))
1348                                         dest->unread++;
1349                                 msginfo->folder->total--;
1350                                 dest->total++;
1351
1352                                 procmsg_msginfo_free(newmsginfo);
1353                         }
1354                         item->folder->remove_msg(item->folder,
1355                                                  msginfo->folder,
1356                                                  msginfo->msgnum);
1357                         msgcache_remove_msg(item->cache, msginfo->msgnum);
1358                 }
1359         }
1360
1361         if (folder->finished_copy)
1362                 folder->finished_copy(folder, dest);
1363
1364         return dest->last_num;
1365 }
1366
1367 /*
1368 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
1369 {
1370         Folder *folder;
1371         gint num;
1372
1373         g_return_val_if_fail(dest != NULL, -1);
1374         g_return_val_if_fail(msginfo != NULL, -1);
1375
1376         folder = dest->folder;
1377         if (dest->last_num < 0) folder->scan(folder, dest);
1378
1379         num = folder->copy_msg(folder, dest, msginfo);
1380         if (num > 0) dest->last_num = num;
1381
1382         return num;
1383 }
1384 */
1385
1386 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
1387 {
1388         Folder *folder;
1389         gint num;
1390         gchar * filename;
1391         Folder * src_folder;
1392
1393         g_return_val_if_fail(dest != NULL, -1);
1394         g_return_val_if_fail(msginfo != NULL, -1);
1395
1396         folder = dest->folder;
1397
1398         g_return_val_if_fail(folder->copy_msg != NULL, -1);
1399
1400         if (!dest->cache) folder_item_read_cache(dest);
1401         
1402         num = folder->copy_msg(folder, dest, msginfo);
1403         if (num != -1) {
1404                 MsgInfo *newmsginfo;
1405
1406                 newmsginfo = folder->fetch_msginfo(folder, dest, num);
1407                 newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
1408                 if (dest->stype == F_OUTBOX ||
1409                     dest->stype == F_QUEUE  ||
1410                     dest->stype == F_DRAFT  ||
1411                     dest->stype == F_TRASH)
1412                         MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
1413                                              MSG_NEW|MSG_UNREAD|MSG_DELETED);
1414                 msgcache_add_msg(dest->cache, newmsginfo);
1415
1416                 if (MSG_IS_NEW(newmsginfo->flags))
1417                         dest->new++;
1418                 if (MSG_IS_UNREAD(newmsginfo->flags))
1419                         dest->unread++;
1420                 dest->total++;
1421
1422                 procmsg_msginfo_free(newmsginfo);
1423         }
1424
1425         if (folder->finished_copy)
1426                 folder->finished_copy(folder, dest);
1427
1428         return num;
1429 }
1430
1431 /*
1432 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
1433 {
1434         Folder *folder;
1435         gint num;
1436
1437         g_return_val_if_fail(dest != NULL, -1);
1438         g_return_val_if_fail(msglist != NULL, -1);
1439
1440         folder = dest->folder;
1441         if (dest->last_num < 0) folder->scan(folder, dest);
1442
1443         num = folder->copy_msgs_with_dest(folder, dest, msglist);
1444         if (num > 0) dest->last_num = num;
1445         else dest->op_count = 0;
1446
1447         return num;
1448 }
1449 */
1450
1451 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
1452 {
1453         Folder *folder;
1454         gint num;
1455         GSList * l;
1456         gchar * filename;
1457
1458         g_return_val_if_fail(dest != NULL, -1);
1459         g_return_val_if_fail(msglist != NULL, -1);
1460
1461         folder = dest->folder;
1462  
1463         g_return_val_if_fail(folder->copy_msg != NULL, -1);
1464
1465         if (!dest->cache) folder_item_read_cache(dest);
1466
1467         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1468                 MsgInfo * msginfo = (MsgInfo *) l->data;
1469
1470                 num = folder->copy_msg(folder, dest, msginfo);
1471                 if (num != -1) {
1472                         MsgInfo *newmsginfo;
1473
1474                         newmsginfo = folder->fetch_msginfo(folder, dest, num);
1475                         if(newmsginfo) {
1476                                 newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
1477                                 if (dest->stype == F_OUTBOX ||
1478                                     dest->stype == F_QUEUE  ||
1479                                     dest->stype == F_DRAFT  ||
1480                                     dest->stype == F_TRASH)
1481                                         MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
1482                                                              MSG_NEW|MSG_UNREAD|MSG_DELETED);
1483                                 msgcache_add_msg(dest->cache, newmsginfo);
1484
1485                                 if (MSG_IS_NEW(newmsginfo->flags))
1486                                         dest->new++;
1487                                 if (MSG_IS_UNREAD(newmsginfo->flags))
1488                                         dest->unread++;
1489                                 dest->total++;
1490
1491                                 procmsg_msginfo_free(newmsginfo);
1492                         }
1493                 }
1494         }
1495
1496         if (folder->finished_copy)
1497                 folder->finished_copy(folder, dest);
1498
1499         return dest->last_num;
1500 }
1501
1502 gint folder_item_remove_msg(FolderItem *item, gint num)
1503 {
1504         Folder *folder;
1505         gint ret;
1506         MsgInfo *msginfo;
1507
1508         g_return_val_if_fail(item != NULL, -1);
1509
1510         folder = item->folder;
1511         if (!item->cache) folder_item_read_cache(item);
1512
1513         ret = folder->remove_msg(folder, item, num);
1514
1515         msginfo = msgcache_get_msg(item->cache, num);
1516         if(MSG_IS_NEW(msginfo->flags))
1517                 item->new--;
1518         if(MSG_IS_UNREAD(msginfo->flags))
1519                 item->unread--;
1520         item->total--;
1521         procmsg_msginfo_free(msginfo);
1522         msgcache_remove_msg(item->cache, num);
1523
1524         return ret;
1525 }
1526
1527 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
1528 {
1529         gint ret = 0;
1530
1531         g_return_val_if_fail(item != NULL, -1);
1532
1533         if (!item->cache) folder_item_read_cache(item);
1534
1535         while (msglist != NULL) {
1536                 MsgInfo *msginfo = (MsgInfo *)msglist->data;
1537
1538                 ret = folder_item_remove_msg(item, msginfo->msgnum);
1539                 if (ret != 0) break;
1540                 msgcache_remove_msg(item->cache, msginfo->msgnum);
1541                 msglist = msglist->next;
1542         }
1543
1544         return ret;
1545 }
1546
1547 gint folder_item_remove_all_msg(FolderItem *item)
1548 {
1549         Folder *folder;
1550         gint result;
1551
1552         g_return_val_if_fail(item != NULL, -1);
1553
1554         folder = item->folder;
1555
1556         g_return_val_if_fail(folder->remove_all_msg != NULL, -1);
1557
1558         result = folder->remove_all_msg(folder, item);
1559
1560         if (result == 0) {
1561                 if (folder->finished_remove)
1562                         folder->finished_remove(folder, item);
1563         }
1564
1565         return result;
1566 }
1567
1568 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
1569 {
1570         Folder *folder;
1571
1572         g_return_val_if_fail(item != NULL, FALSE);
1573
1574         folder = item->folder;
1575
1576         g_return_val_if_fail(folder->is_msg_changed != NULL, -1);
1577
1578         return folder->is_msg_changed(folder, item, msginfo);
1579 }
1580
1581 gchar *folder_item_get_cache_file(FolderItem *item)
1582 {
1583         gchar *path;
1584         gchar *file;
1585
1586         g_return_val_if_fail(item != NULL, NULL);
1587         g_return_val_if_fail(item->path != NULL, NULL);
1588
1589         path = folder_item_get_path(item);
1590         g_return_val_if_fail(path != NULL, NULL);
1591         if (!is_dir_exist(path))
1592                 make_dir_hier(path);
1593         file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
1594         g_free(path);
1595
1596         return file;
1597 }
1598
1599 gchar *folder_item_get_mark_file(FolderItem *item)
1600 {
1601         gchar *path;
1602         gchar *file;
1603
1604         g_return_val_if_fail(item != NULL, NULL);
1605         g_return_val_if_fail(item->path != NULL, NULL);
1606
1607         path = folder_item_get_path(item);
1608         g_return_val_if_fail(path != NULL, NULL);
1609         if (!is_dir_exist(path))
1610                 make_dir_hier(path);
1611         file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
1612         g_free(path);
1613
1614         return file;
1615 }
1616
1617 static gboolean folder_build_tree(GNode *node, gpointer data)
1618 {
1619         Folder *folder = FOLDER(data);
1620         FolderItem *item;
1621         XMLNode *xmlnode;
1622         GList *list;
1623         SpecialFolderItemType stype = F_NORMAL;
1624         const gchar *name = NULL;
1625         const gchar *path = NULL;
1626         PrefsAccount *account = NULL;
1627         gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE, 
1628                  threaded = TRUE, ret_rcpt = FALSE, hidereadmsgs = FALSE;
1629         FolderSortKey sort_key = SORT_BY_NONE;
1630         FolderSortType sort_type = SORT_ASCENDING;
1631         gint new = 0, unread = 0, total = 0;
1632         time_t mtime = 0;
1633
1634         g_return_val_if_fail(node->data != NULL, FALSE);
1635         if (!node->parent) return FALSE;
1636
1637         xmlnode = node->data;
1638         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1639                 g_warning("tag name != \"folderitem\"\n");
1640                 return FALSE;
1641         }
1642
1643         list = xmlnode->tag->attr;
1644         for (; list != NULL; list = list->next) {
1645                 XMLAttr *attr = list->data;
1646
1647                 if (!attr || !attr->name || !attr->value) continue;
1648                 if (!strcmp(attr->name, "type")) {
1649                         if (!strcasecmp(attr->value, "normal"))
1650                                 stype = F_NORMAL;
1651                         else if (!strcasecmp(attr->value, "inbox"))
1652                                 stype = F_INBOX;
1653                         else if (!strcasecmp(attr->value, "outbox"))
1654                                 stype = F_OUTBOX;
1655                         else if (!strcasecmp(attr->value, "draft"))
1656                                 stype = F_DRAFT;
1657                         else if (!strcasecmp(attr->value, "queue"))
1658                                 stype = F_QUEUE;
1659                         else if (!strcasecmp(attr->value, "trash"))
1660                                 stype = F_TRASH;
1661                 } else if (!strcmp(attr->name, "name"))
1662                         name = attr->value;
1663                 else if (!strcmp(attr->name, "path"))
1664                         path = attr->value;
1665                 else if (!strcmp(attr->name, "account_id")) {
1666                         account = account_find_from_id(atoi(attr->value));
1667                         if (!account) g_warning("account_id: %s not found\n",
1668                                                 attr->value);
1669                 } else if (!strcmp(attr->name, "mtime"))
1670                         mtime = strtoul(attr->value, NULL, 10);
1671                 else if (!strcmp(attr->name, "new"))
1672                         new = atoi(attr->value);
1673                 else if (!strcmp(attr->name, "unread"))
1674                         unread = atoi(attr->value);
1675                 else if (!strcmp(attr->name, "total"))
1676                         total = atoi(attr->value);
1677                 else if (!strcmp(attr->name, "no_sub"))
1678                         no_sub = *attr->value == '1' ? TRUE : FALSE;
1679                 else if (!strcmp(attr->name, "no_select"))
1680                         no_select = *attr->value == '1' ? TRUE : FALSE;
1681                 else if (!strcmp(attr->name, "collapsed"))
1682                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1683                 else if (!strcmp(attr->name, "threaded"))
1684                         threaded =  *attr->value == '1' ? TRUE : FALSE;
1685                 else if (!strcmp(attr->name, "hidereadmsgs"))
1686                         hidereadmsgs =  *attr->value == '1' ? TRUE : FALSE;
1687                 else if (!strcmp(attr->name, "reqretrcpt"))
1688                         ret_rcpt =  *attr->value == '1' ? TRUE : FALSE;
1689                 else if (!strcmp(attr->name, "sort_key")) {
1690                         if (!strcmp(attr->value, "none"))
1691                                 sort_key = SORT_BY_NONE;
1692                         else if (!strcmp(attr->value, "number"))
1693                                 sort_key = SORT_BY_NUMBER;
1694                         else if (!strcmp(attr->value, "size"))
1695                                 sort_key = SORT_BY_SIZE;
1696                         else if (!strcmp(attr->value, "date"))
1697                                 sort_key = SORT_BY_DATE;
1698                         else if (!strcmp(attr->value, "from"))
1699                                 sort_key = SORT_BY_FROM;
1700                         else if (!strcmp(attr->value, "subject"))
1701                                 sort_key = SORT_BY_SUBJECT;
1702                         else if (!strcmp(attr->value, "score"))
1703                                 sort_key = SORT_BY_SCORE;
1704                         else if (!strcmp(attr->value, "label"))
1705                                 sort_key = SORT_BY_LABEL;
1706                         else if (!strcmp(attr->value, "mark"))
1707                                 sort_key = SORT_BY_MARK;
1708                         else if (!strcmp(attr->value, "unread"))
1709                                 sort_key = SORT_BY_UNREAD;
1710                         else if (!strcmp(attr->value, "mime"))
1711                                 sort_key = SORT_BY_MIME;
1712                         else if (!strcmp(attr->value, "locked"))
1713                                 sort_key = SORT_BY_LOCKED;
1714                 } else if (!strcmp(attr->name, "sort_type")) {
1715                         if (!strcmp(attr->value, "ascending"))
1716                                 sort_type = SORT_ASCENDING;
1717                         else
1718                                 sort_type = SORT_DESCENDING;
1719                 }
1720         }
1721
1722         item = folder_item_new(name, path);
1723         item->stype = stype;
1724         item->account = account;
1725         item->mtime = mtime;
1726         item->new = new;
1727         item->unread = unread;
1728         item->total = total;
1729         item->no_sub = no_sub;
1730         item->no_select = no_select;
1731         item->collapsed = collapsed;
1732         item->threaded  = threaded;
1733         item->hide_read_msgs  = hidereadmsgs;
1734         item->ret_rcpt  = ret_rcpt;
1735         item->sort_key  = sort_key;
1736         item->sort_type = sort_type;
1737         item->parent = FOLDER_ITEM(node->parent->data);
1738         item->folder = folder;
1739         switch (stype) {
1740         case F_INBOX:  folder->inbox  = item; break;
1741         case F_OUTBOX: folder->outbox = item; break;
1742         case F_DRAFT:  folder->draft  = item; break;
1743         case F_QUEUE:  folder->queue  = item; break;
1744         case F_TRASH:  folder->trash  = item; break;
1745         default:       break;
1746         }
1747
1748         prefs_folder_item_read_config(item);
1749
1750         node->data = item;
1751         xml_free_node(xmlnode);
1752
1753         return FALSE;
1754 }
1755
1756 static gboolean folder_read_folder_func(GNode *node, gpointer data)
1757 {
1758         Folder *folder;
1759         XMLNode *xmlnode;
1760         GList *list;
1761         FolderType type = F_UNKNOWN;
1762         const gchar *name = NULL;
1763         const gchar *path = NULL;
1764         PrefsAccount *account = NULL;
1765         gboolean collapsed = FALSE, threaded = TRUE, ret_rcpt = FALSE;
1766
1767         if (g_node_depth(node) != 2) return FALSE;
1768         g_return_val_if_fail(node->data != NULL, FALSE);
1769
1770         xmlnode = node->data;
1771         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1772                 g_warning("tag name != \"folder\"\n");
1773                 return TRUE;
1774         }
1775         g_node_unlink(node);
1776         list = xmlnode->tag->attr;
1777         for (; list != NULL; list = list->next) {
1778                 XMLAttr *attr = list->data;
1779
1780                 if (!attr || !attr->name || !attr->value) continue;
1781                 if (!strcmp(attr->name, "type")) {
1782                         if (!strcasecmp(attr->value, "mh"))
1783                                 type = F_MH;
1784                         else if (!strcasecmp(attr->value, "mbox"))
1785                                 type = F_MBOX;
1786                         else if (!strcasecmp(attr->value, "maildir"))
1787                                 type = F_MAILDIR;
1788                         else if (!strcasecmp(attr->value, "imap"))
1789                                 type = F_IMAP;
1790                         else if (!strcasecmp(attr->value, "news"))
1791                                 type = F_NEWS;
1792                 } else if (!strcmp(attr->name, "name"))
1793                         name = attr->value;
1794                 else if (!strcmp(attr->name, "path"))
1795                         path = attr->value;
1796                 else if (!strcmp(attr->name, "account_id")) {
1797                         account = account_find_from_id(atoi(attr->value));
1798                         if (!account) g_warning("account_id: %s not found\n",
1799                                                 attr->value);
1800                 } else if (!strcmp(attr->name, "collapsed"))
1801                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1802                 else if (!strcmp(attr->name, "threaded"))
1803                         threaded = *attr->value == '1' ? TRUE : FALSE;
1804                 else if (!strcmp(attr->name, "reqretrcpt"))
1805                         ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
1806         }
1807
1808         folder = folder_new(type, name, path);
1809         g_return_val_if_fail(folder != NULL, FALSE);
1810         folder->account = account;
1811         if (account && (type == F_IMAP || type == F_NEWS))
1812                 account->folder = REMOTE_FOLDER(folder);
1813         node->data = folder->node->data;
1814         g_node_destroy(folder->node);
1815         folder->node = node;
1816         folder_add(folder);
1817         FOLDER_ITEM(node->data)->collapsed = collapsed;
1818         FOLDER_ITEM(node->data)->threaded  = threaded;
1819         FOLDER_ITEM(node->data)->ret_rcpt  = ret_rcpt;
1820
1821         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1822                         folder_build_tree, folder);
1823
1824         return FALSE;
1825 }
1826
1827 static gchar *folder_get_list_path(void)
1828 {
1829         static gchar *filename = NULL;
1830
1831         if (!filename)
1832                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1833                                         FOLDER_LIST, NULL);
1834
1835         return filename;
1836 }
1837
1838 static void folder_write_list_recursive(GNode *node, gpointer data)
1839 {
1840         FILE *fp = (FILE *)data;
1841         FolderItem *item = FOLDER_ITEM(node->data);
1842         gint i, depth;
1843         static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1844                                            "news", "unknown"};
1845         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1846                                                  "draft", "queue", "trash"};
1847         static gchar *sort_key_str[] = {"none", "number", "size", "date",
1848                                         "from", "subject", "score", "label",
1849                                         "mark", "unread", "mime", "locked" };
1850
1851         g_return_if_fail(item != NULL);
1852
1853         depth = g_node_depth(node);
1854         for (i = 0; i < depth; i++)
1855                 fputs("    ", fp);
1856         if (depth == 1) {
1857                 Folder *folder = item->folder;
1858
1859                 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
1860                 if (folder->name) {
1861                         fputs(" name=\"", fp);
1862                         xml_file_put_escape_str(fp, folder->name);
1863                         fputs("\"", fp);
1864                 }
1865                 if ((folder->type == F_MH) || (folder->type == F_MBOX)) {
1866                         fputs(" path=\"", fp);
1867                         xml_file_put_escape_str
1868                                 (fp, LOCAL_FOLDER(folder)->rootpath);
1869                         fputs("\"", fp);
1870                 }
1871                 if (folder->account)
1872                         fprintf(fp, " account_id=\"%d\"",
1873                                 folder->account->account_id);
1874                 if (item->collapsed && node->children)
1875                         fputs(" collapsed=\"1\"", fp);
1876                 if (item->ret_rcpt) 
1877                         fputs(" reqretrcpt=\"1\"", fp);
1878         } else {
1879                 fprintf(fp, "<folderitem type=\"%s\"",
1880                         folder_item_stype_str[item->stype]);
1881                 if (item->name) {
1882                         fputs(" name=\"", fp);
1883                         xml_file_put_escape_str(fp, item->name);
1884                         fputs("\"", fp);
1885                 }
1886                 if (item->path) {
1887                         fputs(" path=\"", fp);
1888                         xml_file_put_escape_str(fp, item->path);
1889                         fputs("\"", fp);
1890                 }
1891                 if (item->account)
1892                         fprintf(fp, " account_id=\"%d\"",
1893                                 item->account->account_id);
1894                 if (item->no_sub)
1895                         fputs(" no_sub=\"1\"", fp);
1896                 if (item->no_select)
1897                         fputs(" no_select=\"1\"", fp);
1898                 if (item->collapsed && node->children)
1899                         fputs(" collapsed=\"1\"", fp);
1900                 if (item->threaded)
1901                         fputs(" threaded=\"1\"", fp);
1902                 else
1903                         fputs(" threaded=\"0\"", fp);
1904                 if (item->hide_read_msgs)
1905                         fputs(" hidereadmsgs=\"1\"", fp);
1906                 else
1907                         fputs(" hidereadmsgs=\"0\"", fp);
1908                 if (item->ret_rcpt)
1909                         fputs(" reqretrcpt=\"1\"", fp);
1910
1911                 if (item->sort_key != SORT_BY_NONE) {
1912                         fprintf(fp, " sort_key=\"%s\"",
1913                                 sort_key_str[item->sort_key]);
1914                         if (item->sort_type == SORT_ASCENDING)
1915                                 fprintf(fp, " sort_type=\"ascending\"");
1916                         else
1917                                 fprintf(fp, " sort_type=\"descending\"");
1918                 }
1919
1920                 fprintf(fp,
1921                         " mtime=\"%lu\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1922                         item->mtime, item->new, item->unread, item->total);
1923         }
1924
1925         if (node->children) {
1926                 GNode *child;
1927                 fputs(">\n", fp);
1928
1929                 child = node->children;
1930                 while (child) {
1931                         GNode *cur;
1932
1933                         cur = child;
1934                         child = cur->next;
1935                         folder_write_list_recursive(cur, data);
1936                 }
1937
1938                 for (i = 0; i < depth; i++)
1939                         fputs("    ", fp);
1940                 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1941         } else
1942                 fputs(" />\n", fp);
1943 }
1944
1945 static void folder_update_op_count_rec(GNode *node) {
1946         FolderItem *fitem = FOLDER_ITEM(node->data);
1947
1948         if (g_node_depth(node) > 0) {
1949                 if (fitem->op_count > 0) {
1950                         fitem->op_count = 0;
1951                         folderview_update_item(fitem, 0);
1952                 }
1953                 if (node->children) {
1954                         GNode *child;
1955
1956                         child = node->children;
1957                         while (child) {
1958                                 GNode *cur;
1959
1960                                 cur = child;
1961                                 child = cur->next;
1962                                 folder_update_op_count_rec(cur);
1963                         }
1964                 }
1965         }
1966 }
1967
1968 void folder_update_op_count() {
1969         GList *cur;
1970         Folder *folder;
1971
1972         for (cur = folder_list; cur != NULL; cur = cur->next) {
1973                 folder = cur->data;
1974                 folder_update_op_count_rec(folder->node);
1975         }
1976 }
1977
1978 typedef struct _type_str {
1979         gchar * str;
1980         gint type;
1981 } type_str;
1982
1983
1984 /*
1985 static gchar * folder_item_get_tree_identifier(FolderItem * item)
1986 {
1987         if (item->parent != NULL) {
1988                 gchar * path;
1989                 gchar * id;
1990
1991                 path = folder_item_get_tree_identifier(item->parent);
1992                 if (path == NULL)
1993                         return NULL;
1994
1995                 id = g_strconcat(path, "/", item->name, NULL);
1996                 g_free(path);
1997
1998                 return id;
1999         }
2000         else {
2001                 return g_strconcat("/", item->name, NULL);
2002         }
2003 }
2004 */
2005
2006 /* CLAWS: temporary local folder for filtering */
2007 static Folder *processing_folder;
2008 static FolderItem *processing_folder_item;
2009
2010 static void folder_create_processing_folder(void)
2011 {
2012 #define PROCESSING_FOLDER ".processing" 
2013         Folder     *tmpparent;
2014         FolderItem *tmpfolder;
2015         gchar      *tmpname;
2016
2017         tmpparent = folder_get_default_folder();
2018         g_assert(tmpparent);
2019         debug_print("tmpparentroot %s\n", LOCAL_FOLDER(tmpparent)->rootpath);
2020         if (LOCAL_FOLDER(tmpparent)->rootpath[0] == '/')
2021                 tmpname = g_strconcat(LOCAL_FOLDER(tmpparent)->rootpath,
2022                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
2023                                       NULL);
2024         else
2025                 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2026                                       LOCAL_FOLDER(tmpparent)->rootpath,
2027                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
2028                                       NULL);
2029
2030         processing_folder = folder_new(F_MH, "PROCESSING", LOCAL_FOLDER(tmpparent)->rootpath);
2031         g_assert(processing_folder);
2032
2033         if (!is_dir_exist(tmpname)) {
2034                 debug_print("*TMP* creating %s\n", tmpname);
2035                 processing_folder_item = processing_folder->create_folder(processing_folder,
2036                                                                           processing_folder->node->data,
2037                                                                           PROCESSING_FOLDER);
2038                 g_assert(processing_folder_item);                                                                         
2039         }
2040         else {
2041                 debug_print("*TMP* already created\n");
2042                 processing_folder_item = folder_item_new(".processing", ".processing");
2043                 g_assert(processing_folder_item);
2044                 folder_item_append(processing_folder->node->data, processing_folder_item);
2045         }
2046         g_free(tmpname);
2047 }
2048
2049 FolderItem *folder_get_default_processing(void)
2050 {
2051         if (!processing_folder_item) {
2052                 folder_create_processing_folder();
2053         }
2054         return processing_folder_item;
2055 }
2056
2057 /* folder_persist_prefs_new() - return hash table with persistent
2058  * settings (and folder name as key). 
2059  * (note that in claws other options are in the PREFS_FOLDER_ITEM_RC
2060  * file, so those don't need to be included in PersistPref yet) 
2061  */
2062 GHashTable *folder_persist_prefs_new(Folder *folder)
2063 {
2064         GHashTable *pptable;
2065
2066         g_return_val_if_fail(folder, NULL);
2067         pptable = g_hash_table_new(g_str_hash, g_str_equal);
2068         folder_get_persist_prefs_recursive(folder->node, pptable);
2069         return pptable;
2070 }
2071
2072 void folder_persist_prefs_free(GHashTable *pptable)
2073 {
2074         g_return_if_fail(pptable);
2075         g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
2076         g_hash_table_destroy(pptable);
2077 }
2078
2079 const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
2080 {
2081         if (pptable == NULL || name == NULL) return NULL;
2082         return g_hash_table_lookup(pptable, name);
2083 }
2084
2085 void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
2086 {
2087         const PersistPrefs *pp;
2088
2089         pp = folder_get_persist_prefs(pptable, item->path); 
2090         if (!pp) return;
2091
2092         /* CLAWS: since not all folder properties have been migrated to 
2093          * folderlist.xml, we need to call the old stuff first before
2094          * setting things that apply both to Main and Claws. */
2095         prefs_folder_item_read_config(item); 
2096          
2097         item->collapsed = pp->collapsed;
2098         item->threaded  = pp->threaded;
2099         item->ret_rcpt  = pp->ret_rcpt;
2100         item->hide_read_msgs = pp->hide_read_msgs;
2101         item->sort_key  = pp->sort_key;
2102         item->sort_type = pp->sort_type;
2103 }
2104
2105 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
2106 {
2107         FolderItem *item = FOLDER_ITEM(node->data);
2108         PersistPrefs *pp;
2109         GNode *child, *cur;
2110
2111         g_return_if_fail(node != NULL);
2112         g_return_if_fail(item != NULL);
2113
2114         /* FIXME: item->path == NULL for top level folder, so this means that 
2115          * properties of MH folder root will not be stored. Not quite important, 
2116          * because the top level folder properties are not special anyway. */
2117         if (item->path) {
2118                 pp = g_new0(PersistPrefs, 1);
2119                 g_return_if_fail(pp != NULL);
2120                 pp->collapsed = item->collapsed;
2121                 pp->threaded  = item->threaded;
2122                 pp->ret_rcpt  = item->ret_rcpt; 
2123                 pp->hide_read_msgs = item->hide_read_msgs;
2124                 pp->sort_key  = item->sort_key;
2125                 pp->sort_type = item->sort_type;
2126                 g_hash_table_insert(pptable, item->path, pp);
2127         }               
2128
2129         if (node->children) {
2130                 child = node->children;
2131                 while (child) {
2132                         cur = child;
2133                         child = cur->next;
2134                         folder_get_persist_prefs_recursive(cur, pptable);
2135                 }
2136         }       
2137 }
2138
2139 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
2140 {
2141         if (val) 
2142                 g_free(val);
2143         return TRUE;    
2144 }
2145