try to authenticate with CRAM-MD5
[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 void folder_item_scan(FolderItem *item)
750 {
751         Folder *folder;
752
753         g_return_if_fail(item != NULL);
754
755         folder = item->folder;
756
757         g_return_if_fail(folder->scan != NULL);
758
759         folder->scan(folder, item);
760 }
761
762 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
763                                           gpointer data)
764 {
765         folder_item_scan(FOLDER_ITEM(key));
766 }
767
768 void folder_item_scan_foreach(GHashTable *table)
769 {
770         g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
771 }
772
773 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
774 {
775         Folder *folder;
776
777         g_return_val_if_fail(item != NULL, NULL);
778
779         folder = item->folder;
780
781         g_return_val_if_fail(folder->scan != NULL, NULL);
782         g_return_val_if_fail(folder->fetch_msg != NULL, NULL);
783
784         if (item->last_num < 0) folder->scan(folder, item);
785
786         return folder->fetch_msg(folder, item, num);
787 }
788
789 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
790                          gboolean remove_source)
791 {
792         Folder *folder;
793         gint num;
794
795         g_return_val_if_fail(dest != NULL, -1);
796         g_return_val_if_fail(file != NULL, -1);
797
798         folder = dest->folder;
799
800         g_return_val_if_fail(folder->scan != NULL, -1);
801         g_return_val_if_fail(folder->add_msg != NULL, -1);
802
803         if (dest->last_num < 0) folder->scan(folder, dest);
804
805         num = folder->add_msg(folder, dest, file, remove_source);
806         if (num > 0) dest->last_num = num;
807
808         return num;
809 }
810
811 /*
812 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
813 {
814         Folder *folder;
815         gint num;
816
817         g_return_val_if_fail(dest != NULL, -1);
818         g_return_val_if_fail(msginfo != NULL, -1);
819
820         folder = dest->folder;
821         if (dest->last_num < 0) folder->scan(folder, dest);
822
823         num = folder->move_msg(folder, dest, msginfo);
824         if (num > 0) dest->last_num = num;
825
826         return num;
827 }
828 */
829
830 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
831 {
832         Folder *folder;
833         gint num;
834         gchar * filename;
835         Folder * src_folder;
836
837         g_return_val_if_fail(dest != NULL, -1);
838         g_return_val_if_fail(msginfo != NULL, -1);
839
840         folder = dest->folder;
841
842         g_return_val_if_fail(folder->scan != NULL, -1);
843         g_return_val_if_fail(folder->remove_msg != NULL, -1);
844         g_return_val_if_fail(folder->copy_msg != NULL, -1);
845
846         if (dest->last_num < 0) folder->scan(folder, dest);
847
848         src_folder = msginfo->folder->folder;
849
850         num = folder->copy_msg(folder, dest, msginfo);
851         
852         if (num != -1) {
853                 /* CLAWS */
854                 g_assert(src_folder);
855                 g_assert(src_folder->remove_msg);
856                 src_folder->remove_msg(src_folder,
857                                        msginfo->folder,
858                                        msginfo->msgnum);
859         }                                      
860         
861         if (folder->finished_copy)
862                 folder->finished_copy(folder, dest);
863
864         src_folder = msginfo->folder->folder;
865
866         if (msginfo->folder && src_folder->scan)
867                 src_folder->scan(src_folder, msginfo->folder);
868         folder->scan(folder, dest);
869
870         return num;
871 }
872
873 /*
874 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
875 {
876         Folder *folder;
877         gint num;
878
879         g_return_val_if_fail(dest != NULL, -1);
880         g_return_val_if_fail(msglist != NULL, -1);
881
882         folder = dest->folder;
883         if (dest->last_num < 0) folder->scan(folder, dest);
884
885         num = folder->move_msgs_with_dest(folder, dest, msglist);
886         if (num > 0) dest->last_num = num;
887         else dest->op_count = 0;
888
889         return num;
890 }
891 */
892
893 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
894 {
895         Folder *folder;
896         FolderItem * item;
897         GSList * l;
898         gchar * filename;
899
900         g_return_val_if_fail(dest != NULL, -1);
901         g_return_val_if_fail(msglist != NULL, -1);
902
903         folder = dest->folder;
904
905         g_return_val_if_fail(folder->scan != NULL, -1);
906         g_return_val_if_fail(folder->copy_msg != NULL, -1);
907         g_return_val_if_fail(folder->remove_msg != NULL, -1);
908
909         if (dest->last_num < 0) folder->scan(folder, dest);
910
911         item = NULL;
912         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
913                 MsgInfo * msginfo = (MsgInfo *) l->data;
914
915                 if (!item && msginfo->folder != NULL)
916                         item = msginfo->folder;
917
918                 if (folder->copy_msg(folder, dest, msginfo) != -1)
919                         item->folder->remove_msg(item->folder,
920                                                  msginfo->folder,
921                                                  msginfo->msgnum);
922         }
923
924         if (folder->finished_copy)
925                 folder->finished_copy(folder, dest);
926
927         if (item && item->folder->scan)
928                 item->folder->scan(item->folder, item);
929         folder->scan(folder, dest);
930
931         return dest->last_num;
932 }
933
934 /*
935 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
936 {
937         Folder *folder;
938         gint num;
939
940         g_return_val_if_fail(dest != NULL, -1);
941         g_return_val_if_fail(msginfo != NULL, -1);
942
943         folder = dest->folder;
944         if (dest->last_num < 0) folder->scan(folder, dest);
945
946         num = folder->copy_msg(folder, dest, msginfo);
947         if (num > 0) dest->last_num = num;
948
949         return num;
950 }
951 */
952
953 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
954 {
955         Folder *folder;
956         gint num;
957         gchar * filename;
958         Folder * src_folder;
959
960         g_return_val_if_fail(dest != NULL, -1);
961         g_return_val_if_fail(msginfo != NULL, -1);
962
963         folder = dest->folder;
964
965         g_return_val_if_fail(folder->scan != NULL, -1);
966         g_return_val_if_fail(folder->copy_msg != NULL, -1);
967
968         if (dest->last_num < 0) folder->scan(folder, dest);
969         
970         num = folder->copy_msg(folder, dest, msginfo);
971
972         if (folder->finished_copy)
973                 folder->finished_copy(folder, dest);
974
975         folder->scan(folder, dest);
976
977         return num;
978 }
979
980 /*
981 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
982 {
983         Folder *folder;
984         gint num;
985
986         g_return_val_if_fail(dest != NULL, -1);
987         g_return_val_if_fail(msglist != NULL, -1);
988
989         folder = dest->folder;
990         if (dest->last_num < 0) folder->scan(folder, dest);
991
992         num = folder->copy_msgs_with_dest(folder, dest, msglist);
993         if (num > 0) dest->last_num = num;
994         else dest->op_count = 0;
995
996         return num;
997 }
998 */
999
1000 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
1001 {
1002         Folder *folder;
1003         gint num;
1004         GSList * l;
1005         gchar * filename;
1006
1007         g_return_val_if_fail(dest != NULL, -1);
1008         g_return_val_if_fail(msglist != NULL, -1);
1009
1010         folder = dest->folder;
1011  
1012         g_return_val_if_fail(folder->scan != NULL, -1);
1013         g_return_val_if_fail(folder->copy_msg != NULL, -1);
1014
1015         if (dest->last_num < 0) folder->scan(folder, dest);
1016
1017         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1018                 MsgInfo * msginfo = (MsgInfo *) l->data;
1019
1020                 folder->copy_msg(folder, dest, msginfo);
1021         }
1022
1023         if (folder->finished_copy)
1024                 folder->finished_copy(folder, dest);
1025
1026         folder->scan(folder, dest);
1027
1028         return dest->last_num;
1029 }
1030
1031 gint folder_item_remove_msg(FolderItem *item, gint num)
1032 {
1033         Folder *folder;
1034         gint ret;
1035
1036         g_return_val_if_fail(item != NULL, -1);
1037
1038         folder = item->folder;
1039         if (item->last_num < 0) folder->scan(folder, item);
1040
1041         ret = folder->remove_msg(folder, item, num);
1042         if (ret == 0 && num == item->last_num)
1043                 folder->scan(folder, item);
1044
1045         return ret;
1046 }
1047
1048 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
1049 {
1050         gint ret = 0;
1051
1052         g_return_val_if_fail(item != NULL, -1);
1053
1054         while (msglist != NULL) {
1055                 MsgInfo *msginfo = (MsgInfo *)msglist->data;
1056
1057                 ret = folder_item_remove_msg(item, msginfo->msgnum);
1058                 if (ret != 0) break;
1059                 msglist = msglist->next;
1060         }
1061
1062         return ret;
1063 }
1064
1065 gint folder_item_remove_all_msg(FolderItem *item)
1066 {
1067         Folder *folder;
1068         gint result;
1069
1070         g_return_val_if_fail(item != NULL, -1);
1071
1072         folder = item->folder;
1073
1074         g_return_val_if_fail(folder->scan != NULL, -1);
1075         g_return_val_if_fail(folder->remove_all_msg != NULL, -1);
1076
1077         if (item->last_num < 0) folder->scan(folder, item);
1078
1079         result = folder->remove_all_msg(folder, item);
1080
1081         if (result == 0){
1082                 if (folder->finished_remove)
1083                         folder->finished_remove(folder, item);
1084         }
1085
1086         return result;
1087 }
1088
1089 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
1090 {
1091         Folder *folder;
1092
1093         g_return_val_if_fail(item != NULL, FALSE);
1094
1095         folder = item->folder;
1096
1097         g_return_val_if_fail(folder->is_msg_changed != NULL, -1);
1098
1099         return folder->is_msg_changed(folder, item, msginfo);
1100 }
1101
1102 gchar *folder_item_get_cache_file(FolderItem *item)
1103 {
1104         gchar *path;
1105         gchar *file;
1106
1107         g_return_val_if_fail(item != NULL, NULL);
1108         g_return_val_if_fail(item->path != NULL, NULL);
1109
1110         path = folder_item_get_path(item);
1111         g_return_val_if_fail(path != NULL, NULL);
1112         if (!is_dir_exist(path))
1113                 make_dir_hier(path);
1114         file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
1115         g_free(path);
1116
1117         return file;
1118 }
1119
1120 gchar *folder_item_get_mark_file(FolderItem *item)
1121 {
1122         gchar *path;
1123         gchar *file;
1124
1125         g_return_val_if_fail(item != NULL, NULL);
1126         g_return_val_if_fail(item->path != NULL, NULL);
1127
1128         path = folder_item_get_path(item);
1129         g_return_val_if_fail(path != NULL, NULL);
1130         if (!is_dir_exist(path))
1131                 make_dir_hier(path);
1132         file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
1133         g_free(path);
1134
1135         return file;
1136 }
1137
1138 static gboolean folder_build_tree(GNode *node, gpointer data)
1139 {
1140         Folder *folder = FOLDER(data);
1141         FolderItem *item;
1142         XMLNode *xmlnode;
1143         GList *list;
1144         SpecialFolderItemType stype = F_NORMAL;
1145         const gchar *name = NULL;
1146         const gchar *path = NULL;
1147         PrefsAccount *account = NULL;
1148         gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE, 
1149                  threaded = TRUE, ret_rcpt = FALSE, hidereadmsgs = FALSE;
1150         gint mtime = 0, new = 0, unread = 0, total = 0;
1151
1152         g_return_val_if_fail(node->data != NULL, FALSE);
1153         if (!node->parent) return FALSE;
1154
1155         xmlnode = node->data;
1156         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1157                 g_warning("tag name != \"folderitem\"\n");
1158                 return FALSE;
1159         }
1160
1161         list = xmlnode->tag->attr;
1162         for (; list != NULL; list = list->next) {
1163                 XMLAttr *attr = list->data;
1164
1165                 if (!attr || !attr->name || !attr->value) continue;
1166                 if (!strcmp(attr->name, "type")) {
1167                         if (!strcasecmp(attr->value, "normal"))
1168                                 stype = F_NORMAL;
1169                         else if (!strcasecmp(attr->value, "inbox"))
1170                                 stype = F_INBOX;
1171                         else if (!strcasecmp(attr->value, "outbox"))
1172                                 stype = F_OUTBOX;
1173                         else if (!strcasecmp(attr->value, "draft"))
1174                                 stype = F_DRAFT;
1175                         else if (!strcasecmp(attr->value, "queue"))
1176                                 stype = F_QUEUE;
1177                         else if (!strcasecmp(attr->value, "trash"))
1178                                 stype = F_TRASH;
1179                 } else if (!strcmp(attr->name, "name"))
1180                         name = attr->value;
1181                 else if (!strcmp(attr->name, "path"))
1182                         path = attr->value;
1183                 else if (!strcmp(attr->name, "account_id")) {
1184                         account = account_find_from_id(atoi(attr->value));
1185                         if (!account) g_warning("account_id: %s not found\n",
1186                                                 attr->value);
1187                 } else if (!strcmp(attr->name, "mtime"))
1188                         mtime = atoi(attr->value);
1189                 else if (!strcmp(attr->name, "new"))
1190                         new = atoi(attr->value);
1191                 else if (!strcmp(attr->name, "unread"))
1192                         unread = atoi(attr->value);
1193                 else if (!strcmp(attr->name, "total"))
1194                         total = atoi(attr->value);
1195                 else if (!strcmp(attr->name, "no_sub"))
1196                         no_sub = *attr->value == '1' ? TRUE : FALSE;
1197                 else if (!strcmp(attr->name, "no_select"))
1198                         no_select = *attr->value == '1' ? TRUE : FALSE;
1199                 else if (!strcmp(attr->name, "collapsed"))
1200                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1201                 else if (!strcmp(attr->name, "threaded"))
1202                         threaded =  *attr->value == '1' ? TRUE : FALSE;
1203                 else if (!strcmp(attr->name, "hidereadmsgs"))
1204                         hidereadmsgs =  *attr->value == '1' ? TRUE : FALSE;
1205                 else if (!strcmp(attr->name, "reqretrcpt"))
1206                         ret_rcpt =  *attr->value == '1' ? TRUE : FALSE;
1207         }
1208
1209         item = folder_item_new(name, path);
1210         item->stype = stype;
1211         item->account = account;
1212         item->mtime = mtime;
1213         item->new = new;
1214         item->unread = unread;
1215         item->total = total;
1216         item->no_sub = no_sub;
1217         item->no_select = no_select;
1218         item->collapsed = collapsed;
1219         item->threaded  = threaded;
1220         item->hide_read_msgs  = hidereadmsgs;
1221         item->ret_rcpt  = ret_rcpt;
1222         item->parent = FOLDER_ITEM(node->parent->data);
1223         item->folder = folder;
1224         switch (stype) {
1225         case F_INBOX:  folder->inbox  = item; break;
1226         case F_OUTBOX: folder->outbox = item; break;
1227         case F_DRAFT:  folder->draft  = item; break;
1228         case F_QUEUE:  folder->queue  = item; break;
1229         case F_TRASH:  folder->trash  = item; break;
1230         default:       break;
1231         }
1232
1233         prefs_folder_item_read_config(item);
1234
1235         node->data = item;
1236         xml_free_node(xmlnode);
1237
1238         return FALSE;
1239 }
1240
1241 static gboolean folder_read_folder_func(GNode *node, gpointer data)
1242 {
1243         Folder *folder;
1244         XMLNode *xmlnode;
1245         GList *list;
1246         FolderType type = F_UNKNOWN;
1247         const gchar *name = NULL;
1248         const gchar *path = NULL;
1249         PrefsAccount *account = NULL;
1250         gboolean collapsed = FALSE, threaded = TRUE, ret_rcpt = FALSE;
1251
1252         if (g_node_depth(node) != 2) return FALSE;
1253         g_return_val_if_fail(node->data != NULL, FALSE);
1254
1255         xmlnode = node->data;
1256         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1257                 g_warning("tag name != \"folder\"\n");
1258                 return TRUE;
1259         }
1260         g_node_unlink(node);
1261         list = xmlnode->tag->attr;
1262         for (; list != NULL; list = list->next) {
1263                 XMLAttr *attr = list->data;
1264
1265                 if (!attr || !attr->name || !attr->value) continue;
1266                 if (!strcmp(attr->name, "type")) {
1267                         if (!strcasecmp(attr->value, "mh"))
1268                                 type = F_MH;
1269                         else if (!strcasecmp(attr->value, "mbox"))
1270                                 type = F_MBOX;
1271                         else if (!strcasecmp(attr->value, "maildir"))
1272                                 type = F_MAILDIR;
1273                         else if (!strcasecmp(attr->value, "imap"))
1274                                 type = F_IMAP;
1275                         else if (!strcasecmp(attr->value, "news"))
1276                                 type = F_NEWS;
1277                 } else if (!strcmp(attr->name, "name"))
1278                         name = attr->value;
1279                 else if (!strcmp(attr->name, "path"))
1280                         path = attr->value;
1281                 else if (!strcmp(attr->name, "account_id")) {
1282                         account = account_find_from_id(atoi(attr->value));
1283                         if (!account) g_warning("account_id: %s not found\n",
1284                                                 attr->value);
1285                 } else if (!strcmp(attr->name, "collapsed"))
1286                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1287                 else if (!strcmp(attr->name, "threaded"))
1288                         threaded = *attr->value == '1' ? TRUE : FALSE;
1289                 else if (!strcmp(attr->name, "reqretrcpt"))
1290                         ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
1291         }
1292
1293         folder = folder_new(type, name, path);
1294         g_return_val_if_fail(folder != NULL, FALSE);
1295         folder->account = account;
1296         if (account && (type == F_IMAP || type == F_NEWS))
1297                 account->folder = REMOTE_FOLDER(folder);
1298         node->data = folder->node->data;
1299         g_node_destroy(folder->node);
1300         folder->node = node;
1301         folder_add(folder);
1302         FOLDER_ITEM(node->data)->collapsed = collapsed;
1303         FOLDER_ITEM(node->data)->threaded  = threaded;
1304         FOLDER_ITEM(node->data)->ret_rcpt  = ret_rcpt;
1305
1306         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1307                         folder_build_tree, folder);
1308
1309         return FALSE;
1310 }
1311
1312 static gchar *folder_get_list_path(void)
1313 {
1314         static gchar *filename = NULL;
1315
1316         if (!filename)
1317                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1318                                         FOLDER_LIST, NULL);
1319
1320         return filename;
1321 }
1322
1323 static void folder_write_list_recursive(GNode *node, gpointer data)
1324 {
1325         FILE *fp = (FILE *)data;
1326         FolderItem *item = FOLDER_ITEM(node->data);
1327         gint i, depth;
1328         static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1329                                            "news", "unknown"};
1330         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1331                                                  "draft", "queue", "trash"};
1332
1333         g_return_if_fail(item != NULL);
1334
1335         depth = g_node_depth(node);
1336         for (i = 0; i < depth; i++)
1337                 fputs("    ", fp);
1338         if (depth == 1) {
1339                 Folder *folder = item->folder;
1340
1341                 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
1342                 if (folder->name) {
1343                         fputs(" name=\"", fp);
1344                         xml_file_put_escape_str(fp, folder->name);
1345                         fputs("\"", fp);
1346                 }
1347                 if ((folder->type == F_MH) || (folder->type == F_MBOX)) {
1348                         fputs(" path=\"", fp);
1349                         xml_file_put_escape_str
1350                                 (fp, LOCAL_FOLDER(folder)->rootpath);
1351                         fputs("\"", fp);
1352                 }
1353                 if (folder->account)
1354                         fprintf(fp, " account_id=\"%d\"",
1355                                 folder->account->account_id);
1356                 if (item->collapsed && node->children)
1357                         fputs(" collapsed=\"1\"", fp);
1358                 if (item->ret_rcpt) 
1359                         fputs(" reqretrcpt=\"1\"", fp);
1360         } else {
1361                 fprintf(fp, "<folderitem type=\"%s\"",
1362                         folder_item_stype_str[item->stype]);
1363                 if (item->name) {
1364                         fputs(" name=\"", fp);
1365                         xml_file_put_escape_str(fp, item->name);
1366                         fputs("\"", fp);
1367                 }
1368                 if (item->path) {
1369                         fputs(" path=\"", fp);
1370                         xml_file_put_escape_str(fp, item->path);
1371                         fputs("\"", fp);
1372                 }
1373                 if (item->account)
1374                         fprintf(fp, " account_id=\"%d\"",
1375                                 item->account->account_id);
1376                 if (item->no_sub)
1377                         fputs(" no_sub=\"1\"", fp);
1378                 if (item->no_select)
1379                         fputs(" no_select=\"1\"", fp);
1380                 if (item->collapsed && node->children)
1381                         fputs(" collapsed=\"1\"", fp);
1382                 if (item->threaded)
1383                         fputs(" threaded=\"1\"", fp);
1384                 else
1385                         fputs(" threaded=\"0\"", fp);
1386                 if (item->hide_read_msgs)
1387                         fputs(" hidereadmsgs=\"1\"", fp);
1388                 else
1389                         fputs(" hidereadmsgs=\"0\"", fp);
1390                 if (item->ret_rcpt)
1391                         fputs(" reqretrcpt=\"1\"", fp);
1392                 fprintf(fp,
1393                         " mtime=\"%lu\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1394                         item->mtime, item->new, item->unread, item->total);
1395         }
1396
1397         if (node->children) {
1398                 GNode *child;
1399                 fputs(">\n", fp);
1400
1401                 child = node->children;
1402                 while (child) {
1403                         GNode *cur;
1404
1405                         cur = child;
1406                         child = cur->next;
1407                         folder_write_list_recursive(cur, data);
1408                 }
1409
1410                 for (i = 0; i < depth; i++)
1411                         fputs("    ", fp);
1412                 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1413         } else
1414                 fputs(" />\n", fp);
1415 }
1416
1417 static void folder_update_op_count_rec(GNode *node) {
1418         FolderItem *fitem = FOLDER_ITEM(node->data);
1419
1420         if (g_node_depth(node) > 0) {
1421                 if (fitem->op_count > 0) {
1422                         fitem->op_count = 0;
1423                         folderview_update_item(fitem, 0);
1424                 }
1425                 if (node->children) {
1426                         GNode *child;
1427
1428                         child = node->children;
1429                         while (child) {
1430                                 GNode *cur;
1431
1432                                 cur = child;
1433                                 child = cur->next;
1434                                 folder_update_op_count_rec(cur);
1435                         }
1436                 }
1437         }
1438 }
1439
1440 void folder_update_op_count() {
1441         GList *cur;
1442         Folder *folder;
1443
1444         for (cur = folder_list; cur != NULL; cur = cur->next) {
1445                 folder = cur->data;
1446                 folder_update_op_count_rec(folder->node);
1447         }
1448 }
1449
1450 typedef struct _type_str {
1451         gchar * str;
1452         gint type;
1453 } type_str;
1454
1455
1456 /*
1457 static gchar * folder_item_get_tree_identifier(FolderItem * item)
1458 {
1459         if (item->parent != NULL) {
1460                 gchar * path;
1461                 gchar * id;
1462
1463                 path = folder_item_get_tree_identifier(item->parent);
1464                 if (path == NULL)
1465                         return NULL;
1466
1467                 id = g_strconcat(path, "/", item->name, NULL);
1468                 g_free(path);
1469
1470                 return id;
1471         }
1472         else {
1473                 return g_strconcat("/", item->name, NULL);
1474         }
1475 }
1476 */
1477
1478 /* CLAWS: temporary local folder for filtering */
1479 static Folder *processing_folder;
1480 static FolderItem *processing_folder_item;
1481
1482 static void folder_create_processing_folder(void)
1483 {
1484 #define PROCESSING_FOLDER ".processing" 
1485         Folder     *tmpparent;
1486         FolderItem *tmpfolder;
1487         gchar      *tmpname;
1488
1489         tmpparent = folder_get_default_folder();
1490         g_assert(tmpparent);
1491         debug_print("tmpparentroot %s\n", LOCAL_FOLDER(tmpparent)->rootpath);
1492         if (LOCAL_FOLDER(tmpparent)->rootpath[0] == '/')
1493                 tmpname = g_strconcat(LOCAL_FOLDER(tmpparent)->rootpath,
1494                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
1495                                       NULL);
1496         else
1497                 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1498                                       LOCAL_FOLDER(tmpparent)->rootpath,
1499                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
1500                                       NULL);
1501
1502         processing_folder = folder_new(F_MH, "PROCESSING", LOCAL_FOLDER(tmpparent)->rootpath);
1503         g_assert(processing_folder);
1504
1505         if (!is_dir_exist(tmpname)) {
1506                 debug_print("*TMP* creating %s\n", tmpname);
1507                 processing_folder_item = processing_folder->create_folder(processing_folder,
1508                                                                           processing_folder->node->data,
1509                                                                           PROCESSING_FOLDER);
1510                 g_assert(processing_folder_item);                                                                         
1511         }
1512         else {
1513                 debug_print("*TMP* already created\n");
1514                 processing_folder_item = folder_item_new(".processing", ".processing");
1515                 g_assert(processing_folder_item);
1516                 folder_item_append(processing_folder->node->data, processing_folder_item);
1517         }
1518         g_free(tmpname);
1519 }
1520
1521 FolderItem *folder_get_default_processing(void)
1522 {
1523         if (!processing_folder_item) {
1524                 folder_create_processing_folder();
1525         }
1526         return processing_folder_item;
1527 }
1528
1529 /* folder_persist_prefs_new() - return hash table with persistent
1530  * settings (and folder name as key). 
1531  * (note that in claws other options are in the PREFS_FOLDER_ITEM_RC
1532  * file, so those don't need to be included in PersistPref yet) 
1533  */
1534 GHashTable *folder_persist_prefs_new(Folder *folder)
1535 {
1536         GHashTable *pptable;
1537
1538         g_return_val_if_fail(folder, NULL);
1539         pptable = g_hash_table_new(g_str_hash, g_str_equal);
1540         folder_get_persist_prefs_recursive(folder->node, pptable);
1541         return pptable;
1542 }
1543
1544 void folder_persist_prefs_free(GHashTable *pptable)
1545 {
1546         g_return_if_fail(pptable);
1547         g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
1548         g_hash_table_destroy(pptable);
1549 }
1550
1551 const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
1552 {
1553         if (pptable == NULL || name == NULL) return NULL;
1554         return g_hash_table_lookup(pptable, name);
1555 }
1556
1557 void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
1558 {
1559         const PersistPrefs *pp;
1560
1561         pp = folder_get_persist_prefs(pptable, item->path); 
1562         if (!pp) return;
1563
1564         /* CLAWS: since not all folder properties have been migrated to 
1565          * folderlist.xml, we need to call the old stuff first before
1566          * setting things that apply both to Main and Claws. */
1567         prefs_folder_item_read_config(item); 
1568          
1569         item->collapsed = pp->collapsed;
1570         item->threaded  = pp->threaded;
1571         item->ret_rcpt  = pp->ret_rcpt;
1572         item->hide_read_msgs = pp->hide_read_msgs;
1573 }
1574
1575 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
1576 {
1577         FolderItem *item = FOLDER_ITEM(node->data);
1578         PersistPrefs *pp;
1579         GNode *child, *cur;
1580
1581         g_return_if_fail(node != NULL);
1582         g_return_if_fail(item != NULL);
1583
1584         /* FIXME: item->path == NULL for top level folder, so this means that 
1585          * properties of MH folder root will not be stored. Not quite important, 
1586          * because the top level folder properties are not special anyway. */
1587         if (item->path) {
1588                 pp = g_new0(PersistPrefs, 1);
1589                 g_return_if_fail(pp != NULL);
1590                 pp->collapsed = item->collapsed;
1591                 pp->threaded  = item->threaded;
1592                 pp->ret_rcpt  = item->ret_rcpt; 
1593                 pp->hide_read_msgs = item->hide_read_msgs;
1594                 g_hash_table_insert(pptable, item->path, pp);
1595         }               
1596
1597         if (node->children) {
1598                 child = node->children;
1599                 while (child) {
1600                         cur = child;
1601                         child = cur->next;
1602                         folder_get_persist_prefs_recursive(cur, pptable);
1603                 }
1604         }       
1605 }
1606
1607 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
1608 {
1609         if (val) 
1610                 g_free(val);
1611         return TRUE;    
1612 }
1613