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