removed debugging info
[claws.git] / src / folder.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999,2000 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 "session.h"
33 #include "imap.h"
34 #include "news.h"
35 #include "mh.h"
36 #include "mbox_folder.h"
37 #include "utils.h"
38 #include "xml.h"
39 #include "codeconv.h"
40 #include "prefs.h"
41 #include "account.h"
42 #include "prefs_account.h"
43 #include "mbox_folder.h"
44
45 static GList *folder_list = NULL;
46
47 static void folder_init         (Folder         *folder,
48                                  FolderType      type,
49                                  const gchar    *name);
50
51 static void local_folder_destroy        (LocalFolder    *lfolder);
52 static void remote_folder_destroy       (RemoteFolder   *rfolder);
53 static void mh_folder_destroy           (MHFolder       *folder);
54 static void mbox_folder_destroy         (MboxFolder     *folder);
55 static void imap_folder_destroy         (IMAPFolder     *folder);
56 static void news_folder_destroy         (NewsFolder     *folder);
57
58 static gboolean folder_read_folder_func (GNode          *node,
59                                          gpointer        data);
60 static gchar *folder_get_list_path      (void);
61 static void folder_write_list_recursive (GNode          *node,
62                                          gpointer        data);
63
64
65 Folder *folder_new(FolderType type, const gchar *name, const gchar *path)
66 {
67         Folder *folder = NULL;
68
69         name = name ? name : path;
70         switch (type) {
71         case F_MBOX:
72                 folder = mbox_folder_new(name, path);
73                 break;
74         case F_MH:
75                 folder = mh_folder_new(name, path);
76                 break;
77         case F_IMAP:
78                 folder = imap_folder_new(name, path);
79                 break;
80         case F_NEWS:
81                 folder = news_folder_new(name, path);
82                 break;
83         default:
84                 return NULL;
85         }
86
87         return folder;
88 }
89
90 Folder *mh_folder_new(const gchar *name, const gchar *path)
91 {
92         Folder *folder;
93
94         folder = (Folder *)g_new0(MHFolder, 1);
95         folder_init(folder, F_MH, name);
96         LOCAL_FOLDER(folder)->rootpath = g_strdup(path);
97
98         return folder;
99 }
100
101 Folder *mbox_folder_new(const gchar *name, const gchar *path)
102 {
103         /* implementing */
104         Folder *folder;
105
106         folder = (Folder *)g_new0(MboxFolder, 1);
107         folder_init(folder, F_MBOX, name);
108         LOCAL_FOLDER(folder)->rootpath = g_strdup(path);
109
110         return folder;
111 }
112
113 Folder *maildir_folder_new(const gchar *name, const gchar *path)
114 {
115         /* not yet implemented */
116         return NULL;
117 }
118
119 Folder *imap_folder_new(const gchar *name, const gchar *path)
120 {
121         Folder *folder;
122
123         folder = (Folder *)g_new0(IMAPFolder, 1);
124         folder_init(folder, F_IMAP, name);
125
126         return folder;
127 }
128
129 Folder *news_folder_new(const gchar *name, const gchar *path)
130 {
131         Folder *folder;
132
133         folder = (Folder *)g_new0(NewsFolder, 1);
134         folder_init(folder, F_NEWS, name);
135
136         return folder;
137 }
138
139 FolderItem *folder_item_new(const gchar *name, const gchar *path)
140 {
141         FolderItem *item;
142
143         item = g_new0(FolderItem, 1);
144
145         item->stype = F_NORMAL;
146         item->name = g_strdup(name);
147         item->path = g_strdup(path);
148         item->account = NULL;
149         item->mtime = 0;
150         item->new = 0;
151         item->unread = 0;
152         item->total = 0;
153         item->last_num = -1;
154         item->parent = NULL;
155         item->folder = NULL;
156         item->data = NULL;
157
158         item->prefs = prefs_folder_item_new();
159
160         return item;
161 }
162
163 void folder_item_append(FolderItem *parent, FolderItem *item)
164 {
165         GNode *node;
166
167         g_return_if_fail(parent != NULL);
168         g_return_if_fail(parent->folder != NULL);
169         g_return_if_fail(item != NULL);
170
171         node = parent->folder->node;
172         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, parent);
173         g_return_if_fail(node != NULL);
174
175         item->parent = parent;
176         item->folder = parent->folder;
177         g_node_append_data(node, item);
178 }
179
180 void folder_item_remove(FolderItem *item)
181 {
182         GNode *node;
183
184         g_return_if_fail(item != NULL);
185         g_return_if_fail(item->folder != NULL);
186
187         node = item->folder->node;
188         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
189         g_return_if_fail(node != NULL);
190
191         /* TODO: free all FolderItem's first */
192         if (item->folder->node == node)
193                 item->folder->node = NULL;
194         g_node_destroy(node);
195 }
196
197 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
198 {
199         g_return_if_fail(folder != NULL);
200
201         folder->ui_func = func;
202         folder->ui_func_data = data;
203 }
204
205 void folder_set_name(Folder *folder, const gchar *name)
206 {
207         g_return_if_fail(folder != NULL);
208
209         g_free(folder->name);
210         folder->name = name ? g_strdup(name) : NULL;
211         if (folder->node && folder->node->data) {
212                 FolderItem *item = (FolderItem *)folder->node->data;
213
214                 g_free(item->name);
215                 item->name = name ? g_strdup(name) : NULL;
216         }
217 }
218
219 void folder_destroy(Folder *folder)
220 {
221         g_return_if_fail(folder != NULL);
222
223         folder_list = g_list_remove(folder_list, folder);
224
225         switch (folder->type) {
226         case F_MH:
227                 mh_folder_destroy(MH_FOLDER(folder));
228                 break;
229         case F_MBOX:
230                 mbox_folder_destroy(MBOX_FOLDER(folder));
231                 break;
232         case F_IMAP:
233                 imap_folder_destroy(IMAP_FOLDER(folder));
234                 break;
235         case F_NEWS:
236                 news_folder_destroy(NEWS_FOLDER(folder));
237                 break;
238         default:
239         }
240
241         folder_tree_destroy(folder);
242         g_free(folder->name);
243         g_free(folder);
244 }
245
246 void folder_tree_destroy(Folder *folder)
247 {
248         /* TODO: destroy all FolderItem before */
249         g_node_destroy(folder->node);
250 }
251
252 void folder_add(Folder *folder)
253 {
254         Folder *cur_folder;
255         GList *cur;
256         gint i;
257
258         g_return_if_fail(folder != NULL);
259
260         for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
261                 cur_folder = FOLDER(cur->data);
262                 if (folder->type == F_MH) {
263                         if (cur_folder->type != F_MH) break;
264                 } else if (folder->type == F_MBOX) {
265                         if (cur_folder->type != F_MH &&
266                             cur_folder->type != F_MBOX) break;
267                 } else if (folder->type == F_IMAP) {
268                         if (cur_folder->type != F_MH &&
269                             cur_folder->type != F_MBOX &&
270                             cur_folder->type != F_IMAP) break;
271                 } else if (folder->type == F_NEWS) {
272                         if (cur_folder->type != F_MH &&
273                             cur_folder->type != F_MBOX &&
274                             cur_folder->type != F_IMAP &&
275                             cur_folder->type != F_NEWS) break;
276                 }
277         }
278
279         folder_list = g_list_insert(folder_list, folder, i);
280 }
281
282 GList *folder_get_list(void)
283 {
284         return folder_list;
285 }
286
287 gint folder_read_list(void)
288 {
289         GNode *node;
290         XMLNode *xmlnode;
291
292         node = xml_parse_file(folder_get_list_path());
293         if (!node) return -1;
294
295         xmlnode = node->data;
296         if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
297                 g_warning("wrong folder list\n");
298                 xml_free_tree(node);
299                 return -1;
300         }
301
302         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
303                         folder_read_folder_func, NULL);
304
305         xml_free_tree(node);
306         if (folder_list)
307                 return 0;
308         else
309                 return -1;
310 }
311
312 void folder_write_list(void)
313 {
314         GList *list;
315         Folder *folder;
316         gchar *path;
317         PrefFile *pfile;
318
319         path = folder_get_list_path();
320         if ((pfile = prefs_write_open(path)) == NULL) return;
321
322         fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
323                 conv_get_current_charset_str());
324         fputs("\n<folderlist>\n", pfile->fp);
325
326         for (list = folder_list; list != NULL; list = list->next) {
327                 folder = list->data;
328                 folder_write_list_recursive(folder->node, pfile->fp);
329         }
330
331         fputs("</folderlist>\n", pfile->fp);
332
333         if (prefs_write_close(pfile) < 0)
334                 g_warning("failed to write folder list.\n");
335 }
336
337 Folder *folder_find_from_path(const gchar *path)
338 {
339         GList *list;
340         Folder *folder;
341
342         for (list = folder_list; list != NULL; list = list->next) {
343                 folder = list->data;
344                 if (folder->type == F_MH &&
345                     !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
346                         return folder;
347         }
348
349         return NULL;
350 }
351
352 static gboolean folder_item_find_func(GNode *node, gpointer data)
353 {
354         FolderItem *item = node->data;
355         gpointer *d = data;
356         const gchar *path = d[0];
357
358         if (path_cmp(path, item->path) != 0)
359                 return FALSE;
360
361         d[1] = item;
362
363         return TRUE;
364 }
365
366 FolderItem *folder_find_item_from_path(const gchar *path)
367 {
368         Folder *folder;
369         gpointer d[2];
370
371         folder = folder_get_default_folder();
372         g_return_val_if_fail(folder != NULL, NULL);
373
374         d[0] = (gpointer)path;
375         d[1] = NULL;
376         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
377                         folder_item_find_func, d);
378         return d[1];
379 }
380
381 Folder *folder_get_default_folder(void)
382 {
383         return FOLDER(folder_list->data);
384 }
385
386 FolderItem *folder_get_default_inbox(void)
387 {
388         Folder *folder;
389
390         folder = FOLDER(folder_list->data);
391         g_return_val_if_fail(folder != NULL, NULL);
392         return folder->inbox;
393 }
394
395 FolderItem *folder_get_default_outbox(void)
396 {
397         Folder *folder;
398
399         folder = FOLDER(folder_list->data);
400         g_return_val_if_fail(folder != NULL, NULL);
401         return folder->outbox;
402 }
403
404 FolderItem *folder_get_default_draft(void)
405 {
406         Folder *folder;
407
408         folder = FOLDER(folder_list->data);
409         g_return_val_if_fail(folder != NULL, NULL);
410         return folder->draft;
411 }
412
413 FolderItem *folder_get_default_queue(void)
414 {
415         Folder *folder;
416
417         folder = FOLDER(folder_list->data);
418         g_return_val_if_fail(folder != NULL, NULL);
419         return folder->queue;
420 }
421
422 FolderItem *folder_get_default_trash(void)
423 {
424         Folder *folder;
425
426         folder = FOLDER(folder_list->data);
427         g_return_val_if_fail(folder != NULL, NULL);
428         return folder->trash;
429 }
430
431 gchar *folder_item_get_path(FolderItem *item)
432 {
433         gchar *folder_path;
434         gchar *path;
435
436         g_return_val_if_fail(item != NULL, NULL);
437
438         if (FOLDER_TYPE(item->folder) == F_MH)
439                 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
440         else if (FOLDER_TYPE(item->folder) == F_MBOX) {
441                 path = mbox_get_virtual_path(item);
442                 if (path == NULL)
443                         return NULL;
444                 folder_path = g_strconcat(get_mbox_cache_dir(),
445                                           G_DIR_SEPARATOR_S, path, NULL);
446                 g_free(path);
447
448                 return folder_path;
449         }
450         else if (FOLDER_TYPE(item->folder) == F_IMAP) {
451                 g_return_val_if_fail(item->folder->account != NULL, NULL);
452                 folder_path = g_strconcat(get_imap_cache_dir(),
453                                           G_DIR_SEPARATOR_S,
454                                           item->folder->account->recv_server,
455                                           G_DIR_SEPARATOR_S,
456                                           item->folder->account->userid,
457                                           NULL);
458         } else if (FOLDER_TYPE(item->folder) == F_NEWS) {
459                 g_return_val_if_fail(item->folder->account != NULL, NULL);
460                 folder_path = g_strconcat(get_news_cache_dir(),
461                                           G_DIR_SEPARATOR_S,
462                                           item->folder->account->nntp_server,
463                                           NULL);
464         } else
465                 return NULL;
466
467         g_return_val_if_fail(folder_path != NULL, NULL);
468
469         if (folder_path[0] == G_DIR_SEPARATOR) {
470                 if (item->path)
471                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
472                                            item->path, NULL);
473                 else
474                         path = g_strdup(folder_path);
475         } else {
476                 if (item->path)
477                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
478                                            folder_path, G_DIR_SEPARATOR_S,
479                                            item->path, NULL);
480                 else
481                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
482                                            folder_path, NULL);
483         }
484
485         g_free(folder_path);
486         return path;
487 }
488
489 void folder_item_scan(FolderItem *item)
490 {
491         Folder *folder;
492
493         g_return_if_fail(item != NULL);
494
495         folder = item->folder;
496
497         g_return_if_fail(folder->scan != NULL);
498
499         folder->scan(folder, item);
500 }
501
502 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
503                                           gpointer data)
504 {
505         folder_item_scan(FOLDER_ITEM(key));
506 }
507
508 void folder_item_scan_foreach(GHashTable *table)
509 {
510         g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
511 }
512
513 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
514 {
515         Folder *folder;
516
517         g_return_val_if_fail(item != NULL, NULL);
518
519         folder = item->folder;
520
521         g_return_val_if_fail(folder->scan != NULL, NULL);
522         g_return_val_if_fail(folder->fetch_msg != NULL, NULL);
523
524         if (item->last_num < 0) folder->scan(folder, item);
525
526         return folder->fetch_msg(folder, item, num);
527 }
528
529 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
530                          gboolean remove_source)
531 {
532         Folder *folder;
533         gint num;
534
535         g_return_val_if_fail(dest != NULL, -1);
536         g_return_val_if_fail(file != NULL, -1);
537
538         folder = dest->folder;
539
540         g_return_val_if_fail(folder->scan != NULL, -1);
541         g_return_val_if_fail(folder->add_msg != NULL, -1);
542
543         if (dest->last_num < 0) folder->scan(folder, dest);
544
545         num = folder->add_msg(folder, dest, file, remove_source);
546         if (num > 0) dest->last_num = num;
547
548         return num;
549 }
550
551 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
552 {
553         Folder *folder;
554         gint num;
555
556         g_return_val_if_fail(dest != NULL, -1);
557         g_return_val_if_fail(msginfo != NULL, -1);
558
559         folder = dest->folder;
560
561         g_return_val_if_fail(folder->scan != NULL, -1);
562         g_return_val_if_fail(folder->move_msg != NULL, -1);
563
564         if (dest->last_num < 0) folder->scan(folder, dest);
565
566         num = folder->move_msg(folder, dest, msginfo);
567         if (num > 0) dest->last_num = num;
568
569         return num;
570 }
571
572 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
573 {
574         Folder *folder;
575         gint num;
576
577         g_return_val_if_fail(dest != NULL, -1);
578         g_return_val_if_fail(msglist != NULL, -1);
579
580         folder = dest->folder;
581
582         g_return_val_if_fail(folder->scan != NULL, -1);
583         g_return_val_if_fail(folder->move_msgs_with_dest != NULL, -1);
584
585         if (dest->last_num < 0) folder->scan(folder, dest);
586
587         num = folder->move_msgs_with_dest(folder, dest, msglist);
588         if (num > 0) dest->last_num = num;
589
590         return num;
591 }
592
593 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
594 {
595         Folder *folder;
596         gint num;
597
598         g_return_val_if_fail(dest != NULL, -1);
599         g_return_val_if_fail(msginfo != NULL, -1);
600
601         folder = dest->folder;
602
603         g_return_val_if_fail(folder->scan != NULL, -1);
604         g_return_val_if_fail(folder->copy_msg != NULL, -1);
605
606         if (dest->last_num < 0) folder->scan(folder, dest);
607
608         num = folder->copy_msg(folder, dest, msginfo);
609         if (num > 0) dest->last_num = num;
610
611         return num;
612 }
613
614 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
615 {
616         Folder *folder;
617         gint num;
618
619         g_return_val_if_fail(dest != NULL, -1);
620         g_return_val_if_fail(msglist != NULL, -1);
621
622         folder = dest->folder;
623
624         g_return_val_if_fail(folder->scan != NULL, -1);
625         g_return_val_if_fail(folder->copy_msgs_with_dest != NULL, -1);
626
627         if (dest->last_num < 0) folder->scan(folder, dest);
628
629         num = folder->copy_msgs_with_dest(folder, dest, msglist);
630         if (num > 0) dest->last_num = num;
631
632         return num;
633 }
634
635 gint folder_item_remove_msg(FolderItem *item, gint num)
636 {
637         Folder *folder;
638
639         g_return_val_if_fail(item != NULL, -1);
640
641         folder = item->folder;
642
643         g_return_val_if_fail(folder->scan != NULL, -1);
644         g_return_val_if_fail(folder->remove_msg != NULL, -1);
645
646         if (item->last_num < 0) folder->scan(folder, item);
647
648         return folder->remove_msg(folder, item, num);
649 }
650
651 gint folder_item_remove_all_msg(FolderItem *item)
652 {
653         Folder *folder;
654
655         g_return_val_if_fail(item != NULL, -1);
656         g_return_val_if_fail(folder->scan != NULL, -1);
657         g_return_val_if_fail(folder->remove_all_msg != NULL, -1);
658
659         folder = item->folder;
660         if (item->last_num < 0) folder->scan(folder, item);
661
662         return folder->remove_all_msg(folder, item);
663 }
664
665 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
666 {
667         Folder *folder;
668
669         g_return_val_if_fail(item != NULL, FALSE);
670
671         folder = item->folder;
672
673         g_return_val_if_fail(folder->is_msg_changed != NULL, -1);
674
675         return folder->is_msg_changed(folder, item, msginfo);
676 }
677
678 gchar *folder_item_get_cache_file(FolderItem *item)
679 {
680         gchar *path;
681         gchar *file;
682
683         g_return_val_if_fail(item != NULL, NULL);
684         g_return_val_if_fail(item->path != NULL, NULL);
685
686         path = folder_item_get_path(item);
687         g_return_val_if_fail(path != NULL, NULL);
688         if (!is_dir_exist(path))
689                 make_dir_hier(path);
690         file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
691         g_free(path);
692
693         return file;
694 }
695
696 gchar *folder_item_get_mark_file(FolderItem *item)
697 {
698         gchar *path;
699         gchar *file;
700
701         g_return_val_if_fail(item != NULL, NULL);
702         g_return_val_if_fail(item->path != NULL, NULL);
703
704         path = folder_item_get_path(item);
705         g_return_val_if_fail(path != NULL, NULL);
706         if (!is_dir_exist(path))
707                 make_dir_hier(path);
708         file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
709         g_free(path);
710
711         return file;
712 }
713
714
715 static void folder_init(Folder *folder, FolderType type, const gchar *name)
716 {
717         FolderItem *item;
718
719         g_return_if_fail(folder != NULL);
720
721         folder->type = type;
722         folder_set_name(folder, name);
723         folder->account = NULL;
724         folder->inbox = NULL;
725         folder->outbox = NULL;
726         folder->draft = NULL;
727         folder->queue = NULL;
728         folder->trash = NULL;
729         folder->ui_func = NULL;
730         folder->ui_func_data = NULL;
731         item = folder_item_new(name, NULL);
732         item->folder = folder;
733         folder->node = g_node_new(item);
734         folder->data = NULL;
735
736         switch (type) {
737         case F_MH:
738                 folder->get_msg_list        = mh_get_msg_list;
739                 folder->fetch_msg           = mh_fetch_msg;
740                 folder->add_msg             = mh_add_msg;
741                 folder->move_msg            = mh_move_msg;
742                 folder->move_msgs_with_dest = mh_move_msgs_with_dest;
743                 folder->copy_msg            = mh_copy_msg;
744                 folder->copy_msgs_with_dest = mh_copy_msgs_with_dest;
745                 folder->remove_msg          = mh_remove_msg;
746                 folder->remove_all_msg      = mh_remove_all_msg;
747                 folder->is_msg_changed      = mh_is_msg_changed;
748                 folder->scan                = mh_scan_folder;
749                 folder->scan_tree           = mh_scan_tree;
750                 folder->create_tree         = mh_create_tree;
751                 folder->create_folder       = mh_create_folder;
752                 folder->rename_folder       = mh_rename_folder;
753                 folder->remove_folder       = mh_remove_folder;
754                 break;
755         case F_IMAP:
756                 folder->get_msg_list        = imap_get_msg_list;
757                 folder->fetch_msg           = imap_fetch_msg;
758                 folder->move_msg            = imap_move_msg;
759                 folder->move_msgs_with_dest = imap_move_msgs_with_dest;
760                 folder->remove_msg          = imap_remove_msg;
761                 folder->remove_all_msg      = imap_remove_all_msg;
762                 folder->scan                = imap_scan_folder;
763                 folder->create_folder       = imap_create_folder;
764                 folder->remove_folder       = imap_remove_folder;               
765                 break;
766         case F_NEWS:
767                 folder->get_msg_list        = news_get_article_list;
768                 folder->fetch_msg           = news_fetch_msg;
769                 folder->scan                = news_scan_group;
770                 break;
771         case F_MBOX:
772                 folder->get_msg_list        = mbox_get_msg_list;
773                 folder->fetch_msg           = mbox_fetch_msg;
774                 folder->scan                = mbox_scan_folder;
775                 folder->add_msg             = mbox_add_msg;
776                 folder->remove_all_msg      = mbox_remove_all_msg;
777                 folder->remove_msg          = mbox_remove_msg;
778                 folder->update_mark         = mbox_update_mark;
779                 folder->move_msg            = mbox_move_msg;
780                 folder->move_msgs_with_dest = mbox_move_msgs_with_dest;
781
782                 /*
783                 folder->remove_msg          = mh_remove_msg;
784                 folder->is_msg_changed      = mh_is_msg_changed;
785                 folder->scan_tree           = mh_scan_tree;
786                 folder->create_tree         = mh_create_tree;
787                 folder->create_folder       = mh_create_folder;
788                 folder->rename_folder       = mh_rename_folder;
789                 folder->remove_folder       = mh_remove_folder;*/
790                 break;
791         default:
792         }
793
794         switch (type) {
795         case F_MH:
796         case F_MBOX:
797         case F_MAILDIR:
798                 LOCAL_FOLDER(folder)->rootpath = NULL;
799                 break;
800         case F_IMAP:
801         case F_NEWS:
802                 REMOTE_FOLDER(folder)->session   = NULL;
803                 break;
804         default:
805         }
806 }
807
808 static void local_folder_destroy(LocalFolder *lfolder)
809 {
810         g_return_if_fail(lfolder != NULL);
811
812         g_free(lfolder->rootpath);
813 }
814
815 static void remote_folder_destroy(RemoteFolder *rfolder)
816 {
817         g_return_if_fail(rfolder != NULL);
818
819         if (rfolder->session)
820                 session_destroy(rfolder->session);
821 }
822
823 static void mh_folder_destroy(MHFolder *folder)
824 {
825         local_folder_destroy(LOCAL_FOLDER(folder));
826 }
827
828 static void mbox_folder_destroy(MboxFolder *folder)
829 {
830         local_folder_destroy(LOCAL_FOLDER(folder));
831 }
832
833 static void imap_folder_destroy(IMAPFolder *folder)
834 {
835         remote_folder_destroy(REMOTE_FOLDER(folder));
836 }
837
838 static void news_folder_destroy(NewsFolder *folder)
839 {
840         remote_folder_destroy(REMOTE_FOLDER(folder));
841 }
842
843 static gboolean folder_build_tree(GNode *node, gpointer data)
844 {
845         Folder *folder = FOLDER(data);
846         FolderItem *item;
847         XMLNode *xmlnode;
848         GList *list;
849         SpecialFolderItemType stype = F_NORMAL;
850         const gchar *name = NULL;
851         const gchar *path = NULL;
852         PrefsAccount *account = NULL;
853         gint mtime = 0, new = 0, unread = 0, total = 0;
854
855         g_return_val_if_fail(node->data != NULL, FALSE);
856         if (!node->parent) return FALSE;
857
858         xmlnode = node->data;
859         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
860                 g_warning("tag name != \"folderitem\"\n");
861                 return FALSE;
862         }
863
864         list = xmlnode->tag->attr;
865         for (; list != NULL; list = list->next) {
866                 XMLAttr *attr = list->data;
867
868                 if (!attr || !attr->name || !attr->value) continue;
869                 if (!strcmp(attr->name, "type")) {
870                         if (!strcasecmp(attr->value, "normal"))
871                                 stype = F_NORMAL;
872                         else if (!strcasecmp(attr->value, "inbox"))
873                                 stype = F_INBOX;
874                         else if (!strcasecmp(attr->value, "outbox"))
875                                 stype = F_OUTBOX;
876                         else if (!strcasecmp(attr->value, "draft"))
877                                 stype = F_DRAFT;
878                         else if (!strcasecmp(attr->value, "queue"))
879                                 stype = F_QUEUE;
880                         else if (!strcasecmp(attr->value, "trash"))
881                                 stype = F_TRASH;
882                 } else if (!strcmp(attr->name, "name"))
883                         name = attr->value;
884                 else if (!strcmp(attr->name, "path"))
885                         path = attr->value;
886                 else if (!strcmp(attr->name, "account_id")) {
887                         account = account_find_from_id(atoi(attr->value));
888                         if (!account) g_warning("account_id: %s not found\n",
889                                                 attr->value);
890                 }
891                 else if (!strcmp(attr->name, "mtime"))
892                         mtime = atoi(attr->value);
893                 else if (!strcmp(attr->name, "new"))
894                         new = atoi(attr->value);
895                 else if (!strcmp(attr->name, "unread"))
896                         unread = atoi(attr->value);
897                 else if (!strcmp(attr->name, "total"))
898                         total = atoi(attr->value);
899         }
900
901         item = folder_item_new(name, path);
902         item->stype = stype;
903         item->account = account;
904         item->mtime = mtime;
905         item->new = new;
906         item->unread = unread;
907         item->total = total;
908         item->parent = FOLDER_ITEM(node->parent->data);
909         item->folder = folder;
910         switch (stype) {
911         case F_INBOX:  folder->inbox  = item; break;
912         case F_OUTBOX: folder->outbox = item; break;
913         case F_DRAFT:  folder->draft  = item; break;
914         case F_QUEUE:  folder->queue  = item; break;
915         case F_TRASH:  folder->trash  = item; break;
916         default:
917         }
918
919         prefs_folder_item_read_config(item);
920
921         node->data = item;
922         xml_free_node(xmlnode);
923
924         return FALSE;
925 }
926
927 static gboolean folder_read_folder_func(GNode *node, gpointer data)
928 {
929         Folder *folder;
930         XMLNode *xmlnode;
931         GList *list;
932         FolderType type = F_UNKNOWN;
933         const gchar *name = NULL;
934         const gchar *path = NULL;
935         PrefsAccount *account = NULL;
936
937         if (g_node_depth(node) != 2) return FALSE;
938         g_return_val_if_fail(node->data != NULL, FALSE);
939
940         xmlnode = node->data;
941         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
942                 g_warning("tag name != \"folder\"\n");
943                 return TRUE;
944         }
945         g_node_unlink(node);
946         list = xmlnode->tag->attr;
947         for (; list != NULL; list = list->next) {
948                 XMLAttr *attr = list->data;
949
950                 if (!attr || !attr->name || !attr->value) continue;
951                 if (!strcmp(attr->name, "type")) {
952                         if (!strcasecmp(attr->value, "mh"))
953                                 type = F_MH;
954                         else if (!strcasecmp(attr->value, "mbox"))
955                                 type = F_MBOX;
956                         else if (!strcasecmp(attr->value, "maildir"))
957                                 type = F_MAILDIR;
958                         else if (!strcasecmp(attr->value, "imap"))
959                                 type = F_IMAP;
960                         else if (!strcasecmp(attr->value, "news"))
961                                 type = F_NEWS;
962                 } else if (!strcmp(attr->name, "name"))
963                         name = attr->value;
964                 else if (!strcmp(attr->name, "path"))
965                         path = attr->value;
966                 else if (!strcmp(attr->name, "account_id")) {
967                         account = account_find_from_id(atoi(attr->value));
968                         if (!account) g_warning("account_id: %s not found\n",
969                                                 attr->value);
970                 }
971         }
972
973         folder = folder_new(type, name, path);
974         g_return_val_if_fail(folder != NULL, FALSE);
975         folder->account = account;
976         if (account && (type == F_IMAP || type == F_NEWS))
977                 account->folder = REMOTE_FOLDER(folder);
978         node->data = folder->node->data;
979         g_node_destroy(folder->node);
980         folder->node = node;
981         folder_add(folder);
982
983         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
984                         folder_build_tree, folder);
985
986         return FALSE;
987 }
988
989 static gchar *folder_get_list_path(void)
990 {
991         static gchar *filename = NULL;
992
993         if (!filename)
994                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
995                                         FOLDER_LIST, NULL);
996
997         return filename;
998 }
999
1000 static void folder_write_list_recursive(GNode *node, gpointer data)
1001 {
1002         FILE *fp = (FILE *)data;
1003         FolderItem *item = FOLDER_ITEM(node->data);
1004         gint i, depth;
1005         static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1006                                            "news", "unknown"};
1007         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1008                                                  "draft", "queue", "trash"};
1009
1010         g_return_if_fail(item != NULL);
1011
1012         depth = g_node_depth(node);
1013         for (i = 0; i < depth; i++)
1014                 fputs("    ", fp);
1015         if (depth == 1) {
1016                 Folder *folder = item->folder;
1017
1018                 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
1019                 if (folder->name) {
1020                         fputs(" name=\"", fp);
1021                         xml_file_put_escape_str(fp, folder->name);
1022                         fputs("\"", fp);
1023                 }
1024                 if (folder->type == F_MH) {
1025                         fputs(" path=\"", fp);
1026                         xml_file_put_escape_str
1027                                 (fp, LOCAL_FOLDER(folder)->rootpath);
1028                         fputs("\"", fp);
1029                 }
1030                 if (folder->account)
1031                         fprintf(fp, " account_id=\"%d\"",
1032                                 folder->account->account_id);
1033         } else {
1034                 fprintf(fp, "<folderitem type=\"%s\"",
1035                         folder_item_stype_str[item->stype]);
1036                 if (item->name) {
1037                         fputs(" name=\"", fp);
1038                         xml_file_put_escape_str(fp, item->name);
1039                         fputs("\"", fp);
1040                 }
1041                 if (item->path) {
1042                         fputs(" path=\"", fp);
1043                         xml_file_put_escape_str(fp, item->path);
1044                         fputs("\"", fp);
1045                 }
1046                 if (item->account)
1047                         fprintf(fp, " account_id = \"%d\"",
1048                                 item->account->account_id);
1049                 fprintf(fp,
1050                         " mtime=\"%ld\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1051                         item->mtime, item->new, item->unread, item->total);
1052         }
1053
1054         if (node->children) {
1055                 GNode *child;
1056                 fputs(">\n", fp);
1057
1058                 child = node->children;
1059                 while (child) {
1060                         GNode *cur;
1061
1062                         cur = child;
1063                         child = cur->next;
1064                         folder_write_list_recursive(cur, data);
1065                 }
1066
1067                 for (i = 0; i < depth; i++)
1068                         fputs("    ", fp);
1069                 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1070         } else
1071                 fputs(" />\n", fp);
1072 }