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