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