17ea90c84dd3458d7ad9a24e9859186e6f9c2606
[claws.git] / src / folder.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include "intl.h"
32 #include "folder.h"
33 #include "folderview.h"
34 #include "session.h"
35 #include "imap.h"
36 #include "news.h"
37 #include "mh.h"
38 #include "mbox_folder.h"
39 #include "utils.h"
40 #include "xml.h"
41 #include "codeconv.h"
42 #include "prefs.h"
43 #include "account.h"
44 #include "prefs_account.h"
45 #include "prefs_folder_item.h"
46
47 static GList *folder_list = NULL;
48
49 static void folder_init         (Folder         *folder,
50                                  const gchar    *name);
51
52 static gboolean folder_read_folder_func (GNode          *node,
53                                          gpointer        data);
54 static gchar *folder_get_list_path      (void);
55 static void folder_write_list_recursive (GNode          *node,
56                                          gpointer        data);
57 static void folder_update_op_count_rec  (GNode          *node);
58
59
60 static void folder_get_persist_prefs_recursive
61                                         (GNode *node, GHashTable *pptable);
62 static gboolean persist_prefs_free      (gpointer key, gpointer val, gpointer data);
63
64
65 Folder *folder_new(FolderType type, const gchar *name, const gchar *path)
66 {
67         Folder *folder = NULL;
68
69         name = name ? name : path;
70         switch (type) {
71         case F_MBOX:
72                 folder = mbox_folder_new(name, path);
73                 break;
74         case F_MH:
75                 folder = mh_folder_new(name, path);
76                 break;
77         case F_IMAP:
78                 folder = imap_folder_new(name, path);
79                 break;
80         case F_NEWS:
81                 folder = news_folder_new(name, path);
82                 break;
83         default:
84                 return NULL;
85         }
86
87         return folder;
88 }
89
90 static void folder_init(Folder *folder, const gchar *name)
91 {
92         FolderItem *item;
93
94         g_return_if_fail(folder != NULL);
95
96         folder_set_name(folder, name);
97         folder->type = F_UNKNOWN;
98         folder->account = NULL;
99         folder->inbox = NULL;
100         folder->outbox = NULL;
101         folder->draft = NULL;
102         folder->queue = NULL;
103         folder->trash = NULL;
104         folder->ui_func = NULL;
105         folder->ui_func_data = NULL;
106         item = folder_item_new(name, NULL);
107         item->folder = folder;
108         folder->node = g_node_new(item);
109         folder->data = NULL;
110 }
111
112 void folder_local_folder_init(Folder *folder, const gchar *name,
113                               const gchar *path)
114 {
115         folder_init(folder, name);
116         LOCAL_FOLDER(folder)->rootpath = g_strdup(path);
117 }
118
119 void folder_remote_folder_init(Folder *folder, const gchar *name,
120                                const gchar *path)
121 {
122         folder_init(folder, name);
123         REMOTE_FOLDER(folder)->session = NULL;
124 }
125
126 void folder_destroy(Folder *folder)
127 {
128         g_return_if_fail(folder != NULL);
129
130         switch (folder->type) {
131         case F_MBOX:
132                 mbox_folder_destroy(MBOX_FOLDER(folder));
133         case F_MH:
134                 mh_folder_destroy(MH_FOLDER(folder));
135                 break;
136         case F_IMAP:
137                 imap_folder_destroy(IMAP_FOLDER(folder));
138                 break;
139         case F_NEWS:
140                 news_folder_destroy(NEWS_FOLDER(folder));
141                 break;
142         default:
143                 break;
144         }
145
146         folder_list = g_list_remove(folder_list, folder);
147
148         folder_tree_destroy(folder);
149         g_free(folder->name);
150         g_free(folder);
151 }
152
153 void folder_local_folder_destroy(LocalFolder *lfolder)
154 {
155         g_return_if_fail(lfolder != NULL);
156
157         g_free(lfolder->rootpath);
158 }
159
160 void folder_remote_folder_destroy(RemoteFolder *rfolder)
161 {
162         g_return_if_fail(rfolder != NULL);
163
164         if (rfolder->session)
165                 session_destroy(rfolder->session);
166 }
167
168 #if 0
169 Folder *mbox_folder_new(const gchar *name, const gchar *path)
170 {
171         /* not yet implemented */
172         return NULL;
173 }
174
175 Folder *maildir_folder_new(const gchar *name, const gchar *path)
176 {
177         /* not yet implemented */
178         return NULL;
179 }
180 #endif
181
182 FolderItem *folder_item_new(const gchar *name, const gchar *path)
183 {
184         FolderItem *item;
185
186         item = g_new0(FolderItem, 1);
187
188         item->stype = F_NORMAL;
189         item->name = g_strdup(name);
190         item->path = g_strdup(path);
191         item->account = NULL;
192         item->mtime = 0;
193         item->new = 0;
194         item->unread = 0;
195         item->total = 0;
196         item->last_num = -1;
197         item->no_sub = FALSE;
198         item->no_select = FALSE;
199         item->collapsed = FALSE;
200         item->threaded  = TRUE;
201         item->ret_rcpt  = FALSE;
202         item->opened    = FALSE;
203         item->parent = NULL;
204         item->folder = NULL;
205         item->mark_queue = NULL;
206         item->data = NULL;
207
208         item->prefs = prefs_folder_item_new();
209
210         return item;
211 }
212
213 void folder_item_append(FolderItem *parent, FolderItem *item)
214 {
215         GNode *node;
216
217         g_return_if_fail(parent != NULL);
218         g_return_if_fail(parent->folder != NULL);
219         g_return_if_fail(item != NULL);
220
221         node = parent->folder->node;
222         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, parent);
223         g_return_if_fail(node != NULL);
224
225         item->parent = parent;
226         item->folder = parent->folder;
227         g_node_append_data(node, item);
228 }
229
230 void folder_item_remove(FolderItem *item)
231 {
232         GNode *node;
233
234         g_return_if_fail(item != NULL);
235         g_return_if_fail(item->folder != NULL);
236
237         node = item->folder->node;
238         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
239         g_return_if_fail(node != NULL);
240
241         /* TODO: free all FolderItem's first */
242         if (item->folder->node == node)
243                 item->folder->node = NULL;
244         g_node_destroy(node);
245 }
246
247 void folder_item_destroy(FolderItem *item)
248 {
249         g_return_if_fail(item != NULL);
250
251         g_free(item->name);
252         g_free(item->path);
253         g_free(item);
254 }
255
256 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
257 {
258         g_return_if_fail(folder != NULL);
259
260         folder->ui_func = func;
261         folder->ui_func_data = data;
262 }
263
264 void folder_set_name(Folder *folder, const gchar *name)
265 {
266         g_return_if_fail(folder != NULL);
267
268         g_free(folder->name);
269         folder->name = name ? g_strdup(name) : NULL;
270         if (folder->node && folder->node->data) {
271                 FolderItem *item = (FolderItem *)folder->node->data;
272
273                 g_free(item->name);
274                 item->name = name ? g_strdup(name) : NULL;
275         }
276 }
277
278 void folder_tree_destroy(Folder *folder)
279 {
280         /* TODO: destroy all FolderItem before */
281         g_node_destroy(folder->node);
282
283         folder->inbox = NULL;
284         folder->outbox = NULL;
285         folder->draft = NULL;
286         folder->queue = NULL;
287         folder->trash = NULL;
288         folder->node = NULL;
289 }
290
291 void folder_add(Folder *folder)
292 {
293         Folder *cur_folder;
294         GList *cur;
295         gint i;
296
297         g_return_if_fail(folder != NULL);
298
299         for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
300                 cur_folder = FOLDER(cur->data);
301                 if (folder->type == F_MH) {
302                         if (cur_folder->type != F_MH) break;
303                 } else if (folder->type == F_MBOX) {
304                         if (cur_folder->type != F_MH &&
305                             cur_folder->type != F_MBOX) break;
306                 } else if (folder->type == F_IMAP) {
307                         if (cur_folder->type != F_MH &&
308                             cur_folder->type != F_MBOX &&
309                             cur_folder->type != F_IMAP) break;
310                 } else if (folder->type == F_NEWS) {
311                         if (cur_folder->type != F_MH &&
312                             cur_folder->type != F_MBOX &&
313                             cur_folder->type != F_IMAP &&
314                             cur_folder->type != F_NEWS) break;
315                 }
316         }
317
318         folder_list = g_list_insert(folder_list, folder, i);
319 }
320
321 GList *folder_get_list(void)
322 {
323         return folder_list;
324 }
325
326 gint folder_read_list(void)
327 {
328         GNode *node;
329         XMLNode *xmlnode;
330         gchar *path;
331
332         path = folder_get_list_path();
333         if (!is_file_exist(path)) return -1;
334         node = xml_parse_file(path);
335         if (!node) return -1;
336
337         xmlnode = node->data;
338         if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
339                 g_warning("wrong folder list\n");
340                 xml_free_tree(node);
341                 return -1;
342         }
343
344         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
345                         folder_read_folder_func, NULL);
346
347         xml_free_tree(node);
348         if (folder_list)
349                 return 0;
350         else
351                 return -1;
352 }
353
354 void folder_write_list(void)
355 {
356         GList *list;
357         Folder *folder;
358         gchar *path;
359         PrefFile *pfile;
360
361         path = folder_get_list_path();
362         if ((pfile = prefs_write_open(path)) == NULL) return;
363
364         fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
365                 conv_get_current_charset_str());
366         fputs("\n<folderlist>\n", pfile->fp);
367
368         for (list = folder_list; list != NULL; list = list->next) {
369                 folder = list->data;
370                 folder_write_list_recursive(folder->node, pfile->fp);
371         }
372
373         fputs("</folderlist>\n", pfile->fp);
374
375         if (prefs_write_close(pfile) < 0)
376                 g_warning("failed to write folder list.\n");
377 }
378
379 struct TotalMsgCount
380 {
381         guint new;
382         guint unread;
383         guint total;
384 };
385
386 static gboolean folder_count_total_msgs_func(GNode *node, gpointer data)
387 {
388         FolderItem *item;
389         struct TotalMsgCount *count = (struct TotalMsgCount *)data;
390
391         g_return_val_if_fail(node->data != NULL, FALSE);
392
393         item = FOLDER_ITEM(node->data);
394         count->new += item->new;
395         count->unread += item->unread;
396         count->total += item->total;
397
398         return FALSE;
399 }
400
401 void folder_count_total_msgs(guint *new, guint *unread, guint *total)
402 {
403         GList *list;
404         Folder *folder;
405         struct TotalMsgCount count;
406
407         count.new = count.unread = count.total = 0;
408
409         debug_print(_("Counting total number of messages...\n"));
410
411         for (list = folder_list; list != NULL; list = list->next) {
412                 folder = FOLDER(list->data);
413                 if (folder->node)
414                         g_node_traverse(folder->node, G_PRE_ORDER,
415                                         G_TRAVERSE_ALL, -1,
416                                         folder_count_total_msgs_func,
417                                         &count);
418         }
419
420         *new = count.new;
421         *unread = count.unread;
422         *total = count.total;
423
424         return;
425 }
426
427 Folder *folder_find_from_path(const gchar *path)
428 {
429         GList *list;
430         Folder *folder;
431
432         for (list = folder_list; list != NULL; list = list->next) {
433                 folder = list->data;
434                 if ((folder->type == F_MH || folder->type == F_MBOX) &&
435                     !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
436                         return folder;
437         }
438
439         return NULL;
440 }
441
442 Folder *folder_find_from_name(const gchar *name, FolderType type)
443 {
444         GList *list;
445         Folder *folder;
446
447         for (list = folder_list; list != NULL; list = list->next) {
448                 folder = list->data;
449                 if (folder->type == type && strcmp2(name, folder->name) == 0)
450                         return folder;
451         }
452
453         return NULL;
454 }
455
456 static gboolean folder_item_find_func(GNode *node, gpointer data)
457 {
458         FolderItem *item = node->data;
459         gpointer *d = data;
460         const gchar *path = d[0];
461
462         if (path_cmp(path, item->path) != 0)
463                 return FALSE;
464
465         d[1] = item;
466
467         return TRUE;
468 }
469
470 FolderItem *folder_find_item_from_path(const gchar *path)
471 {
472         Folder *folder;
473         gpointer d[2];
474
475         folder = folder_get_default_folder();
476         g_return_val_if_fail(folder != NULL, NULL);
477
478         d[0] = (gpointer)path;
479         d[1] = NULL;
480         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
481                         folder_item_find_func, d);
482         return d[1];
483 }
484
485 static const struct {
486         gchar *str;
487         FolderType type;
488 } type_str_table[] = {
489         {"#mh"     , F_MH},
490         {"#mbox"   , F_MBOX},
491         {"#maildir", F_MAILDIR},
492         {"#imap"   , F_IMAP},
493         {"#news"   , F_NEWS}
494 };
495
496 static gchar *folder_get_type_string(FolderType type)
497 {
498         gint i;
499
500         for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
501              i++) {
502                 if (type_str_table[i].type == type)
503                         return type_str_table[i].str;
504         }
505
506         return NULL;
507 }
508
509 static FolderType folder_get_type_from_string(const gchar *str)
510 {
511         gint i;
512
513         for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
514              i++) {
515                 if (g_strcasecmp(type_str_table[i].str, str) == 0)
516                         return type_str_table[i].type;
517         }
518
519         return F_UNKNOWN;
520 }
521
522 gchar *folder_get_identifier(Folder *folder)
523 {
524         gchar *type_str;
525
526         g_return_val_if_fail(folder != NULL, NULL);
527
528         type_str = folder_get_type_string(folder->type);
529         return g_strconcat(type_str, "/", folder->name, NULL);
530 }
531
532 gchar *folder_item_get_identifier(FolderItem *item)
533 {
534         gchar *id;
535         gchar *folder_id;
536
537         g_return_val_if_fail(item != NULL, NULL);
538         g_return_val_if_fail(item->path != NULL, NULL);
539
540         folder_id = folder_get_identifier(item->folder);
541         id = g_strconcat(folder_id, "/", item->path, NULL);
542         g_free(folder_id);
543
544         return id;
545 }
546
547 FolderItem *folder_find_item_from_identifier(const gchar *identifier)
548 {
549         Folder *folder;
550         gpointer d[2];
551         gchar *str;
552         gchar *p;
553         gchar *name;
554         gchar *path;
555         FolderType type;
556
557         g_return_val_if_fail(identifier != NULL, NULL);
558
559         if (*identifier != '#')
560                 return folder_find_item_from_path(identifier);
561
562         Xstrdup_a(str, identifier, return NULL);
563
564         p = strchr(str, '/');
565         if (!p)
566                 return folder_find_item_from_path(identifier);
567         *p = '\0';
568         p++;
569         type = folder_get_type_from_string(str);
570         if (type == F_UNKNOWN)
571                 return folder_find_item_from_path(identifier);
572
573         name = p;
574         p = strchr(p, '/');
575         if (!p)
576                 return folder_find_item_from_path(identifier);
577         *p = '\0';
578         p++;
579
580         folder = folder_find_from_name(name, type);
581         if (!folder)
582                 return folder_find_item_from_path(identifier);
583
584         path = p;
585
586         d[0] = (gpointer)path;
587         d[1] = NULL;
588         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
589                         folder_item_find_func, d);
590         return d[1];
591 }
592
593 Folder *folder_get_default_folder(void)
594 {
595         return folder_list ? FOLDER(folder_list->data) : NULL;
596 }
597
598 FolderItem *folder_get_default_inbox(void)
599 {
600         Folder *folder;
601
602         if (!folder_list) return NULL;
603         folder = FOLDER(folder_list->data);
604         g_return_val_if_fail(folder != NULL, NULL);
605         return folder->inbox;
606 }
607
608 FolderItem *folder_get_default_outbox(void)
609 {
610         Folder *folder;
611
612         if (!folder_list) return NULL;
613         folder = FOLDER(folder_list->data);
614         g_return_val_if_fail(folder != NULL, NULL);
615         return folder->outbox;
616 }
617
618 FolderItem *folder_get_default_draft(void)
619 {
620         Folder *folder;
621
622         if (!folder_list) return NULL;
623         folder = FOLDER(folder_list->data);
624         g_return_val_if_fail(folder != NULL, NULL);
625         return folder->draft;
626 }
627
628 FolderItem *folder_get_default_queue(void)
629 {
630         Folder *folder;
631
632         if (!folder_list) return NULL;
633         folder = FOLDER(folder_list->data);
634         g_return_val_if_fail(folder != NULL, NULL);
635         return folder->queue;
636 }
637
638 FolderItem *folder_get_default_trash(void)
639 {
640         Folder *folder;
641
642         if (!folder_list) return NULL;
643         folder = FOLDER(folder_list->data);
644         g_return_val_if_fail(folder != NULL, NULL);
645         return folder->trash;
646 }
647
648 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type)   \
649 {                                                       \
650         if (!folder->member) {                          \
651                 item = folder_item_new(dir, dir);       \
652                 item->stype = type;                     \
653                 folder_item_append(rootitem, item);     \
654                 folder->member = item;                  \
655         }                                               \
656 }
657
658 void folder_set_missing_folders(void)
659 {
660         Folder *folder;
661         FolderItem *rootitem;
662         FolderItem *item;
663         GList *list;
664
665         for (list = folder_list; list != NULL; list = list->next) {
666                 folder = list->data;
667                 if (folder->type != F_MH) continue;
668                 rootitem = FOLDER_ITEM(folder->node->data);
669                 g_return_if_fail(rootitem != NULL);
670
671                 if (folder->inbox && folder->outbox && folder->draft &&
672                     folder->queue && folder->trash)
673                         continue;
674
675                 if (folder->create_tree(folder) < 0) {
676                         g_warning("%s: can't create the folder tree.\n",
677                                   LOCAL_FOLDER(folder)->rootpath);
678                         continue;
679                 }
680
681                 CREATE_FOLDER_IF_NOT_EXIST(inbox,  INBOX_DIR,  F_INBOX);
682                 CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
683                 CREATE_FOLDER_IF_NOT_EXIST(draft,  DRAFT_DIR,  F_DRAFT);
684                 CREATE_FOLDER_IF_NOT_EXIST(queue,  QUEUE_DIR,  F_QUEUE);
685                 CREATE_FOLDER_IF_NOT_EXIST(trash,  TRASH_DIR,  F_TRASH);
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 new = 0, unread = 0, total = 0;
1150         time_t mtime = 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 = strtoul(attr->value, NULL, 10);
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                 else if (!strcmp(attr->name, "sort_key")) {
1208                         if (!strcmp(attr->value, "none"))
1209                                 sort_key = SORT_BY_NONE;
1210                         else if (!strcmp(attr->value, "number"))
1211                                 sort_key = SORT_BY_NUMBER;
1212                         else if (!strcmp(attr->value, "size"))
1213                                 sort_key = SORT_BY_SIZE;
1214                         else if (!strcmp(attr->value, "date"))
1215                                 sort_key = SORT_BY_DATE;
1216                         else if (!strcmp(attr->value, "from"))
1217                                 sort_key = SORT_BY_FROM;
1218                         else if (!strcmp(attr->value, "subject"))
1219                                 sort_key = SORT_BY_SUBJECT;
1220                         else if (!strcmp(attr->value, "score"))
1221                                 sort_key = SORT_BY_SCORE;
1222                         else if (!strcmp(attr->value, "label"))
1223                                 sort_key = SORT_BY_LABEL;
1224                         else if (!strcmp(attr->value, "mark"))
1225                                 sort_key = SORT_BY_MARK;
1226                         else if (!strcmp(attr->value, "unread"))
1227                                 sort_key = SORT_BY_UNREAD;
1228                         else if (!strcmp(attr->value, "mime"))
1229                                 sort_key = SORT_BY_MIME;
1230                         else if (!strcmp(attr->value, "locked"))
1231                                 sort_key = SORT_BY_LOCKED;
1232                 } else if (!strcmp(attr->name, "sort_type")) {
1233                         if (!strcmp(attr->value, "ascending"))
1234                                 sort_type = SORT_ASCENDING;
1235                         else
1236                                 sort_type = SORT_DESCENDING;
1237                 }
1238         }
1239
1240         item = folder_item_new(name, path);
1241         item->stype = stype;
1242         item->account = account;
1243         item->mtime = mtime;
1244         item->new = new;
1245         item->unread = unread;
1246         item->total = total;
1247         item->no_sub = no_sub;
1248         item->no_select = no_select;
1249         item->collapsed = collapsed;
1250         item->threaded  = threaded;
1251         item->hide_read_msgs  = hidereadmsgs;
1252         item->ret_rcpt  = ret_rcpt;
1253         item->sort_key  = sort_key;
1254         item->sort_type = sort_type;
1255         item->parent = FOLDER_ITEM(node->parent->data);
1256         item->folder = folder;
1257         switch (stype) {
1258         case F_INBOX:  folder->inbox  = item; break;
1259         case F_OUTBOX: folder->outbox = item; break;
1260         case F_DRAFT:  folder->draft  = item; break;
1261         case F_QUEUE:  folder->queue  = item; break;
1262         case F_TRASH:  folder->trash  = item; break;
1263         default:       break;
1264         }
1265
1266         prefs_folder_item_read_config(item);
1267
1268         node->data = item;
1269         xml_free_node(xmlnode);
1270
1271         return FALSE;
1272 }
1273
1274 static gboolean folder_read_folder_func(GNode *node, gpointer data)
1275 {
1276         Folder *folder;
1277         XMLNode *xmlnode;
1278         GList *list;
1279         FolderType type = F_UNKNOWN;
1280         const gchar *name = NULL;
1281         const gchar *path = NULL;
1282         PrefsAccount *account = NULL;
1283         gboolean collapsed = FALSE, threaded = TRUE, ret_rcpt = FALSE;
1284
1285         if (g_node_depth(node) != 2) return FALSE;
1286         g_return_val_if_fail(node->data != NULL, FALSE);
1287
1288         xmlnode = node->data;
1289         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1290                 g_warning("tag name != \"folder\"\n");
1291                 return TRUE;
1292         }
1293         g_node_unlink(node);
1294         list = xmlnode->tag->attr;
1295         for (; list != NULL; list = list->next) {
1296                 XMLAttr *attr = list->data;
1297
1298                 if (!attr || !attr->name || !attr->value) continue;
1299                 if (!strcmp(attr->name, "type")) {
1300                         if (!strcasecmp(attr->value, "mh"))
1301                                 type = F_MH;
1302                         else if (!strcasecmp(attr->value, "mbox"))
1303                                 type = F_MBOX;
1304                         else if (!strcasecmp(attr->value, "maildir"))
1305                                 type = F_MAILDIR;
1306                         else if (!strcasecmp(attr->value, "imap"))
1307                                 type = F_IMAP;
1308                         else if (!strcasecmp(attr->value, "news"))
1309                                 type = F_NEWS;
1310                 } else if (!strcmp(attr->name, "name"))
1311                         name = attr->value;
1312                 else if (!strcmp(attr->name, "path"))
1313                         path = attr->value;
1314                 else if (!strcmp(attr->name, "account_id")) {
1315                         account = account_find_from_id(atoi(attr->value));
1316                         if (!account) g_warning("account_id: %s not found\n",
1317                                                 attr->value);
1318                 } else if (!strcmp(attr->name, "collapsed"))
1319                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1320                 else if (!strcmp(attr->name, "threaded"))
1321                         threaded = *attr->value == '1' ? TRUE : FALSE;
1322                 else if (!strcmp(attr->name, "reqretrcpt"))
1323                         ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
1324         }
1325
1326         folder = folder_new(type, name, path);
1327         g_return_val_if_fail(folder != NULL, FALSE);
1328         folder->account = account;
1329         if (account && (type == F_IMAP || type == F_NEWS))
1330                 account->folder = REMOTE_FOLDER(folder);
1331         node->data = folder->node->data;
1332         g_node_destroy(folder->node);
1333         folder->node = node;
1334         folder_add(folder);
1335         FOLDER_ITEM(node->data)->collapsed = collapsed;
1336         FOLDER_ITEM(node->data)->threaded  = threaded;
1337         FOLDER_ITEM(node->data)->ret_rcpt  = ret_rcpt;
1338
1339         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1340                         folder_build_tree, folder);
1341
1342         return FALSE;
1343 }
1344
1345 static gchar *folder_get_list_path(void)
1346 {
1347         static gchar *filename = NULL;
1348
1349         if (!filename)
1350                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1351                                         FOLDER_LIST, NULL);
1352
1353         return filename;
1354 }
1355
1356 static void folder_write_list_recursive(GNode *node, gpointer data)
1357 {
1358         FILE *fp = (FILE *)data;
1359         FolderItem *item = FOLDER_ITEM(node->data);
1360         gint i, depth;
1361         static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1362                                            "news", "unknown"};
1363         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1364                                                  "draft", "queue", "trash"};
1365         static gchar *sort_key_str[] = {"none", "number", "size", "date",
1366                                         "from", "subject", "score", "label",
1367                                         "mark", "unread", "mime", "locked" };
1368
1369         g_return_if_fail(item != NULL);
1370
1371         depth = g_node_depth(node);
1372         for (i = 0; i < depth; i++)
1373                 fputs("    ", fp);
1374         if (depth == 1) {
1375                 Folder *folder = item->folder;
1376
1377                 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
1378                 if (folder->name) {
1379                         fputs(" name=\"", fp);
1380                         xml_file_put_escape_str(fp, folder->name);
1381                         fputs("\"", fp);
1382                 }
1383                 if ((folder->type == F_MH) || (folder->type == F_MBOX)) {
1384                         fputs(" path=\"", fp);
1385                         xml_file_put_escape_str
1386                                 (fp, LOCAL_FOLDER(folder)->rootpath);
1387                         fputs("\"", fp);
1388                 }
1389                 if (folder->account)
1390                         fprintf(fp, " account_id=\"%d\"",
1391                                 folder->account->account_id);
1392                 if (item->collapsed && node->children)
1393                         fputs(" collapsed=\"1\"", fp);
1394                 if (item->ret_rcpt) 
1395                         fputs(" reqretrcpt=\"1\"", fp);
1396         } else {
1397                 fprintf(fp, "<folderitem type=\"%s\"",
1398                         folder_item_stype_str[item->stype]);
1399                 if (item->name) {
1400                         fputs(" name=\"", fp);
1401                         xml_file_put_escape_str(fp, item->name);
1402                         fputs("\"", fp);
1403                 }
1404                 if (item->path) {
1405                         fputs(" path=\"", fp);
1406                         xml_file_put_escape_str(fp, item->path);
1407                         fputs("\"", fp);
1408                 }
1409                 if (item->account)
1410                         fprintf(fp, " account_id=\"%d\"",
1411                                 item->account->account_id);
1412                 if (item->no_sub)
1413                         fputs(" no_sub=\"1\"", fp);
1414                 if (item->no_select)
1415                         fputs(" no_select=\"1\"", fp);
1416                 if (item->collapsed && node->children)
1417                         fputs(" collapsed=\"1\"", fp);
1418                 if (item->threaded)
1419                         fputs(" threaded=\"1\"", fp);
1420                 else
1421                         fputs(" threaded=\"0\"", fp);
1422                 if (item->hide_read_msgs)
1423                         fputs(" hidereadmsgs=\"1\"", fp);
1424                 else
1425                         fputs(" hidereadmsgs=\"0\"", fp);
1426                 if (item->ret_rcpt)
1427                         fputs(" reqretrcpt=\"1\"", fp);
1428
1429                 if (item->sort_key != SORT_BY_NONE) {
1430                         fprintf(fp, " sort_key=\"%s\"",
1431                                 sort_key_str[item->sort_key]);
1432                         if (item->sort_type == SORT_ASCENDING)
1433                                 fprintf(fp, " sort_type=\"ascending\"");
1434                         else
1435                                 fprintf(fp, " sort_type=\"descending\"");
1436                 }
1437
1438                 fprintf(fp,
1439                         " mtime=\"%lu\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1440                         item->mtime, item->new, item->unread, item->total);
1441         }
1442
1443         if (node->children) {
1444                 GNode *child;
1445                 fputs(">\n", fp);
1446
1447                 child = node->children;
1448                 while (child) {
1449                         GNode *cur;
1450
1451                         cur = child;
1452                         child = cur->next;
1453                         folder_write_list_recursive(cur, data);
1454                 }
1455
1456                 for (i = 0; i < depth; i++)
1457                         fputs("    ", fp);
1458                 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1459         } else
1460                 fputs(" />\n", fp);
1461 }
1462
1463 static void folder_update_op_count_rec(GNode *node) {
1464         FolderItem *fitem = FOLDER_ITEM(node->data);
1465
1466         if (g_node_depth(node) > 0) {
1467                 if (fitem->op_count > 0) {
1468                         fitem->op_count = 0;
1469                         folderview_update_item(fitem, 0);
1470                 }
1471                 if (node->children) {
1472                         GNode *child;
1473
1474                         child = node->children;
1475                         while (child) {
1476                                 GNode *cur;
1477
1478                                 cur = child;
1479                                 child = cur->next;
1480                                 folder_update_op_count_rec(cur);
1481                         }
1482                 }
1483         }
1484 }
1485
1486 void folder_update_op_count() {
1487         GList *cur;
1488         Folder *folder;
1489
1490         for (cur = folder_list; cur != NULL; cur = cur->next) {
1491                 folder = cur->data;
1492                 folder_update_op_count_rec(folder->node);
1493         }
1494 }
1495
1496 typedef struct _type_str {
1497         gchar * str;
1498         gint type;
1499 } type_str;
1500
1501
1502 /*
1503 static gchar * folder_item_get_tree_identifier(FolderItem * item)
1504 {
1505         if (item->parent != NULL) {
1506                 gchar * path;
1507                 gchar * id;
1508
1509                 path = folder_item_get_tree_identifier(item->parent);
1510                 if (path == NULL)
1511                         return NULL;
1512
1513                 id = g_strconcat(path, "/", item->name, NULL);
1514                 g_free(path);
1515
1516                 return id;
1517         }
1518         else {
1519                 return g_strconcat("/", item->name, NULL);
1520         }
1521 }
1522 */
1523
1524 /* CLAWS: temporary local folder for filtering */
1525 static Folder *processing_folder;
1526 static FolderItem *processing_folder_item;
1527
1528 static void folder_create_processing_folder(void)
1529 {
1530 #define PROCESSING_FOLDER ".processing" 
1531         Folder     *tmpparent;
1532         FolderItem *tmpfolder;
1533         gchar      *tmpname;
1534
1535         tmpparent = folder_get_default_folder();
1536         g_assert(tmpparent);
1537         debug_print("tmpparentroot %s\n", LOCAL_FOLDER(tmpparent)->rootpath);
1538         if (LOCAL_FOLDER(tmpparent)->rootpath[0] == '/')
1539                 tmpname = g_strconcat(LOCAL_FOLDER(tmpparent)->rootpath,
1540                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
1541                                       NULL);
1542         else
1543                 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1544                                       LOCAL_FOLDER(tmpparent)->rootpath,
1545                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
1546                                       NULL);
1547
1548         processing_folder = folder_new(F_MH, "PROCESSING", LOCAL_FOLDER(tmpparent)->rootpath);
1549         g_assert(processing_folder);
1550
1551         if (!is_dir_exist(tmpname)) {
1552                 debug_print("*TMP* creating %s\n", tmpname);
1553                 processing_folder_item = processing_folder->create_folder(processing_folder,
1554                                                                           processing_folder->node->data,
1555                                                                           PROCESSING_FOLDER);
1556                 g_assert(processing_folder_item);                                                                         
1557         }
1558         else {
1559                 debug_print("*TMP* already created\n");
1560                 processing_folder_item = folder_item_new(".processing", ".processing");
1561                 g_assert(processing_folder_item);
1562                 folder_item_append(processing_folder->node->data, processing_folder_item);
1563         }
1564         g_free(tmpname);
1565 }
1566
1567 FolderItem *folder_get_default_processing(void)
1568 {
1569         if (!processing_folder_item) {
1570                 folder_create_processing_folder();
1571         }
1572         return processing_folder_item;
1573 }
1574
1575 /* folder_persist_prefs_new() - return hash table with persistent
1576  * settings (and folder name as key). 
1577  * (note that in claws other options are in the PREFS_FOLDER_ITEM_RC
1578  * file, so those don't need to be included in PersistPref yet) 
1579  */
1580 GHashTable *folder_persist_prefs_new(Folder *folder)
1581 {
1582         GHashTable *pptable;
1583
1584         g_return_val_if_fail(folder, NULL);
1585         pptable = g_hash_table_new(g_str_hash, g_str_equal);
1586         folder_get_persist_prefs_recursive(folder->node, pptable);
1587         return pptable;
1588 }
1589
1590 void folder_persist_prefs_free(GHashTable *pptable)
1591 {
1592         g_return_if_fail(pptable);
1593         g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
1594         g_hash_table_destroy(pptable);
1595 }
1596
1597 const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
1598 {
1599         if (pptable == NULL || name == NULL) return NULL;
1600         return g_hash_table_lookup(pptable, name);
1601 }
1602
1603 void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
1604 {
1605         const PersistPrefs *pp;
1606
1607         pp = folder_get_persist_prefs(pptable, item->path); 
1608         if (!pp) return;
1609
1610         /* CLAWS: since not all folder properties have been migrated to 
1611          * folderlist.xml, we need to call the old stuff first before
1612          * setting things that apply both to Main and Claws. */
1613         prefs_folder_item_read_config(item); 
1614          
1615         item->collapsed = pp->collapsed;
1616         item->threaded  = pp->threaded;
1617         item->ret_rcpt  = pp->ret_rcpt;
1618         item->hide_read_msgs = pp->hide_read_msgs;
1619         item->sort_key  = pp->sort_key;
1620         item->sort_type = pp->sort_type;
1621 }
1622
1623 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
1624 {
1625         FolderItem *item = FOLDER_ITEM(node->data);
1626         PersistPrefs *pp;
1627         GNode *child, *cur;
1628
1629         g_return_if_fail(node != NULL);
1630         g_return_if_fail(item != NULL);
1631
1632         /* FIXME: item->path == NULL for top level folder, so this means that 
1633          * properties of MH folder root will not be stored. Not quite important, 
1634          * because the top level folder properties are not special anyway. */
1635         if (item->path) {
1636                 pp = g_new0(PersistPrefs, 1);
1637                 g_return_if_fail(pp != NULL);
1638                 pp->collapsed = item->collapsed;
1639                 pp->threaded  = item->threaded;
1640                 pp->ret_rcpt  = item->ret_rcpt; 
1641                 pp->hide_read_msgs = item->hide_read_msgs;
1642                 pp->sort_key  = item->sort_key;
1643                 pp->sort_type = item->sort_type;
1644                 g_hash_table_insert(pptable, item->path, pp);
1645         }               
1646
1647         if (node->children) {
1648                 child = node->children;
1649                 while (child) {
1650                         cur = child;
1651                         child = cur->next;
1652                         folder_get_persist_prefs_recursive(cur, pptable);
1653                 }
1654         }       
1655 }
1656
1657 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
1658 {
1659         if (val) 
1660                 g_free(val);
1661         return TRUE;    
1662 }
1663