906c2e757448420490d2800f26b59a475b483f3e
[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
30 #include "intl.h"
31 #include "folder.h"
32 #include "folderview.h"
33 #include "session.h"
34 #include "imap.h"
35 #include "news.h"
36 #include "mh.h"
37 #include "mbox_folder.h"
38 #include "utils.h"
39 #include "xml.h"
40 #include "codeconv.h"
41 #include "prefs.h"
42 #include "account.h"
43 #include "prefs_account.h"
44 #include "prefs_folder_item.h"
45
46 static GList *folder_list = NULL;
47
48 static void folder_init         (Folder         *folder,
49                                  const gchar    *name);
50
51 static gboolean folder_read_folder_func (GNode          *node,
52                                          gpointer        data);
53 static gchar *folder_get_list_path      (void);
54 static void folder_write_list_recursive (GNode          *node,
55                                          gpointer        data);
56 static void folder_update_op_count_rec  (GNode          *node);
57
58
59 static void folder_get_persist_prefs_recursive
60                                         (GNode *node, GHashTable *pptable);
61 static gboolean persist_prefs_free      (gpointer key, gpointer val, gpointer data);
62
63
64 Folder *folder_new(FolderType type, const gchar *name, const gchar *path)
65 {
66         Folder *folder = NULL;
67
68         name = name ? name : path;
69         switch (type) {
70         case F_MBOX:
71                 folder = mbox_folder_new(name, path);
72                 break;
73         case F_MH:
74                 folder = mh_folder_new(name, path);
75                 break;
76         case F_IMAP:
77                 folder = imap_folder_new(name, path);
78                 break;
79         case F_NEWS:
80                 folder = news_folder_new(name, path);
81                 break;
82         default:
83                 return NULL;
84         }
85
86         return folder;
87 }
88
89 static void folder_init(Folder *folder, const gchar *name)
90 {
91         FolderItem *item;
92
93         g_return_if_fail(folder != NULL);
94
95         folder_set_name(folder, name);
96         folder->type = F_UNKNOWN;
97         folder->account = NULL;
98         folder->inbox = NULL;
99         folder->outbox = NULL;
100         folder->draft = NULL;
101         folder->queue = NULL;
102         folder->trash = NULL;
103         folder->ui_func = NULL;
104         folder->ui_func_data = NULL;
105         item = folder_item_new(name, NULL);
106         item->folder = folder;
107         folder->node = g_node_new(item);
108         folder->data = NULL;
109 }
110
111 void folder_local_folder_init(Folder *folder, const gchar *name,
112                               const gchar *path)
113 {
114         folder_init(folder, name);
115         LOCAL_FOLDER(folder)->rootpath = g_strdup(path);
116 }
117
118 void folder_remote_folder_init(Folder *folder, const gchar *name,
119                                const gchar *path)
120 {
121         folder_init(folder, name);
122         REMOTE_FOLDER(folder)->session = NULL;
123 }
124
125 void folder_destroy(Folder *folder)
126 {
127         g_return_if_fail(folder != NULL);
128
129         switch (folder->type) {
130         case F_MBOX:
131                 mbox_folder_destroy(MBOX_FOLDER(folder));
132         case F_MH:
133                 mh_folder_destroy(MH_FOLDER(folder));
134                 break;
135         case F_IMAP:
136                 imap_folder_destroy(IMAP_FOLDER(folder));
137                 break;
138         case F_NEWS:
139                 news_folder_destroy(NEWS_FOLDER(folder));
140                 break;
141         default:
142                 break;
143         }
144
145         folder_list = g_list_remove(folder_list, folder);
146
147         folder_tree_destroy(folder);
148         g_free(folder->name);
149         g_free(folder);
150 }
151
152 void folder_local_folder_destroy(LocalFolder *lfolder)
153 {
154         g_return_if_fail(lfolder != NULL);
155
156         g_free(lfolder->rootpath);
157 }
158
159 void folder_remote_folder_destroy(RemoteFolder *rfolder)
160 {
161         g_return_if_fail(rfolder != NULL);
162
163         if (rfolder->session)
164                 session_destroy(rfolder->session);
165 }
166
167 #if 0
168 Folder *mbox_folder_new(const gchar *name, const gchar *path)
169 {
170         /* not yet implemented */
171         return NULL;
172 }
173
174 Folder *maildir_folder_new(const gchar *name, const gchar *path)
175 {
176         /* not yet implemented */
177         return NULL;
178 }
179 #endif
180
181 FolderItem *folder_item_new(const gchar *name, const gchar *path)
182 {
183         FolderItem *item;
184
185         item = g_new0(FolderItem, 1);
186
187         item->stype = F_NORMAL;
188         item->name = g_strdup(name);
189         item->path = g_strdup(path);
190         item->account = NULL;
191         item->mtime = 0;
192         item->new = 0;
193         item->unread = 0;
194         item->total = 0;
195         item->last_num = -1;
196         item->no_sub = FALSE;
197         item->no_select = FALSE;
198         item->collapsed = FALSE;
199         item->threaded  = TRUE;
200         item->ret_rcpt  = FALSE;
201         item->opened    = FALSE;
202         item->parent = NULL;
203         item->folder = NULL;
204         item->mark_queue = NULL;
205         item->data = NULL;
206
207         item->prefs = prefs_folder_item_new();
208
209         return item;
210 }
211
212 void folder_item_append(FolderItem *parent, FolderItem *item)
213 {
214         GNode *node;
215
216         g_return_if_fail(parent != NULL);
217         g_return_if_fail(parent->folder != NULL);
218         g_return_if_fail(item != NULL);
219
220         node = parent->folder->node;
221         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, parent);
222         g_return_if_fail(node != NULL);
223
224         item->parent = parent;
225         item->folder = parent->folder;
226         g_node_append_data(node, item);
227 }
228
229 void folder_item_remove(FolderItem *item)
230 {
231         GNode *node;
232
233         g_return_if_fail(item != NULL);
234         g_return_if_fail(item->folder != NULL);
235
236         node = item->folder->node;
237         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
238         g_return_if_fail(node != NULL);
239
240         /* TODO: free all FolderItem's first */
241         if (item->folder->node == node)
242                 item->folder->node = NULL;
243         g_node_destroy(node);
244 }
245
246 void folder_item_destroy(FolderItem *item)
247 {
248         g_return_if_fail(item != NULL);
249
250         g_free(item->name);
251         g_free(item->path);
252         g_free(item);
253 }
254
255 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
256 {
257         g_return_if_fail(folder != NULL);
258
259         folder->ui_func = func;
260         folder->ui_func_data = data;
261 }
262
263 void folder_set_name(Folder *folder, const gchar *name)
264 {
265         g_return_if_fail(folder != NULL);
266
267         g_free(folder->name);
268         folder->name = name ? g_strdup(name) : NULL;
269         if (folder->node && folder->node->data) {
270                 FolderItem *item = (FolderItem *)folder->node->data;
271
272                 g_free(item->name);
273                 item->name = name ? g_strdup(name) : NULL;
274         }
275 }
276
277 void folder_tree_destroy(Folder *folder)
278 {
279         /* TODO: destroy all FolderItem before */
280         g_node_destroy(folder->node);
281
282         folder->inbox = NULL;
283         folder->outbox = NULL;
284         folder->draft = NULL;
285         folder->queue = NULL;
286         folder->trash = NULL;
287         folder->node = NULL;
288 }
289
290 void folder_add(Folder *folder)
291 {
292         Folder *cur_folder;
293         GList *cur;
294         gint i;
295
296         g_return_if_fail(folder != NULL);
297
298         for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
299                 cur_folder = FOLDER(cur->data);
300                 if (folder->type == F_MH) {
301                         if (cur_folder->type != F_MH) break;
302                 } else if (folder->type == F_MBOX) {
303                         if (cur_folder->type != F_MH &&
304                             cur_folder->type != F_MBOX) break;
305                 } else if (folder->type == F_IMAP) {
306                         if (cur_folder->type != F_MH &&
307                             cur_folder->type != F_MBOX &&
308                             cur_folder->type != F_IMAP) break;
309                 } else if (folder->type == F_NEWS) {
310                         if (cur_folder->type != F_MH &&
311                             cur_folder->type != F_MBOX &&
312                             cur_folder->type != F_IMAP &&
313                             cur_folder->type != F_NEWS) break;
314                 }
315         }
316
317         folder_list = g_list_insert(folder_list, folder, i);
318 }
319
320 GList *folder_get_list(void)
321 {
322         return folder_list;
323 }
324
325 gint folder_read_list(void)
326 {
327         GNode *node;
328         XMLNode *xmlnode;
329         gchar *path;
330
331         path = folder_get_list_path();
332         if (!is_file_exist(path)) return -1;
333         node = xml_parse_file(path);
334         if (!node) return -1;
335
336         xmlnode = node->data;
337         if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
338                 g_warning("wrong folder list\n");
339                 xml_free_tree(node);
340                 return -1;
341         }
342
343         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
344                         folder_read_folder_func, NULL);
345
346         xml_free_tree(node);
347         if (folder_list)
348                 return 0;
349         else
350                 return -1;
351 }
352
353 void folder_write_list(void)
354 {
355         GList *list;
356         Folder *folder;
357         gchar *path;
358         PrefFile *pfile;
359
360         path = folder_get_list_path();
361         if ((pfile = prefs_write_open(path)) == NULL) return;
362
363         fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
364                 conv_get_current_charset_str());
365         fputs("\n<folderlist>\n", pfile->fp);
366
367         for (list = folder_list; list != NULL; list = list->next) {
368                 folder = list->data;
369                 folder_write_list_recursive(folder->node, pfile->fp);
370         }
371
372         fputs("</folderlist>\n", pfile->fp);
373
374         if (prefs_write_close(pfile) < 0)
375                 g_warning("failed to write folder list.\n");
376 }
377
378 struct TotalMsgCount
379 {
380         guint new;
381         guint unread;
382         guint total;
383 };
384
385 static gboolean folder_count_total_msgs_func(GNode *node, gpointer data)
386 {
387         FolderItem *item;
388         struct TotalMsgCount *count = (struct TotalMsgCount *)data;
389
390         g_return_val_if_fail(node->data != NULL, FALSE);
391
392         item = FOLDER_ITEM(node->data);
393         count->new += item->new;
394         count->unread += item->unread;
395         count->total += item->total;
396
397         return FALSE;
398 }
399
400 void folder_count_total_msgs(guint *new, guint *unread, guint *total)
401 {
402         GList *list;
403         Folder *folder;
404         struct TotalMsgCount count;
405
406         count.new = count.unread = count.total = 0;
407
408         debug_print(_("Counting total number of messages...\n"));
409
410         for (list = folder_list; list != NULL; list = list->next) {
411                 folder = FOLDER(list->data);
412                 if (folder->node)
413                         g_node_traverse(folder->node, G_PRE_ORDER,
414                                         G_TRAVERSE_ALL, -1,
415                                         folder_count_total_msgs_func,
416                                         &count);
417         }
418
419         *new = count.new;
420         *unread = count.unread;
421         *total = count.total;
422
423         return;
424 }
425
426 Folder *folder_find_from_path(const gchar *path)
427 {
428         GList *list;
429         Folder *folder;
430
431         for (list = folder_list; list != NULL; list = list->next) {
432                 folder = list->data;
433                 if ((folder->type == F_MH || folder->type == F_MBOX) &&
434                     !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
435                         return folder;
436         }
437
438         return NULL;
439 }
440
441 Folder *folder_find_from_name(const gchar *name, FolderType type)
442 {
443         GList *list;
444         Folder *folder;
445
446         for (list = folder_list; list != NULL; list = list->next) {
447                 folder = list->data;
448                 if (folder->type == type && strcmp2(name, folder->name) == 0)
449                         return folder;
450         }
451
452         return NULL;
453 }
454
455 static gboolean folder_item_find_func(GNode *node, gpointer data)
456 {
457         FolderItem *item = node->data;
458         gpointer *d = data;
459         const gchar *path = d[0];
460
461         if (path_cmp(path, item->path) != 0)
462                 return FALSE;
463
464         d[1] = item;
465
466         return TRUE;
467 }
468
469 FolderItem *folder_find_item_from_path(const gchar *path)
470 {
471         Folder *folder;
472         gpointer d[2];
473
474         folder = folder_get_default_folder();
475         g_return_val_if_fail(folder != NULL, NULL);
476
477         d[0] = (gpointer)path;
478         d[1] = NULL;
479         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
480                         folder_item_find_func, d);
481         return d[1];
482 }
483
484 static const struct {
485         gchar *str;
486         FolderType type;
487 } type_str_table[] = {
488         {"#mh"     , F_MH},
489         {"#mbox"   , F_MBOX},
490         {"#maildir", F_MAILDIR},
491         {"#imap"   , F_IMAP},
492         {"#news"   , F_NEWS}
493 };
494
495 static gchar *folder_get_type_string(FolderType type)
496 {
497         gint i;
498
499         for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
500              i++) {
501                 if (type_str_table[i].type == type)
502                         return type_str_table[i].str;
503         }
504
505         return NULL;
506 }
507
508 static FolderType folder_get_type_from_string(const gchar *str)
509 {
510         gint i;
511
512         for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
513              i++) {
514                 if (g_strcasecmp(type_str_table[i].str, str) == 0)
515                         return type_str_table[i].type;
516         }
517
518         return F_UNKNOWN;
519 }
520
521 gchar *folder_get_identifier(Folder *folder)
522 {
523         gchar *type_str;
524
525         g_return_val_if_fail(folder != NULL, NULL);
526
527         type_str = folder_get_type_string(folder->type);
528         return g_strconcat(type_str, "/", folder->name, NULL);
529 }
530
531 gchar *folder_item_get_identifier(FolderItem *item)
532 {
533         gchar *id;
534         gchar *folder_id;
535
536         g_return_val_if_fail(item != NULL, NULL);
537         g_return_val_if_fail(item->path != NULL, NULL);
538
539         folder_id = folder_get_identifier(item->folder);
540         id = g_strconcat(folder_id, "/", item->path, NULL);
541         g_free(folder_id);
542
543         return id;
544 }
545
546 FolderItem *folder_find_item_from_identifier(const gchar *identifier)
547 {
548         Folder *folder;
549         gpointer d[2];
550         gchar *str;
551         gchar *p;
552         gchar *name;
553         gchar *path;
554         FolderType type;
555
556         g_return_val_if_fail(identifier != NULL, NULL);
557
558         if (*identifier != '#')
559                 return folder_find_item_from_path(identifier);
560
561         Xstrdup_a(str, identifier, return NULL);
562
563         p = strchr(str, '/');
564         if (!p)
565                 return folder_find_item_from_path(identifier);
566         *p = '\0';
567         p++;
568         type = folder_get_type_from_string(str);
569         if (type == F_UNKNOWN)
570                 return folder_find_item_from_path(identifier);
571
572         name = p;
573         p = strchr(p, '/');
574         if (!p)
575                 return folder_find_item_from_path(identifier);
576         *p = '\0';
577         p++;
578
579         folder = folder_find_from_name(name, type);
580         if (!folder)
581                 return folder_find_item_from_path(identifier);
582
583         path = p;
584
585         d[0] = (gpointer)path;
586         d[1] = NULL;
587         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
588                         folder_item_find_func, d);
589         return d[1];
590 }
591
592 Folder *folder_get_default_folder(void)
593 {
594         return folder_list ? FOLDER(folder_list->data) : NULL;
595 }
596
597 FolderItem *folder_get_default_inbox(void)
598 {
599         Folder *folder;
600
601         if (!folder_list) return NULL;
602         folder = FOLDER(folder_list->data);
603         g_return_val_if_fail(folder != NULL, NULL);
604         return folder->inbox;
605 }
606
607 FolderItem *folder_get_default_outbox(void)
608 {
609         Folder *folder;
610
611         if (!folder_list) return NULL;
612         folder = FOLDER(folder_list->data);
613         g_return_val_if_fail(folder != NULL, NULL);
614         return folder->outbox;
615 }
616
617 FolderItem *folder_get_default_draft(void)
618 {
619         Folder *folder;
620
621         if (!folder_list) return NULL;
622         folder = FOLDER(folder_list->data);
623         g_return_val_if_fail(folder != NULL, NULL);
624         return folder->draft;
625 }
626
627 FolderItem *folder_get_default_queue(void)
628 {
629         Folder *folder;
630
631         if (!folder_list) return NULL;
632         folder = FOLDER(folder_list->data);
633         g_return_val_if_fail(folder != NULL, NULL);
634         return folder->queue;
635 }
636
637 FolderItem *folder_get_default_trash(void)
638 {
639         Folder *folder;
640
641         if (!folder_list) return NULL;
642         folder = FOLDER(folder_list->data);
643         g_return_val_if_fail(folder != NULL, NULL);
644         return folder->trash;
645 }
646
647 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type)   \
648 {                                                       \
649         if (!folder->member) {                          \
650                 item = folder_item_new(dir, dir);       \
651                 item->stype = type;                     \
652                 folder_item_append(rootitem, item);     \
653                 folder->member = item;                  \
654         }                                               \
655 }
656
657 void folder_set_missing_folders(void)
658 {
659         Folder *folder;
660         FolderItem *rootitem;
661         FolderItem *item;
662         GList *list;
663
664         for (list = folder_list; list != NULL; list = list->next) {
665                 folder = list->data;
666                 if (folder->type != F_MH) continue;
667                 rootitem = FOLDER_ITEM(folder->node->data);
668                 g_return_if_fail(rootitem != NULL);
669
670                 if (folder->inbox && folder->outbox && folder->draft &&
671                     folder->queue && folder->trash)
672                         continue;
673
674                 if (folder->create_tree(folder) < 0) {
675                         g_warning("%s: can't create the folder tree.\n",
676                                   LOCAL_FOLDER(folder)->rootpath);
677                         continue;
678                 }
679
680                 CREATE_FOLDER_IF_NOT_EXIST(inbox,  INBOX_DIR,  F_INBOX);
681                 CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_INBOX);
682                 CREATE_FOLDER_IF_NOT_EXIST(draft,  DRAFT_DIR,  F_INBOX);
683                 CREATE_FOLDER_IF_NOT_EXIST(queue,  QUEUE_DIR,  F_INBOX);
684                 CREATE_FOLDER_IF_NOT_EXIST(trash,  TRASH_DIR,  F_INBOX);
685         }
686
687 }
688
689 #undef CREATE_FOLDER_IF_NOT_EXIST
690
691 gchar *folder_item_get_path(FolderItem *item)
692 {
693         gchar *folder_path;
694         gchar *path;
695
696         g_return_val_if_fail(item != NULL, NULL);
697
698         if (FOLDER_TYPE(item->folder) == F_MH)
699                 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
700         else if (FOLDER_TYPE(item->folder) == F_MBOX) {
701                 path = mbox_get_virtual_path(item);
702                 if (path == NULL)
703                         return NULL;
704                 folder_path = g_strconcat(get_mbox_cache_dir(),
705                                           G_DIR_SEPARATOR_S, path, NULL);
706                 g_free(path);
707
708                 return folder_path;
709         }
710         else if (FOLDER_TYPE(item->folder) == F_IMAP) {
711                 g_return_val_if_fail(item->folder->account != NULL, NULL);
712                 folder_path = g_strconcat(get_imap_cache_dir(),
713                                           G_DIR_SEPARATOR_S,
714                                           item->folder->account->recv_server,
715                                           G_DIR_SEPARATOR_S,
716                                           item->folder->account->userid,
717                                           NULL);
718         } else if (FOLDER_TYPE(item->folder) == F_NEWS) {
719                 g_return_val_if_fail(item->folder->account != NULL, NULL);
720                 folder_path = g_strconcat(get_news_cache_dir(),
721                                           G_DIR_SEPARATOR_S,
722                                           item->folder->account->nntp_server,
723                                           NULL);
724         } else
725                 return NULL;
726
727         g_return_val_if_fail(folder_path != NULL, NULL);
728
729         if (folder_path[0] == G_DIR_SEPARATOR) {
730                 if (item->path)
731                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
732                                            item->path, NULL);
733                 else
734                         path = g_strdup(folder_path);
735         } else {
736                 if (item->path)
737                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
738                                            folder_path, G_DIR_SEPARATOR_S,
739                                            item->path, NULL);
740                 else
741                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
742                                            folder_path, NULL);
743         }
744
745         g_free(folder_path);
746         return path;
747 }
748
749 gint folder_item_scan(FolderItem *item)
750 {
751         Folder *folder;
752
753         g_return_val_if_fail(item != NULL, -1);
754
755         folder = item->folder;
756         return folder->scan(folder, item);
757 }
758
759 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
760                                           gpointer data)
761 {
762         folder_item_scan(FOLDER_ITEM(key));
763 }
764
765 void folder_item_scan_foreach(GHashTable *table)
766 {
767         g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
768 }
769
770 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
771 {
772         Folder *folder;
773
774         g_return_val_if_fail(item != NULL, NULL);
775
776         folder = item->folder;
777
778         g_return_val_if_fail(folder->scan != NULL, NULL);
779         g_return_val_if_fail(folder->fetch_msg != NULL, NULL);
780
781         if (item->last_num < 0) folder->scan(folder, item);
782
783         return folder->fetch_msg(folder, item, num);
784 }
785
786 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
787                          gboolean remove_source)
788 {
789         Folder *folder;
790         gint num;
791
792         g_return_val_if_fail(dest != NULL, -1);
793         g_return_val_if_fail(file != NULL, -1);
794
795         folder = dest->folder;
796
797         g_return_val_if_fail(folder->scan != NULL, -1);
798         g_return_val_if_fail(folder->add_msg != NULL, -1);
799
800         if (dest->last_num < 0) folder->scan(folder, dest);
801
802         num = folder->add_msg(folder, dest, file, remove_source);
803         if (num > 0) dest->last_num = num;
804
805         return num;
806 }
807
808 /*
809 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
810 {
811         Folder *folder;
812         gint num;
813
814         g_return_val_if_fail(dest != NULL, -1);
815         g_return_val_if_fail(msginfo != NULL, -1);
816
817         folder = dest->folder;
818         if (dest->last_num < 0) folder->scan(folder, dest);
819
820         num = folder->move_msg(folder, dest, msginfo);
821         if (num > 0) dest->last_num = num;
822
823         return num;
824 }
825 */
826
827 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
828 {
829         Folder *folder;
830         gint num;
831         gchar * filename;
832         Folder * src_folder;
833
834         g_return_val_if_fail(dest != NULL, -1);
835         g_return_val_if_fail(msginfo != NULL, -1);
836
837         folder = dest->folder;
838
839         g_return_val_if_fail(folder->scan != NULL, -1);
840         g_return_val_if_fail(folder->remove_msg != NULL, -1);
841         g_return_val_if_fail(folder->copy_msg != NULL, -1);
842
843         if (dest->last_num < 0) folder->scan(folder, dest);
844
845         src_folder = msginfo->folder->folder;
846
847         num = folder->copy_msg(folder, dest, msginfo);
848         
849         if (num != -1) {
850                 /* CLAWS */
851                 g_assert(src_folder);
852                 g_assert(src_folder->remove_msg);
853                 src_folder->remove_msg(src_folder,
854                                        msginfo->folder,
855                                        msginfo->msgnum);
856         }                                      
857         
858         if (folder->finished_copy)
859                 folder->finished_copy(folder, dest);
860
861         src_folder = msginfo->folder->folder;
862
863         if (msginfo->folder && src_folder->scan)
864                 src_folder->scan(src_folder, msginfo->folder);
865         folder->scan(folder, dest);
866
867         return num;
868 }
869
870 /*
871 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
872 {
873         Folder *folder;
874         gint num;
875
876         g_return_val_if_fail(dest != NULL, -1);
877         g_return_val_if_fail(msglist != NULL, -1);
878
879         folder = dest->folder;
880         if (dest->last_num < 0) folder->scan(folder, dest);
881
882         num = folder->move_msgs_with_dest(folder, dest, msglist);
883         if (num > 0) dest->last_num = num;
884         else dest->op_count = 0;
885
886         return num;
887 }
888 */
889
890 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
891 {
892         Folder *folder;
893         FolderItem * item;
894         GSList * l;
895         gchar * filename;
896
897         g_return_val_if_fail(dest != NULL, -1);
898         g_return_val_if_fail(msglist != NULL, -1);
899
900         folder = dest->folder;
901
902         g_return_val_if_fail(folder->scan != NULL, -1);
903         g_return_val_if_fail(folder->copy_msg != NULL, -1);
904         g_return_val_if_fail(folder->remove_msg != NULL, -1);
905
906         if (dest->last_num < 0) folder->scan(folder, dest);
907
908         item = NULL;
909         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
910                 MsgInfo * msginfo = (MsgInfo *) l->data;
911
912                 if (!item && msginfo->folder != NULL)
913                         item = msginfo->folder;
914
915                 if (folder->copy_msg(folder, dest, msginfo) != -1)
916                         item->folder->remove_msg(item->folder,
917                                                  msginfo->folder,
918                                                  msginfo->msgnum);
919         }
920
921         if (folder->finished_copy)
922                 folder->finished_copy(folder, dest);
923
924         if (item && item->folder->scan)
925                 item->folder->scan(item->folder, item);
926         folder->scan(folder, dest);
927
928         return dest->last_num;
929 }
930
931 /*
932 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
933 {
934         Folder *folder;
935         gint num;
936
937         g_return_val_if_fail(dest != NULL, -1);
938         g_return_val_if_fail(msginfo != NULL, -1);
939
940         folder = dest->folder;
941         if (dest->last_num < 0) folder->scan(folder, dest);
942
943         num = folder->copy_msg(folder, dest, msginfo);
944         if (num > 0) dest->last_num = num;
945
946         return num;
947 }
948 */
949
950 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
951 {
952         Folder *folder;
953         gint num;
954         gchar * filename;
955         Folder * src_folder;
956
957         g_return_val_if_fail(dest != NULL, -1);
958         g_return_val_if_fail(msginfo != NULL, -1);
959
960         folder = dest->folder;
961
962         g_return_val_if_fail(folder->scan != NULL, -1);
963         g_return_val_if_fail(folder->copy_msg != NULL, -1);
964
965         if (dest->last_num < 0) folder->scan(folder, dest);
966         
967         num = folder->copy_msg(folder, dest, msginfo);
968
969         if (folder->finished_copy)
970                 folder->finished_copy(folder, dest);
971
972         folder->scan(folder, dest);
973
974         return num;
975 }
976
977 /*
978 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
979 {
980         Folder *folder;
981         gint num;
982
983         g_return_val_if_fail(dest != NULL, -1);
984         g_return_val_if_fail(msglist != NULL, -1);
985
986         folder = dest->folder;
987         if (dest->last_num < 0) folder->scan(folder, dest);
988
989         num = folder->copy_msgs_with_dest(folder, dest, msglist);
990         if (num > 0) dest->last_num = num;
991         else dest->op_count = 0;
992
993         return num;
994 }
995 */
996
997 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
998 {
999         Folder *folder;
1000         gint num;
1001         GSList * l;
1002         gchar * filename;
1003
1004         g_return_val_if_fail(dest != NULL, -1);
1005         g_return_val_if_fail(msglist != NULL, -1);
1006
1007         folder = dest->folder;
1008  
1009         g_return_val_if_fail(folder->scan != NULL, -1);
1010         g_return_val_if_fail(folder->copy_msg != NULL, -1);
1011
1012         if (dest->last_num < 0) folder->scan(folder, dest);
1013
1014         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1015                 MsgInfo * msginfo = (MsgInfo *) l->data;
1016
1017                 folder->copy_msg(folder, dest, msginfo);
1018         }
1019
1020         if (folder->finished_copy)
1021                 folder->finished_copy(folder, dest);
1022
1023         folder->scan(folder, dest);
1024
1025         return dest->last_num;
1026 }
1027
1028 gint folder_item_remove_msg(FolderItem *item, gint num)
1029 {
1030         Folder *folder;
1031         gint ret;
1032
1033         g_return_val_if_fail(item != NULL, -1);
1034
1035         folder = item->folder;
1036         if (item->last_num < 0) folder->scan(folder, item);
1037
1038         ret = folder->remove_msg(folder, item, num);
1039         if (ret == 0 && num == item->last_num)
1040                 folder->scan(folder, item);
1041
1042         return ret;
1043 }
1044
1045 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
1046 {
1047         gint ret = 0;
1048
1049         g_return_val_if_fail(item != NULL, -1);
1050
1051         while (msglist != NULL) {
1052                 MsgInfo *msginfo = (MsgInfo *)msglist->data;
1053
1054                 ret = folder_item_remove_msg(item, msginfo->msgnum);
1055                 if (ret != 0) break;
1056                 msglist = msglist->next;
1057         }
1058
1059         return ret;
1060 }
1061
1062 gint folder_item_remove_all_msg(FolderItem *item)
1063 {
1064         Folder *folder;
1065         gint result;
1066
1067         g_return_val_if_fail(item != NULL, -1);
1068
1069         folder = item->folder;
1070
1071         g_return_val_if_fail(folder->scan != NULL, -1);
1072         g_return_val_if_fail(folder->remove_all_msg != NULL, -1);
1073
1074         if (item->last_num < 0) folder->scan(folder, item);
1075
1076         result = folder->remove_all_msg(folder, item);
1077
1078         if (result == 0){
1079                 if (folder->finished_remove)
1080                         folder->finished_remove(folder, item);
1081         }
1082
1083         return result;
1084 }
1085
1086 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
1087 {
1088         Folder *folder;
1089
1090         g_return_val_if_fail(item != NULL, FALSE);
1091
1092         folder = item->folder;
1093
1094         g_return_val_if_fail(folder->is_msg_changed != NULL, -1);
1095
1096         return folder->is_msg_changed(folder, item, msginfo);
1097 }
1098
1099 gchar *folder_item_get_cache_file(FolderItem *item)
1100 {
1101         gchar *path;
1102         gchar *file;
1103
1104         g_return_val_if_fail(item != NULL, NULL);
1105         g_return_val_if_fail(item->path != NULL, NULL);
1106
1107         path = folder_item_get_path(item);
1108         g_return_val_if_fail(path != NULL, NULL);
1109         if (!is_dir_exist(path))
1110                 make_dir_hier(path);
1111         file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
1112         g_free(path);
1113
1114         return file;
1115 }
1116
1117 gchar *folder_item_get_mark_file(FolderItem *item)
1118 {
1119         gchar *path;
1120         gchar *file;
1121
1122         g_return_val_if_fail(item != NULL, NULL);
1123         g_return_val_if_fail(item->path != NULL, NULL);
1124
1125         path = folder_item_get_path(item);
1126         g_return_val_if_fail(path != NULL, NULL);
1127         if (!is_dir_exist(path))
1128                 make_dir_hier(path);
1129         file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
1130         g_free(path);
1131
1132         return file;
1133 }
1134
1135 static gboolean folder_build_tree(GNode *node, gpointer data)
1136 {
1137         Folder *folder = FOLDER(data);
1138         FolderItem *item;
1139         XMLNode *xmlnode;
1140         GList *list;
1141         SpecialFolderItemType stype = F_NORMAL;
1142         const gchar *name = NULL;
1143         const gchar *path = NULL;
1144         PrefsAccount *account = NULL;
1145         gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE, 
1146                  threaded = TRUE, ret_rcpt = FALSE, hidereadmsgs = FALSE;
1147         gint mtime = 0, new = 0, unread = 0, total = 0;
1148
1149         g_return_val_if_fail(node->data != NULL, FALSE);
1150         if (!node->parent) return FALSE;
1151
1152         xmlnode = node->data;
1153         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1154                 g_warning("tag name != \"folderitem\"\n");
1155                 return FALSE;
1156         }
1157
1158         list = xmlnode->tag->attr;
1159         for (; list != NULL; list = list->next) {
1160                 XMLAttr *attr = list->data;
1161
1162                 if (!attr || !attr->name || !attr->value) continue;
1163                 if (!strcmp(attr->name, "type")) {
1164                         if (!strcasecmp(attr->value, "normal"))
1165                                 stype = F_NORMAL;
1166                         else if (!strcasecmp(attr->value, "inbox"))
1167                                 stype = F_INBOX;
1168                         else if (!strcasecmp(attr->value, "outbox"))
1169                                 stype = F_OUTBOX;
1170                         else if (!strcasecmp(attr->value, "draft"))
1171                                 stype = F_DRAFT;
1172                         else if (!strcasecmp(attr->value, "queue"))
1173                                 stype = F_QUEUE;
1174                         else if (!strcasecmp(attr->value, "trash"))
1175                                 stype = F_TRASH;
1176                 } else if (!strcmp(attr->name, "name"))
1177                         name = attr->value;
1178                 else if (!strcmp(attr->name, "path"))
1179                         path = attr->value;
1180                 else if (!strcmp(attr->name, "account_id")) {
1181                         account = account_find_from_id(atoi(attr->value));
1182                         if (!account) g_warning("account_id: %s not found\n",
1183                                                 attr->value);
1184                 } else if (!strcmp(attr->name, "mtime"))
1185                         mtime = atoi(attr->value);
1186                 else if (!strcmp(attr->name, "new"))
1187                         new = atoi(attr->value);
1188                 else if (!strcmp(attr->name, "unread"))
1189                         unread = atoi(attr->value);
1190                 else if (!strcmp(attr->name, "total"))
1191                         total = atoi(attr->value);
1192                 else if (!strcmp(attr->name, "no_sub"))
1193                         no_sub = *attr->value == '1' ? TRUE : FALSE;
1194                 else if (!strcmp(attr->name, "no_select"))
1195                         no_select = *attr->value == '1' ? TRUE : FALSE;
1196                 else if (!strcmp(attr->name, "collapsed"))
1197                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1198                 else if (!strcmp(attr->name, "threaded"))
1199                         threaded =  *attr->value == '1' ? TRUE : FALSE;
1200                 else if (!strcmp(attr->name, "hidereadmsgs"))
1201                         hidereadmsgs =  *attr->value == '1' ? TRUE : FALSE;
1202                 else if (!strcmp(attr->name, "reqretrcpt"))
1203                         ret_rcpt =  *attr->value == '1' ? TRUE : FALSE;
1204         }
1205
1206         item = folder_item_new(name, path);
1207         item->stype = stype;
1208         item->account = account;
1209         item->mtime = mtime;
1210         item->new = new;
1211         item->unread = unread;
1212         item->total = total;
1213         item->no_sub = no_sub;
1214         item->no_select = no_select;
1215         item->collapsed = collapsed;
1216         item->threaded  = threaded;
1217         item->hide_read_msgs  = hidereadmsgs;
1218         item->ret_rcpt  = ret_rcpt;
1219         item->parent = FOLDER_ITEM(node->parent->data);
1220         item->folder = folder;
1221         switch (stype) {
1222         case F_INBOX:  folder->inbox  = item; break;
1223         case F_OUTBOX: folder->outbox = item; break;
1224         case F_DRAFT:  folder->draft  = item; break;
1225         case F_QUEUE:  folder->queue  = item; break;
1226         case F_TRASH:  folder->trash  = item; break;
1227         default:       break;
1228         }
1229
1230         prefs_folder_item_read_config(item);
1231
1232         node->data = item;
1233         xml_free_node(xmlnode);
1234
1235         return FALSE;
1236 }
1237
1238 static gboolean folder_read_folder_func(GNode *node, gpointer data)
1239 {
1240         Folder *folder;
1241         XMLNode *xmlnode;
1242         GList *list;
1243         FolderType type = F_UNKNOWN;
1244         const gchar *name = NULL;
1245         const gchar *path = NULL;
1246         PrefsAccount *account = NULL;
1247         gboolean collapsed = FALSE, threaded = TRUE, ret_rcpt = FALSE;
1248
1249         if (g_node_depth(node) != 2) return FALSE;
1250         g_return_val_if_fail(node->data != NULL, FALSE);
1251
1252         xmlnode = node->data;
1253         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1254                 g_warning("tag name != \"folder\"\n");
1255                 return TRUE;
1256         }
1257         g_node_unlink(node);
1258         list = xmlnode->tag->attr;
1259         for (; list != NULL; list = list->next) {
1260                 XMLAttr *attr = list->data;
1261
1262                 if (!attr || !attr->name || !attr->value) continue;
1263                 if (!strcmp(attr->name, "type")) {
1264                         if (!strcasecmp(attr->value, "mh"))
1265                                 type = F_MH;
1266                         else if (!strcasecmp(attr->value, "mbox"))
1267                                 type = F_MBOX;
1268                         else if (!strcasecmp(attr->value, "maildir"))
1269                                 type = F_MAILDIR;
1270                         else if (!strcasecmp(attr->value, "imap"))
1271                                 type = F_IMAP;
1272                         else if (!strcasecmp(attr->value, "news"))
1273                                 type = F_NEWS;
1274                 } else if (!strcmp(attr->name, "name"))
1275                         name = attr->value;
1276                 else if (!strcmp(attr->name, "path"))
1277                         path = attr->value;
1278                 else if (!strcmp(attr->name, "account_id")) {
1279                         account = account_find_from_id(atoi(attr->value));
1280                         if (!account) g_warning("account_id: %s not found\n",
1281                                                 attr->value);
1282                 } else if (!strcmp(attr->name, "collapsed"))
1283                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1284                 else if (!strcmp(attr->name, "threaded"))
1285                         threaded = *attr->value == '1' ? TRUE : FALSE;
1286                 else if (!strcmp(attr->name, "reqretrcpt"))
1287                         ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
1288         }
1289
1290         folder = folder_new(type, name, path);
1291         g_return_val_if_fail(folder != NULL, FALSE);
1292         folder->account = account;
1293         if (account && (type == F_IMAP || type == F_NEWS))
1294                 account->folder = REMOTE_FOLDER(folder);
1295         node->data = folder->node->data;
1296         g_node_destroy(folder->node);
1297         folder->node = node;
1298         folder_add(folder);
1299         FOLDER_ITEM(node->data)->collapsed = collapsed;
1300         FOLDER_ITEM(node->data)->threaded  = threaded;
1301         FOLDER_ITEM(node->data)->ret_rcpt  = ret_rcpt;
1302
1303         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1304                         folder_build_tree, folder);
1305
1306         return FALSE;
1307 }
1308
1309 static gchar *folder_get_list_path(void)
1310 {
1311         static gchar *filename = NULL;
1312
1313         if (!filename)
1314                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1315                                         FOLDER_LIST, NULL);
1316
1317         return filename;
1318 }
1319
1320 static void folder_write_list_recursive(GNode *node, gpointer data)
1321 {
1322         FILE *fp = (FILE *)data;
1323         FolderItem *item = FOLDER_ITEM(node->data);
1324         gint i, depth;
1325         static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1326                                            "news", "unknown"};
1327         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1328                                                  "draft", "queue", "trash"};
1329
1330         g_return_if_fail(item != NULL);
1331
1332         depth = g_node_depth(node);
1333         for (i = 0; i < depth; i++)
1334                 fputs("    ", fp);
1335         if (depth == 1) {
1336                 Folder *folder = item->folder;
1337
1338                 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
1339                 if (folder->name) {
1340                         fputs(" name=\"", fp);
1341                         xml_file_put_escape_str(fp, folder->name);
1342                         fputs("\"", fp);
1343                 }
1344                 if ((folder->type == F_MH) || (folder->type == F_MBOX)) {
1345                         fputs(" path=\"", fp);
1346                         xml_file_put_escape_str
1347                                 (fp, LOCAL_FOLDER(folder)->rootpath);
1348                         fputs("\"", fp);
1349                 }
1350                 if (folder->account)
1351                         fprintf(fp, " account_id=\"%d\"",
1352                                 folder->account->account_id);
1353                 if (item->collapsed && node->children)
1354                         fputs(" collapsed=\"1\"", fp);
1355                 if (item->ret_rcpt) 
1356                         fputs(" reqretrcpt=\"1\"", fp);
1357         } else {
1358                 fprintf(fp, "<folderitem type=\"%s\"",
1359                         folder_item_stype_str[item->stype]);
1360                 if (item->name) {
1361                         fputs(" name=\"", fp);
1362                         xml_file_put_escape_str(fp, item->name);
1363                         fputs("\"", fp);
1364                 }
1365                 if (item->path) {
1366                         fputs(" path=\"", fp);
1367                         xml_file_put_escape_str(fp, item->path);
1368                         fputs("\"", fp);
1369                 }
1370                 if (item->account)
1371                         fprintf(fp, " account_id=\"%d\"",
1372                                 item->account->account_id);
1373                 if (item->no_sub)
1374                         fputs(" no_sub=\"1\"", fp);
1375                 if (item->no_select)
1376                         fputs(" no_select=\"1\"", fp);
1377                 if (item->collapsed && node->children)
1378                         fputs(" collapsed=\"1\"", fp);
1379                 if (item->threaded)
1380                         fputs(" threaded=\"1\"", fp);
1381                 else
1382                         fputs(" threaded=\"0\"", fp);
1383                 if (item->hide_read_msgs)
1384                         fputs(" hidereadmsgs=\"1\"", fp);
1385                 else
1386                         fputs(" hidereadmsgs=\"0\"", fp);
1387                 if (item->ret_rcpt)
1388                         fputs(" reqretrcpt=\"1\"", fp);
1389                 fprintf(fp,
1390                         " mtime=\"%lu\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1391                         item->mtime, item->new, item->unread, item->total);
1392         }
1393
1394         if (node->children) {
1395                 GNode *child;
1396                 fputs(">\n", fp);
1397
1398                 child = node->children;
1399                 while (child) {
1400                         GNode *cur;
1401
1402                         cur = child;
1403                         child = cur->next;
1404                         folder_write_list_recursive(cur, data);
1405                 }
1406
1407                 for (i = 0; i < depth; i++)
1408                         fputs("    ", fp);
1409                 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1410         } else
1411                 fputs(" />\n", fp);
1412 }
1413
1414 static void folder_update_op_count_rec(GNode *node) {
1415         FolderItem *fitem = FOLDER_ITEM(node->data);
1416
1417         if (g_node_depth(node) > 0) {
1418                 if (fitem->op_count > 0) {
1419                         fitem->op_count = 0;
1420                         folderview_update_item(fitem, 0);
1421                 }
1422                 if (node->children) {
1423                         GNode *child;
1424
1425                         child = node->children;
1426                         while (child) {
1427                                 GNode *cur;
1428
1429                                 cur = child;
1430                                 child = cur->next;
1431                                 folder_update_op_count_rec(cur);
1432                         }
1433                 }
1434         }
1435 }
1436
1437 void folder_update_op_count() {
1438         GList *cur;
1439         Folder *folder;
1440
1441         for (cur = folder_list; cur != NULL; cur = cur->next) {
1442                 folder = cur->data;
1443                 folder_update_op_count_rec(folder->node);
1444         }
1445 }
1446
1447 typedef struct _type_str {
1448         gchar * str;
1449         gint type;
1450 } type_str;
1451
1452
1453 /*
1454 static gchar * folder_item_get_tree_identifier(FolderItem * item)
1455 {
1456         if (item->parent != NULL) {
1457                 gchar * path;
1458                 gchar * id;
1459
1460                 path = folder_item_get_tree_identifier(item->parent);
1461                 if (path == NULL)
1462                         return NULL;
1463
1464                 id = g_strconcat(path, "/", item->name, NULL);
1465                 g_free(path);
1466
1467                 return id;
1468         }
1469         else {
1470                 return g_strconcat("/", item->name, NULL);
1471         }
1472 }
1473 */
1474
1475 /* CLAWS: temporary local folder for filtering */
1476 static Folder *processing_folder;
1477 static FolderItem *processing_folder_item;
1478
1479 static void folder_create_processing_folder(void)
1480 {
1481 #define PROCESSING_FOLDER ".processing" 
1482         Folder     *tmpparent;
1483         FolderItem *tmpfolder;
1484         gchar      *tmpname;
1485
1486         tmpparent = folder_get_default_folder();
1487         g_assert(tmpparent);
1488         debug_print("tmpparentroot %s\n", LOCAL_FOLDER(tmpparent)->rootpath);
1489         if (LOCAL_FOLDER(tmpparent)->rootpath[0] == '/')
1490                 tmpname = g_strconcat(LOCAL_FOLDER(tmpparent)->rootpath,
1491                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
1492                                       NULL);
1493         else
1494                 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1495                                       LOCAL_FOLDER(tmpparent)->rootpath,
1496                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
1497                                       NULL);
1498
1499         processing_folder = folder_new(F_MH, "PROCESSING", LOCAL_FOLDER(tmpparent)->rootpath);
1500         g_assert(processing_folder);
1501
1502         if (!is_dir_exist(tmpname)) {
1503                 debug_print("*TMP* creating %s\n", tmpname);
1504                 processing_folder_item = processing_folder->create_folder(processing_folder,
1505                                                                           processing_folder->node->data,
1506                                                                           PROCESSING_FOLDER);
1507                 g_assert(processing_folder_item);                                                                         
1508         }
1509         else {
1510                 debug_print("*TMP* already created\n");
1511                 processing_folder_item = folder_item_new(".processing", ".processing");
1512                 g_assert(processing_folder_item);
1513                 folder_item_append(processing_folder->node->data, processing_folder_item);
1514         }
1515         g_free(tmpname);
1516 }
1517
1518 FolderItem *folder_get_default_processing(void)
1519 {
1520         if (!processing_folder_item) {
1521                 folder_create_processing_folder();
1522         }
1523         return processing_folder_item;
1524 }
1525
1526 /* folder_persist_prefs_new() - return hash table with persistent
1527  * settings (and folder name as key). 
1528  * (note that in claws other options are in the PREFS_FOLDER_ITEM_RC
1529  * file, so those don't need to be included in PersistPref yet) 
1530  */
1531 GHashTable *folder_persist_prefs_new(Folder *folder)
1532 {
1533         GHashTable *pptable;
1534
1535         g_return_val_if_fail(folder, NULL);
1536         pptable = g_hash_table_new(g_str_hash, g_str_equal);
1537         folder_get_persist_prefs_recursive(folder->node, pptable);
1538         return pptable;
1539 }
1540
1541 void folder_persist_prefs_free(GHashTable *pptable)
1542 {
1543         g_return_if_fail(pptable);
1544         g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
1545         g_hash_table_destroy(pptable);
1546 }
1547
1548 const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
1549 {
1550         if (pptable == NULL || name == NULL) return NULL;
1551         return g_hash_table_lookup(pptable, name);
1552 }
1553
1554 void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
1555 {
1556         const PersistPrefs *pp;
1557
1558         pp = folder_get_persist_prefs(pptable, item->path); 
1559         if (!pp) return;
1560
1561         /* CLAWS: since not all folder properties have been migrated to 
1562          * folderlist.xml, we need to call the old stuff first before
1563          * setting things that apply both to Main and Claws. */
1564         prefs_folder_item_read_config(item); 
1565          
1566         item->collapsed = pp->collapsed;
1567         item->threaded  = pp->threaded;
1568         item->ret_rcpt  = pp->ret_rcpt;
1569         item->hide_read_msgs = pp->hide_read_msgs;
1570 }
1571
1572 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
1573 {
1574         FolderItem *item = FOLDER_ITEM(node->data);
1575         PersistPrefs *pp;
1576         GNode *child, *cur;
1577
1578         g_return_if_fail(node != NULL);
1579         g_return_if_fail(item != NULL);
1580
1581         /* FIXME: item->path == NULL for top level folder, so this means that 
1582          * properties of MH folder root will not be stored. Not quite important, 
1583          * because the top level folder properties are not special anyway. */
1584         if (item->path) {
1585                 pp = g_new0(PersistPrefs, 1);
1586                 g_return_if_fail(pp != NULL);
1587                 pp->collapsed = item->collapsed;
1588                 pp->threaded  = item->threaded;
1589                 pp->ret_rcpt  = item->ret_rcpt; 
1590                 pp->hide_read_msgs = item->hide_read_msgs;
1591                 g_hash_table_insert(pptable, item->path, pp);
1592         }               
1593
1594         if (node->children) {
1595                 child = node->children;
1596                 while (child) {
1597                         cur = child;
1598                         child = cur->next;
1599                         folder_get_persist_prefs_recursive(cur, pptable);
1600                 }
1601         }       
1602 }
1603
1604 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
1605 {
1606         if (val) 
1607                 g_free(val);
1608         return TRUE;    
1609 }
1610