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