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