sync with 0.7.6cvs21
[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         FolderSortKey sort_key = SORT_BY_NONE;
1148         FolderSortType sort_type = SORT_ASCENDING;
1149         gint mtime = 0, new = 0, unread = 0, total = 0;
1150
1151         g_return_val_if_fail(node->data != NULL, FALSE);
1152         if (!node->parent) return FALSE;
1153
1154         xmlnode = node->data;
1155         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1156                 g_warning("tag name != \"folderitem\"\n");
1157                 return FALSE;
1158         }
1159
1160         list = xmlnode->tag->attr;
1161         for (; list != NULL; list = list->next) {
1162                 XMLAttr *attr = list->data;
1163
1164                 if (!attr || !attr->name || !attr->value) continue;
1165                 if (!strcmp(attr->name, "type")) {
1166                         if (!strcasecmp(attr->value, "normal"))
1167                                 stype = F_NORMAL;
1168                         else if (!strcasecmp(attr->value, "inbox"))
1169                                 stype = F_INBOX;
1170                         else if (!strcasecmp(attr->value, "outbox"))
1171                                 stype = F_OUTBOX;
1172                         else if (!strcasecmp(attr->value, "draft"))
1173                                 stype = F_DRAFT;
1174                         else if (!strcasecmp(attr->value, "queue"))
1175                                 stype = F_QUEUE;
1176                         else if (!strcasecmp(attr->value, "trash"))
1177                                 stype = F_TRASH;
1178                 } else if (!strcmp(attr->name, "name"))
1179                         name = attr->value;
1180                 else if (!strcmp(attr->name, "path"))
1181                         path = attr->value;
1182                 else if (!strcmp(attr->name, "account_id")) {
1183                         account = account_find_from_id(atoi(attr->value));
1184                         if (!account) g_warning("account_id: %s not found\n",
1185                                                 attr->value);
1186                 } else if (!strcmp(attr->name, "mtime"))
1187                         mtime = atoi(attr->value);
1188                 else if (!strcmp(attr->name, "new"))
1189                         new = atoi(attr->value);
1190                 else if (!strcmp(attr->name, "unread"))
1191                         unread = atoi(attr->value);
1192                 else if (!strcmp(attr->name, "total"))
1193                         total = atoi(attr->value);
1194                 else if (!strcmp(attr->name, "no_sub"))
1195                         no_sub = *attr->value == '1' ? TRUE : FALSE;
1196                 else if (!strcmp(attr->name, "no_select"))
1197                         no_select = *attr->value == '1' ? TRUE : FALSE;
1198                 else if (!strcmp(attr->name, "collapsed"))
1199                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1200                 else if (!strcmp(attr->name, "threaded"))
1201                         threaded =  *attr->value == '1' ? TRUE : FALSE;
1202                 else if (!strcmp(attr->name, "hidereadmsgs"))
1203                         hidereadmsgs =  *attr->value == '1' ? TRUE : FALSE;
1204                 else if (!strcmp(attr->name, "reqretrcpt"))
1205                         ret_rcpt =  *attr->value == '1' ? TRUE : FALSE;
1206                 else if (!strcmp(attr->name, "sort_key")) {
1207                         if (!strcmp(attr->value, "none"))
1208                                 sort_key = SORT_BY_NONE;
1209                         else if (!strcmp(attr->value, "number"))
1210                                 sort_key = SORT_BY_NUMBER;
1211                         else if (!strcmp(attr->value, "size"))
1212                                 sort_key = SORT_BY_SIZE;
1213                         else if (!strcmp(attr->value, "date"))
1214                                 sort_key = SORT_BY_DATE;
1215                         else if (!strcmp(attr->value, "from"))
1216                                 sort_key = SORT_BY_FROM;
1217                         else if (!strcmp(attr->value, "subject"))
1218                                 sort_key = SORT_BY_SUBJECT;
1219                         else if (!strcmp(attr->value, "score"))
1220                                 sort_key = SORT_BY_SCORE;
1221                         else if (!strcmp(attr->value, "label"))
1222                                 sort_key = SORT_BY_LABEL;
1223                         else if (!strcmp(attr->value, "mark"))
1224                                 sort_key = SORT_BY_MARK;
1225                         else if (!strcmp(attr->value, "unread"))
1226                                 sort_key = SORT_BY_UNREAD;
1227                         else if (!strcmp(attr->value, "mime"))
1228                                 sort_key = SORT_BY_MIME;
1229                 } else if (!strcmp(attr->name, "sort_type")) {
1230                         if (!strcmp(attr->value, "ascending"))
1231                                 sort_type = SORT_ASCENDING;
1232                         else
1233                                 sort_type = SORT_DESCENDING;
1234                 }
1235         }
1236
1237         item = folder_item_new(name, path);
1238         item->stype = stype;
1239         item->account = account;
1240         item->mtime = mtime;
1241         item->new = new;
1242         item->unread = unread;
1243         item->total = total;
1244         item->no_sub = no_sub;
1245         item->no_select = no_select;
1246         item->collapsed = collapsed;
1247         item->threaded  = threaded;
1248         item->hide_read_msgs  = hidereadmsgs;
1249         item->ret_rcpt  = ret_rcpt;
1250         item->sort_key  = sort_key;
1251         item->sort_type = sort_type;
1252         item->parent = FOLDER_ITEM(node->parent->data);
1253         item->folder = folder;
1254         switch (stype) {
1255         case F_INBOX:  folder->inbox  = item; break;
1256         case F_OUTBOX: folder->outbox = item; break;
1257         case F_DRAFT:  folder->draft  = item; break;
1258         case F_QUEUE:  folder->queue  = item; break;
1259         case F_TRASH:  folder->trash  = item; break;
1260         default:       break;
1261         }
1262
1263         prefs_folder_item_read_config(item);
1264
1265         node->data = item;
1266         xml_free_node(xmlnode);
1267
1268         return FALSE;
1269 }
1270
1271 static gboolean folder_read_folder_func(GNode *node, gpointer data)
1272 {
1273         Folder *folder;
1274         XMLNode *xmlnode;
1275         GList *list;
1276         FolderType type = F_UNKNOWN;
1277         const gchar *name = NULL;
1278         const gchar *path = NULL;
1279         PrefsAccount *account = NULL;
1280         gboolean collapsed = FALSE, threaded = TRUE, ret_rcpt = FALSE;
1281
1282         if (g_node_depth(node) != 2) return FALSE;
1283         g_return_val_if_fail(node->data != NULL, FALSE);
1284
1285         xmlnode = node->data;
1286         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1287                 g_warning("tag name != \"folder\"\n");
1288                 return TRUE;
1289         }
1290         g_node_unlink(node);
1291         list = xmlnode->tag->attr;
1292         for (; list != NULL; list = list->next) {
1293                 XMLAttr *attr = list->data;
1294
1295                 if (!attr || !attr->name || !attr->value) continue;
1296                 if (!strcmp(attr->name, "type")) {
1297                         if (!strcasecmp(attr->value, "mh"))
1298                                 type = F_MH;
1299                         else if (!strcasecmp(attr->value, "mbox"))
1300                                 type = F_MBOX;
1301                         else if (!strcasecmp(attr->value, "maildir"))
1302                                 type = F_MAILDIR;
1303                         else if (!strcasecmp(attr->value, "imap"))
1304                                 type = F_IMAP;
1305                         else if (!strcasecmp(attr->value, "news"))
1306                                 type = F_NEWS;
1307                 } else if (!strcmp(attr->name, "name"))
1308                         name = attr->value;
1309                 else if (!strcmp(attr->name, "path"))
1310                         path = attr->value;
1311                 else if (!strcmp(attr->name, "account_id")) {
1312                         account = account_find_from_id(atoi(attr->value));
1313                         if (!account) g_warning("account_id: %s not found\n",
1314                                                 attr->value);
1315                 } else if (!strcmp(attr->name, "collapsed"))
1316                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1317                 else if (!strcmp(attr->name, "threaded"))
1318                         threaded = *attr->value == '1' ? TRUE : FALSE;
1319                 else if (!strcmp(attr->name, "reqretrcpt"))
1320                         ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
1321         }
1322
1323         folder = folder_new(type, name, path);
1324         g_return_val_if_fail(folder != NULL, FALSE);
1325         folder->account = account;
1326         if (account && (type == F_IMAP || type == F_NEWS))
1327                 account->folder = REMOTE_FOLDER(folder);
1328         node->data = folder->node->data;
1329         g_node_destroy(folder->node);
1330         folder->node = node;
1331         folder_add(folder);
1332         FOLDER_ITEM(node->data)->collapsed = collapsed;
1333         FOLDER_ITEM(node->data)->threaded  = threaded;
1334         FOLDER_ITEM(node->data)->ret_rcpt  = ret_rcpt;
1335
1336         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1337                         folder_build_tree, folder);
1338
1339         return FALSE;
1340 }
1341
1342 static gchar *folder_get_list_path(void)
1343 {
1344         static gchar *filename = NULL;
1345
1346         if (!filename)
1347                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1348                                         FOLDER_LIST, NULL);
1349
1350         return filename;
1351 }
1352
1353 static void folder_write_list_recursive(GNode *node, gpointer data)
1354 {
1355         FILE *fp = (FILE *)data;
1356         FolderItem *item = FOLDER_ITEM(node->data);
1357         gint i, depth;
1358         static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1359                                            "news", "unknown"};
1360         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1361                                                  "draft", "queue", "trash"};
1362         static gchar *sort_key_str[] = {"none", "number", "size", "date",
1363                                         "from", "subject", "score", "label",
1364                                         "mark", "unread", "mime"};
1365
1366         g_return_if_fail(item != NULL);
1367
1368         depth = g_node_depth(node);
1369         for (i = 0; i < depth; i++)
1370                 fputs("    ", fp);
1371         if (depth == 1) {
1372                 Folder *folder = item->folder;
1373
1374                 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
1375                 if (folder->name) {
1376                         fputs(" name=\"", fp);
1377                         xml_file_put_escape_str(fp, folder->name);
1378                         fputs("\"", fp);
1379                 }
1380                 if ((folder->type == F_MH) || (folder->type == F_MBOX)) {
1381                         fputs(" path=\"", fp);
1382                         xml_file_put_escape_str
1383                                 (fp, LOCAL_FOLDER(folder)->rootpath);
1384                         fputs("\"", fp);
1385                 }
1386                 if (folder->account)
1387                         fprintf(fp, " account_id=\"%d\"",
1388                                 folder->account->account_id);
1389                 if (item->collapsed && node->children)
1390                         fputs(" collapsed=\"1\"", fp);
1391                 if (item->ret_rcpt) 
1392                         fputs(" reqretrcpt=\"1\"", fp);
1393         } else {
1394                 fprintf(fp, "<folderitem type=\"%s\"",
1395                         folder_item_stype_str[item->stype]);
1396                 if (item->name) {
1397                         fputs(" name=\"", fp);
1398                         xml_file_put_escape_str(fp, item->name);
1399                         fputs("\"", fp);
1400                 }
1401                 if (item->path) {
1402                         fputs(" path=\"", fp);
1403                         xml_file_put_escape_str(fp, item->path);
1404                         fputs("\"", fp);
1405                 }
1406                 if (item->account)
1407                         fprintf(fp, " account_id=\"%d\"",
1408                                 item->account->account_id);
1409                 if (item->no_sub)
1410                         fputs(" no_sub=\"1\"", fp);
1411                 if (item->no_select)
1412                         fputs(" no_select=\"1\"", fp);
1413                 if (item->collapsed && node->children)
1414                         fputs(" collapsed=\"1\"", fp);
1415                 if (item->threaded)
1416                         fputs(" threaded=\"1\"", fp);
1417                 else
1418                         fputs(" threaded=\"0\"", fp);
1419                 if (item->hide_read_msgs)
1420                         fputs(" hidereadmsgs=\"1\"", fp);
1421                 else
1422                         fputs(" hidereadmsgs=\"0\"", fp);
1423                 if (item->ret_rcpt)
1424                         fputs(" reqretrcpt=\"1\"", fp);
1425
1426                 if (item->sort_key != SORT_BY_NONE) {
1427                         fprintf(fp, " sort_key=\"%s\"",
1428                                 sort_key_str[item->sort_key]);
1429                         if (item->sort_type == SORT_ASCENDING)
1430                                 fprintf(fp, " sort_type=\"ascending\"");
1431                         else
1432                                 fprintf(fp, " sort_type=\"descending\"");
1433                 }
1434
1435                 fprintf(fp,
1436                         " mtime=\"%lu\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1437                         item->mtime, item->new, item->unread, item->total);
1438         }
1439
1440         if (node->children) {
1441                 GNode *child;
1442                 fputs(">\n", fp);
1443
1444                 child = node->children;
1445                 while (child) {
1446                         GNode *cur;
1447
1448                         cur = child;
1449                         child = cur->next;
1450                         folder_write_list_recursive(cur, data);
1451                 }
1452
1453                 for (i = 0; i < depth; i++)
1454                         fputs("    ", fp);
1455                 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1456         } else
1457                 fputs(" />\n", fp);
1458 }
1459
1460 static void folder_update_op_count_rec(GNode *node) {
1461         FolderItem *fitem = FOLDER_ITEM(node->data);
1462
1463         if (g_node_depth(node) > 0) {
1464                 if (fitem->op_count > 0) {
1465                         fitem->op_count = 0;
1466                         folderview_update_item(fitem, 0);
1467                 }
1468                 if (node->children) {
1469                         GNode *child;
1470
1471                         child = node->children;
1472                         while (child) {
1473                                 GNode *cur;
1474
1475                                 cur = child;
1476                                 child = cur->next;
1477                                 folder_update_op_count_rec(cur);
1478                         }
1479                 }
1480         }
1481 }
1482
1483 void folder_update_op_count() {
1484         GList *cur;
1485         Folder *folder;
1486
1487         for (cur = folder_list; cur != NULL; cur = cur->next) {
1488                 folder = cur->data;
1489                 folder_update_op_count_rec(folder->node);
1490         }
1491 }
1492
1493 typedef struct _type_str {
1494         gchar * str;
1495         gint type;
1496 } type_str;
1497
1498
1499 /*
1500 static gchar * folder_item_get_tree_identifier(FolderItem * item)
1501 {
1502         if (item->parent != NULL) {
1503                 gchar * path;
1504                 gchar * id;
1505
1506                 path = folder_item_get_tree_identifier(item->parent);
1507                 if (path == NULL)
1508                         return NULL;
1509
1510                 id = g_strconcat(path, "/", item->name, NULL);
1511                 g_free(path);
1512
1513                 return id;
1514         }
1515         else {
1516                 return g_strconcat("/", item->name, NULL);
1517         }
1518 }
1519 */
1520
1521 /* CLAWS: temporary local folder for filtering */
1522 static Folder *processing_folder;
1523 static FolderItem *processing_folder_item;
1524
1525 static void folder_create_processing_folder(void)
1526 {
1527 #define PROCESSING_FOLDER ".processing" 
1528         Folder     *tmpparent;
1529         FolderItem *tmpfolder;
1530         gchar      *tmpname;
1531
1532         tmpparent = folder_get_default_folder();
1533         g_assert(tmpparent);
1534         debug_print("tmpparentroot %s\n", LOCAL_FOLDER(tmpparent)->rootpath);
1535         if (LOCAL_FOLDER(tmpparent)->rootpath[0] == '/')
1536                 tmpname = g_strconcat(LOCAL_FOLDER(tmpparent)->rootpath,
1537                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
1538                                       NULL);
1539         else
1540                 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1541                                       LOCAL_FOLDER(tmpparent)->rootpath,
1542                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
1543                                       NULL);
1544
1545         processing_folder = folder_new(F_MH, "PROCESSING", LOCAL_FOLDER(tmpparent)->rootpath);
1546         g_assert(processing_folder);
1547
1548         if (!is_dir_exist(tmpname)) {
1549                 debug_print("*TMP* creating %s\n", tmpname);
1550                 processing_folder_item = processing_folder->create_folder(processing_folder,
1551                                                                           processing_folder->node->data,
1552                                                                           PROCESSING_FOLDER);
1553                 g_assert(processing_folder_item);                                                                         
1554         }
1555         else {
1556                 debug_print("*TMP* already created\n");
1557                 processing_folder_item = folder_item_new(".processing", ".processing");
1558                 g_assert(processing_folder_item);
1559                 folder_item_append(processing_folder->node->data, processing_folder_item);
1560         }
1561         g_free(tmpname);
1562 }
1563
1564 FolderItem *folder_get_default_processing(void)
1565 {
1566         if (!processing_folder_item) {
1567                 folder_create_processing_folder();
1568         }
1569         return processing_folder_item;
1570 }
1571
1572 /* folder_persist_prefs_new() - return hash table with persistent
1573  * settings (and folder name as key). 
1574  * (note that in claws other options are in the PREFS_FOLDER_ITEM_RC
1575  * file, so those don't need to be included in PersistPref yet) 
1576  */
1577 GHashTable *folder_persist_prefs_new(Folder *folder)
1578 {
1579         GHashTable *pptable;
1580
1581         g_return_val_if_fail(folder, NULL);
1582         pptable = g_hash_table_new(g_str_hash, g_str_equal);
1583         folder_get_persist_prefs_recursive(folder->node, pptable);
1584         return pptable;
1585 }
1586
1587 void folder_persist_prefs_free(GHashTable *pptable)
1588 {
1589         g_return_if_fail(pptable);
1590         g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
1591         g_hash_table_destroy(pptable);
1592 }
1593
1594 const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
1595 {
1596         if (pptable == NULL || name == NULL) return NULL;
1597         return g_hash_table_lookup(pptable, name);
1598 }
1599
1600 void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
1601 {
1602         const PersistPrefs *pp;
1603
1604         pp = folder_get_persist_prefs(pptable, item->path); 
1605         if (!pp) return;
1606
1607         /* CLAWS: since not all folder properties have been migrated to 
1608          * folderlist.xml, we need to call the old stuff first before
1609          * setting things that apply both to Main and Claws. */
1610         prefs_folder_item_read_config(item); 
1611          
1612         item->collapsed = pp->collapsed;
1613         item->threaded  = pp->threaded;
1614         item->ret_rcpt  = pp->ret_rcpt;
1615         item->hide_read_msgs = pp->hide_read_msgs;
1616 }
1617
1618 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
1619 {
1620         FolderItem *item = FOLDER_ITEM(node->data);
1621         PersistPrefs *pp;
1622         GNode *child, *cur;
1623
1624         g_return_if_fail(node != NULL);
1625         g_return_if_fail(item != NULL);
1626
1627         /* FIXME: item->path == NULL for top level folder, so this means that 
1628          * properties of MH folder root will not be stored. Not quite important, 
1629          * because the top level folder properties are not special anyway. */
1630         if (item->path) {
1631                 pp = g_new0(PersistPrefs, 1);
1632                 g_return_if_fail(pp != NULL);
1633                 pp->collapsed = item->collapsed;
1634                 pp->threaded  = item->threaded;
1635                 pp->ret_rcpt  = item->ret_rcpt; 
1636                 pp->hide_read_msgs = item->hide_read_msgs;
1637                 g_hash_table_insert(pptable, item->path, pp);
1638         }               
1639
1640         if (node->children) {
1641                 child = node->children;
1642                 while (child) {
1643                         cur = child;
1644                         child = cur->next;
1645                         folder_get_persist_prefs_recursive(cur, pptable);
1646                 }
1647         }       
1648 }
1649
1650 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
1651 {
1652         if (val) 
1653                 g_free(val);
1654         return TRUE;    
1655 }
1656