sync with Hiroyuki's 0.5.0claws2-3
[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 "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->no_sub = FALSE;
155         item->no_select = FALSE;
156         item->collapsed = FALSE;
157         item->parent = NULL;
158         item->folder = NULL;
159         item->data = NULL;
160
161         item->prefs = prefs_folder_item_new();
162
163         return item;
164 }
165
166 void folder_item_append(FolderItem *parent, FolderItem *item)
167 {
168         GNode *node;
169
170         g_return_if_fail(parent != NULL);
171         g_return_if_fail(parent->folder != NULL);
172         g_return_if_fail(item != NULL);
173
174         node = parent->folder->node;
175         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, parent);
176         g_return_if_fail(node != NULL);
177
178         item->parent = parent;
179         item->folder = parent->folder;
180         g_node_append_data(node, item);
181 }
182
183 void folder_item_remove(FolderItem *item)
184 {
185         GNode *node;
186
187         g_return_if_fail(item != NULL);
188         g_return_if_fail(item->folder != NULL);
189
190         node = item->folder->node;
191         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
192         g_return_if_fail(node != NULL);
193
194         /* TODO: free all FolderItem's first */
195         if (item->folder->node == node)
196                 item->folder->node = NULL;
197         g_node_destroy(node);
198 }
199
200 void folder_item_destroy(FolderItem *item)
201 {
202         g_return_if_fail(item != NULL);
203
204         g_free(item->name);
205         g_free(item->path);
206         g_free(item);
207 }
208
209 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
210 {
211         g_return_if_fail(folder != NULL);
212
213         folder->ui_func = func;
214         folder->ui_func_data = data;
215 }
216
217 void folder_set_name(Folder *folder, const gchar *name)
218 {
219         g_return_if_fail(folder != NULL);
220
221         g_free(folder->name);
222         folder->name = name ? g_strdup(name) : NULL;
223         if (folder->node && folder->node->data) {
224                 FolderItem *item = (FolderItem *)folder->node->data;
225
226                 g_free(item->name);
227                 item->name = name ? g_strdup(name) : NULL;
228         }
229 }
230
231 void folder_destroy(Folder *folder)
232 {
233         g_return_if_fail(folder != NULL);
234
235         folder_list = g_list_remove(folder_list, folder);
236
237         switch (folder->type) {
238         case F_MH:
239                 mh_folder_destroy(MH_FOLDER(folder));
240                 break;
241         case F_MBOX:
242                 mbox_folder_destroy(MBOX_FOLDER(folder));
243                 break;
244         case F_IMAP:
245                 imap_folder_destroy(IMAP_FOLDER(folder));
246                 break;
247         case F_NEWS:
248                 news_folder_destroy(NEWS_FOLDER(folder));
249                 break;
250         default:
251                 break;
252         }
253
254         folder_tree_destroy(folder);
255         g_free(folder->name);
256         g_free(folder);
257 }
258
259 void folder_tree_destroy(Folder *folder)
260 {
261         /* TODO: destroy all FolderItem before */
262         g_node_destroy(folder->node);
263
264         folder->inbox = NULL;
265         folder->outbox = NULL;
266         folder->draft = NULL;
267         folder->queue = NULL;
268         folder->trash = NULL;
269         folder->node = NULL;
270 }
271
272 void folder_add(Folder *folder)
273 {
274         Folder *cur_folder;
275         GList *cur;
276         gint i;
277
278         g_return_if_fail(folder != NULL);
279
280         for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
281                 cur_folder = FOLDER(cur->data);
282                 if (folder->type == F_MH) {
283                         if (cur_folder->type != F_MH) break;
284                 } else if (folder->type == F_MBOX) {
285                         if (cur_folder->type != F_MH &&
286                             cur_folder->type != F_MBOX) break;
287                 } else if (folder->type == F_IMAP) {
288                         if (cur_folder->type != F_MH &&
289                             cur_folder->type != F_MBOX &&
290                             cur_folder->type != F_IMAP) break;
291                 } else if (folder->type == F_NEWS) {
292                         if (cur_folder->type != F_MH &&
293                             cur_folder->type != F_MBOX &&
294                             cur_folder->type != F_IMAP &&
295                             cur_folder->type != F_NEWS) break;
296                 }
297         }
298
299         folder_list = g_list_insert(folder_list, folder, i);
300 }
301
302 GList *folder_get_list(void)
303 {
304         return folder_list;
305 }
306
307 gint folder_read_list(void)
308 {
309         GNode *node;
310         XMLNode *xmlnode;
311
312         node = xml_parse_file(folder_get_list_path());
313         if (!node) return -1;
314
315         xmlnode = node->data;
316         if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
317                 g_warning("wrong folder list\n");
318                 xml_free_tree(node);
319                 return -1;
320         }
321
322         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
323                         folder_read_folder_func, NULL);
324
325         xml_free_tree(node);
326         if (folder_list)
327                 return 0;
328         else
329                 return -1;
330 }
331
332 void folder_write_list(void)
333 {
334         GList *list;
335         Folder *folder;
336         gchar *path;
337         PrefFile *pfile;
338
339         path = folder_get_list_path();
340         if ((pfile = prefs_write_open(path)) == NULL) return;
341
342         fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
343                 conv_get_current_charset_str());
344         fputs("\n<folderlist>\n", pfile->fp);
345
346         for (list = folder_list; list != NULL; list = list->next) {
347                 folder = list->data;
348                 folder_write_list_recursive(folder->node, pfile->fp);
349         }
350
351         fputs("</folderlist>\n", pfile->fp);
352
353         if (prefs_write_close(pfile) < 0)
354                 g_warning("failed to write folder list.\n");
355 }
356
357 Folder *folder_find_from_path(const gchar *path)
358 {
359         GList *list;
360         Folder *folder;
361
362         for (list = folder_list; list != NULL; list = list->next) {
363                 folder = list->data;
364                 if ((folder->type == F_MH || folder->type == F_MBOX) &&
365                     !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
366                         return folder;
367         }
368
369         return NULL;
370 }
371
372 static gboolean folder_item_find_func(GNode *node, gpointer data)
373 {
374         FolderItem *item = node->data;
375         gpointer *d = data;
376         const gchar *path = d[0];
377
378         if (path_cmp(path, item->path) != 0)
379                 return FALSE;
380
381         d[1] = item;
382
383         return TRUE;
384 }
385
386 FolderItem *folder_find_item_from_path(const gchar *path)
387 {
388         Folder *folder;
389         gpointer d[2];
390
391         folder = folder_get_default_folder();
392         g_return_val_if_fail(folder != NULL, NULL);
393
394         d[0] = (gpointer)path;
395         d[1] = NULL;
396         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
397                         folder_item_find_func, d);
398         return d[1];
399 }
400
401 Folder *folder_get_default_folder(void)
402 {
403         return FOLDER(folder_list->data);
404 }
405
406 FolderItem *folder_get_default_inbox(void)
407 {
408         Folder *folder;
409
410         folder = FOLDER(folder_list->data);
411         g_return_val_if_fail(folder != NULL, NULL);
412         return folder->inbox;
413 }
414
415 FolderItem *folder_get_default_outbox(void)
416 {
417         Folder *folder;
418
419         folder = FOLDER(folder_list->data);
420         g_return_val_if_fail(folder != NULL, NULL);
421         return folder->outbox;
422 }
423
424 FolderItem *folder_get_default_draft(void)
425 {
426         Folder *folder;
427
428         folder = FOLDER(folder_list->data);
429         g_return_val_if_fail(folder != NULL, NULL);
430         return folder->draft;
431 }
432
433 FolderItem *folder_get_default_queue(void)
434 {
435         Folder *folder;
436
437         folder = FOLDER(folder_list->data);
438         g_return_val_if_fail(folder != NULL, NULL);
439         return folder->queue;
440 }
441
442 FolderItem *folder_get_default_trash(void)
443 {
444         Folder *folder;
445
446         folder = FOLDER(folder_list->data);
447         g_return_val_if_fail(folder != NULL, NULL);
448         return folder->trash;
449 }
450
451 gchar *folder_item_get_path(FolderItem *item)
452 {
453         gchar *folder_path;
454         gchar *path;
455
456         g_return_val_if_fail(item != NULL, NULL);
457
458         if (FOLDER_TYPE(item->folder) == F_MH)
459                 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
460         else if (FOLDER_TYPE(item->folder) == F_MBOX) {
461                 path = mbox_get_virtual_path(item);
462                 if (path == NULL)
463                         return NULL;
464                 folder_path = g_strconcat(get_mbox_cache_dir(),
465                                           G_DIR_SEPARATOR_S, path, NULL);
466                 g_free(path);
467
468                 return folder_path;
469         }
470         else if (FOLDER_TYPE(item->folder) == F_IMAP) {
471                 g_return_val_if_fail(item->folder->account != NULL, NULL);
472                 folder_path = g_strconcat(get_imap_cache_dir(),
473                                           G_DIR_SEPARATOR_S,
474                                           item->folder->account->recv_server,
475                                           G_DIR_SEPARATOR_S,
476                                           item->folder->account->userid,
477                                           NULL);
478         } else if (FOLDER_TYPE(item->folder) == F_NEWS) {
479                 g_return_val_if_fail(item->folder->account != NULL, NULL);
480                 folder_path = g_strconcat(get_news_cache_dir(),
481                                           G_DIR_SEPARATOR_S,
482                                           item->folder->account->nntp_server,
483                                           NULL);
484         } else
485                 return NULL;
486
487         g_return_val_if_fail(folder_path != NULL, NULL);
488
489         if (folder_path[0] == G_DIR_SEPARATOR) {
490                 if (item->path)
491                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
492                                            item->path, NULL);
493                 else
494                         path = g_strdup(folder_path);
495         } else {
496                 if (item->path)
497                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
498                                            folder_path, G_DIR_SEPARATOR_S,
499                                            item->path, NULL);
500                 else
501                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
502                                            folder_path, NULL);
503         }
504
505         g_free(folder_path);
506         return path;
507 }
508
509 void folder_item_scan(FolderItem *item)
510 {
511         Folder *folder;
512
513         g_return_if_fail(item != NULL);
514
515         folder = item->folder;
516
517         g_return_if_fail(folder->scan != NULL);
518
519         folder->scan(folder, item);
520 }
521
522 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
523                                           gpointer data)
524 {
525         folder_item_scan(FOLDER_ITEM(key));
526 }
527
528 void folder_item_scan_foreach(GHashTable *table)
529 {
530         g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
531 }
532
533 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
534 {
535         Folder *folder;
536
537         g_return_val_if_fail(item != NULL, NULL);
538
539         folder = item->folder;
540
541         g_return_val_if_fail(folder->scan != NULL, NULL);
542         g_return_val_if_fail(folder->fetch_msg != NULL, NULL);
543
544         if (item->last_num < 0) folder->scan(folder, item);
545
546         return folder->fetch_msg(folder, item, num);
547 }
548
549 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
550                          gboolean remove_source)
551 {
552         Folder *folder;
553         gint num;
554
555         g_return_val_if_fail(dest != NULL, -1);
556         g_return_val_if_fail(file != NULL, -1);
557
558         folder = dest->folder;
559
560         g_return_val_if_fail(folder->scan != NULL, -1);
561         g_return_val_if_fail(folder->add_msg != NULL, -1);
562
563         if (dest->last_num < 0) folder->scan(folder, dest);
564
565         num = folder->add_msg(folder, dest, file, remove_source);
566         if (num > 0) dest->last_num = num;
567
568         return num;
569 }
570
571 /*
572 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
573 {
574         Folder *folder;
575         gint num;
576
577         g_return_val_if_fail(dest != NULL, -1);
578         g_return_val_if_fail(msginfo != NULL, -1);
579
580         folder = dest->folder;
581         if (dest->last_num < 0) folder->scan(folder, dest);
582
583         num = folder->move_msg(folder, dest, msginfo);
584         if (num > 0) dest->last_num = num;
585
586         return num;
587 }
588 */
589
590 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
591 {
592         Folder *folder;
593         gint num;
594         gchar * filename;
595         Folder * src_folder;
596
597         g_return_val_if_fail(dest != NULL, -1);
598         g_return_val_if_fail(msginfo != NULL, -1);
599
600         folder = dest->folder;
601
602         g_return_val_if_fail(folder->scan != NULL, -1);
603         g_return_val_if_fail(folder->remove_msg != 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         src_folder = msginfo->folder->folder;
609
610         num = folder->copy_msg(folder, dest, msginfo);
611         if (num != -1)
612                 src_folder->remove_msg(src_folder,
613                                        msginfo->folder,
614                                        msginfo->msgnum);
615         
616         if (folder->finished_copy)
617                 folder->finished_copy(folder, dest);
618
619         src_folder = msginfo->folder->folder;
620
621         if (msginfo->folder && src_folder->scan)
622                 src_folder->scan(src_folder, msginfo->folder);
623         folder->scan(folder, dest);
624
625         return num;
626 }
627
628 /*
629 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
630 {
631         Folder *folder;
632         gint num;
633
634         g_return_val_if_fail(dest != NULL, -1);
635         g_return_val_if_fail(msglist != NULL, -1);
636
637         folder = dest->folder;
638         if (dest->last_num < 0) folder->scan(folder, dest);
639
640         num = folder->move_msgs_with_dest(folder, dest, msglist);
641         if (num > 0) dest->last_num = num;
642
643         return num;
644 }
645 */
646
647 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
648 {
649         Folder *folder;
650         FolderItem * item;
651         GSList * l;
652         gchar * filename;
653
654         g_return_val_if_fail(dest != NULL, -1);
655         g_return_val_if_fail(msglist != NULL, -1);
656
657         folder = dest->folder;
658
659         g_return_val_if_fail(folder->scan != NULL, -1);
660         g_return_val_if_fail(folder->copy_msg != NULL, -1);
661         g_return_val_if_fail(folder->remove_msg != NULL, -1);
662
663         if (dest->last_num < 0) folder->scan(folder, dest);
664
665         item = NULL;
666         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
667                 MsgInfo * msginfo = (MsgInfo *) l->data;
668
669                 if (!item && msginfo->folder != NULL)
670                         item = msginfo->folder;
671
672                 if (folder->copy_msg(folder, dest, msginfo) != -1)
673                         item->folder->remove_msg(item->folder,
674                                                  msginfo->folder,
675                                                  msginfo->msgnum);
676         }
677
678         if (folder->finished_copy)
679                 folder->finished_copy(folder, dest);
680
681         if (item && item->folder->scan)
682                 item->folder->scan(item->folder, item);
683         folder->scan(folder, dest);
684
685         return dest->last_num;
686 }
687
688 /*
689 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
690 {
691         Folder *folder;
692         gint num;
693
694         g_return_val_if_fail(dest != NULL, -1);
695         g_return_val_if_fail(msginfo != NULL, -1);
696
697         folder = dest->folder;
698         if (dest->last_num < 0) folder->scan(folder, dest);
699
700         num = folder->copy_msg(folder, dest, msginfo);
701         if (num > 0) dest->last_num = num;
702
703         return num;
704 }
705 */
706
707 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
708 {
709         Folder *folder;
710         gint num;
711         gchar * filename;
712         Folder * src_folder;
713
714         g_return_val_if_fail(dest != NULL, -1);
715         g_return_val_if_fail(msginfo != NULL, -1);
716
717         folder = dest->folder;
718
719         g_return_val_if_fail(folder->scan != NULL, -1);
720         g_return_val_if_fail(folder->copy_msg != NULL, -1);
721
722         if (dest->last_num < 0) folder->scan(folder, dest);
723         
724         num = folder->copy_msg(folder, dest, msginfo);
725
726         if (folder->finished_copy)
727                 folder->finished_copy(folder, dest);
728
729         folder->scan(folder, dest);
730
731         return num;
732 }
733
734 /*
735 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
736 {
737         Folder *folder;
738         gint num;
739
740         g_return_val_if_fail(dest != NULL, -1);
741         g_return_val_if_fail(msglist != NULL, -1);
742
743         folder = dest->folder;
744         if (dest->last_num < 0) folder->scan(folder, dest);
745
746         num = folder->copy_msgs_with_dest(folder, dest, msglist);
747         if (num > 0) dest->last_num = num;
748
749         return num;
750 }
751 */
752
753 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
754 {
755         Folder *folder;
756         gint num;
757         GSList * l;
758         gchar * filename;
759
760         g_return_val_if_fail(dest != NULL, -1);
761         g_return_val_if_fail(msglist != NULL, -1);
762
763         folder = dest->folder;
764  
765         g_return_val_if_fail(folder->scan != NULL, -1);
766         g_return_val_if_fail(folder->copy_msg != NULL, -1);
767
768         if (dest->last_num < 0) folder->scan(folder, dest);
769
770         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
771                 MsgInfo * msginfo = (MsgInfo *) l->data;
772
773                 folder->copy_msg(folder, dest, msginfo);
774         }
775
776         if (folder->finished_copy)
777                 folder->finished_copy(folder, dest);
778
779         folder->scan(folder, dest);
780
781         return dest->last_num;
782 }
783
784 gint folder_item_remove_msg(FolderItem *item, gint num)
785 {
786         Folder *folder;
787         gint result;
788
789         g_return_val_if_fail(item != NULL, -1);
790
791         folder = item->folder;
792
793         g_return_val_if_fail(folder->scan != NULL, -1);
794         g_return_val_if_fail(folder->remove_msg != NULL, -1);
795
796         if (folder->finished_remove)
797                 folder->finished_remove(folder, item);
798
799         result = folder->remove_msg(folder, item, num);
800
801         if (item->last_num < 0) folder->scan(folder, item);
802
803         if (result == 0){
804                 if (folder->finished_remove)
805                         folder->finished_remove(folder, item);
806         }
807
808         return result;
809 }
810
811 gint folder_item_remove_all_msg(FolderItem *item)
812 {
813         Folder *folder;
814         gint result;
815
816         g_return_val_if_fail(item != NULL, -1);
817
818         folder = item->folder;
819
820         g_return_val_if_fail(folder->scan != NULL, -1);
821         g_return_val_if_fail(folder->remove_all_msg != NULL, -1);
822
823         if (item->last_num < 0) folder->scan(folder, item);
824
825         result = folder->remove_all_msg(folder, item);
826
827         if (result == 0){
828                 if (folder->finished_remove)
829                         folder->finished_remove(folder, item);
830         }
831
832         return result;
833 }
834
835 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
836 {
837         Folder *folder;
838
839         g_return_val_if_fail(item != NULL, FALSE);
840
841         folder = item->folder;
842
843         g_return_val_if_fail(folder->is_msg_changed != NULL, -1);
844
845         return folder->is_msg_changed(folder, item, msginfo);
846 }
847
848 gchar *folder_item_get_cache_file(FolderItem *item)
849 {
850         gchar *path;
851         gchar *file;
852
853         g_return_val_if_fail(item != NULL, NULL);
854         g_return_val_if_fail(item->path != NULL, NULL);
855
856         path = folder_item_get_path(item);
857         g_return_val_if_fail(path != NULL, NULL);
858         if (!is_dir_exist(path))
859                 make_dir_hier(path);
860         file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
861         g_free(path);
862
863         return file;
864 }
865
866 gchar *folder_item_get_mark_file(FolderItem *item)
867 {
868         gchar *path;
869         gchar *file;
870
871         g_return_val_if_fail(item != NULL, NULL);
872         g_return_val_if_fail(item->path != NULL, NULL);
873
874         path = folder_item_get_path(item);
875         g_return_val_if_fail(path != NULL, NULL);
876         if (!is_dir_exist(path))
877                 make_dir_hier(path);
878         file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
879         g_free(path);
880
881         return file;
882 }
883
884
885 static void folder_init(Folder *folder, FolderType type, const gchar *name)
886 {
887         FolderItem *item;
888
889         g_return_if_fail(folder != NULL);
890
891         folder->type = type;
892         folder_set_name(folder, name);
893         folder->account = NULL;
894         folder->inbox = NULL;
895         folder->outbox = NULL;
896         folder->draft = NULL;
897         folder->queue = NULL;
898         folder->trash = NULL;
899         folder->ui_func = NULL;
900         folder->ui_func_data = NULL;
901         item = folder_item_new(name, NULL);
902         item->folder = folder;
903         folder->node = g_node_new(item);
904         folder->data = NULL;
905
906         switch (type) {
907         case F_MH:
908                 folder->get_msg_list        = mh_get_msg_list;
909                 folder->fetch_msg           = mh_fetch_msg;
910                 folder->add_msg             = mh_add_msg;
911                 /*
912                 folder->move_msg            = mh_move_msg;
913                 folder->move_msgs_with_dest = mh_move_msgs_with_dest;
914                 folder->copy_msg            = mh_copy_msg;
915                 folder->copy_msgs_with_dest = mh_copy_msgs_with_dest;
916                 */
917                 folder->copy_msg            = mh_copy_msg;
918                 folder->remove_msg          = mh_remove_msg;
919                 folder->remove_all_msg      = mh_remove_all_msg;
920                 folder->is_msg_changed      = mh_is_msg_changed;
921                 folder->scan                = mh_scan_folder;
922                 folder->scan_tree           = mh_scan_tree;
923                 folder->create_tree         = mh_create_tree;
924                 folder->create_folder       = mh_create_folder;
925                 folder->rename_folder       = mh_rename_folder;
926                 folder->remove_folder       = mh_remove_folder;
927                 break;
928         case F_IMAP:
929                 folder->get_msg_list        = imap_get_msg_list;
930                 folder->fetch_msg           = imap_fetch_msg;
931                 folder->add_msg             = imap_add_msg;
932                 folder->move_msg            = imap_move_msg;
933                 folder->move_msgs_with_dest = imap_move_msgs_with_dest;
934                 folder->copy_msg            = imap_copy_msg;
935                 folder->copy_msgs_with_dest = imap_copy_msgs_with_dest;
936                 folder->remove_msg          = imap_remove_msg;
937                 folder->remove_all_msg      = imap_remove_all_msg;
938                 folder->scan                = imap_scan_folder;
939                 folder->scan_tree           = imap_scan_tree;
940                 folder->create_tree         = imap_create_tree;
941                 folder->create_folder       = imap_create_folder;
942                 folder->remove_folder       = imap_remove_folder;
943                 break;
944         case F_NEWS:
945                 folder->get_msg_list        = news_get_article_list;
946                 folder->fetch_msg           = news_fetch_msg;
947                 folder->scan                = news_scan_group;
948                 break;
949         case F_MBOX:
950                 folder->get_msg_list        = mbox_get_msg_list;
951                 folder->fetch_msg           = mbox_fetch_msg;
952                 folder->scan                = mbox_scan_folder;
953                 folder->add_msg             = mbox_add_msg;
954                 folder->remove_all_msg      = mbox_remove_all_msg;
955                 folder->remove_msg          = mbox_remove_msg;
956                 /*
957                 folder->move_msg            = mbox_move_msg;
958                 folder->move_msgs_with_dest = mbox_move_msgs_with_dest;
959                 folder->copy_msg            = mbox_copy_msg;
960                 folder->copy_msgs_with_dest = mbox_copy_msgs_with_dest;
961                 */
962                 folder->copy_msg            = mbox_copy_msg;
963
964                 folder->create_tree         = mbox_create_tree;
965                 folder->create_folder       = mbox_create_folder;
966                 folder->rename_folder       = mbox_rename_folder;
967                 folder->remove_folder       = mbox_remove_folder;
968
969                 folder->update_mark         = mbox_update_mark;
970                 folder->change_flags        = mbox_change_flags;
971                 folder->finished_copy       = mbox_finished_copy;
972
973                 break;
974         default:
975                 break;
976         }
977
978         switch (type) {
979         case F_MH:
980         case F_MBOX:
981         case F_MAILDIR:
982                 LOCAL_FOLDER(folder)->rootpath = NULL;
983                 break;
984         case F_IMAP:
985         case F_NEWS:
986                 REMOTE_FOLDER(folder)->session = NULL;
987                 break;
988         default:
989                 break;
990         }
991 }
992
993 static void local_folder_destroy(LocalFolder *lfolder)
994 {
995         g_return_if_fail(lfolder != NULL);
996
997         g_free(lfolder->rootpath);
998 }
999
1000 static void remote_folder_destroy(RemoteFolder *rfolder)
1001 {
1002         g_return_if_fail(rfolder != NULL);
1003
1004         if (rfolder->session)
1005                 session_destroy(rfolder->session);
1006 }
1007
1008 static void mh_folder_destroy(MHFolder *folder)
1009 {
1010         local_folder_destroy(LOCAL_FOLDER(folder));
1011 }
1012
1013 static void mbox_folder_destroy(MboxFolder *folder)
1014 {
1015         local_folder_destroy(LOCAL_FOLDER(folder));
1016 }
1017
1018 static void imap_folder_destroy(IMAPFolder *folder)
1019 {
1020         remote_folder_destroy(REMOTE_FOLDER(folder));
1021 }
1022
1023 static void news_folder_destroy(NewsFolder *folder)
1024 {
1025         remote_folder_destroy(REMOTE_FOLDER(folder));
1026 }
1027
1028 static gboolean folder_build_tree(GNode *node, gpointer data)
1029 {
1030         Folder *folder = FOLDER(data);
1031         FolderItem *item;
1032         XMLNode *xmlnode;
1033         GList *list;
1034         SpecialFolderItemType stype = F_NORMAL;
1035         const gchar *name = NULL;
1036         const gchar *path = NULL;
1037         PrefsAccount *account = NULL;
1038         gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE;
1039         gint mtime = 0, new = 0, unread = 0, total = 0;
1040
1041         g_return_val_if_fail(node->data != NULL, FALSE);
1042         if (!node->parent) return FALSE;
1043
1044         xmlnode = node->data;
1045         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1046                 g_warning("tag name != \"folderitem\"\n");
1047                 return FALSE;
1048         }
1049
1050         list = xmlnode->tag->attr;
1051         for (; list != NULL; list = list->next) {
1052                 XMLAttr *attr = list->data;
1053
1054                 if (!attr || !attr->name || !attr->value) continue;
1055                 if (!strcmp(attr->name, "type")) {
1056                         if (!strcasecmp(attr->value, "normal"))
1057                                 stype = F_NORMAL;
1058                         else if (!strcasecmp(attr->value, "inbox"))
1059                                 stype = F_INBOX;
1060                         else if (!strcasecmp(attr->value, "outbox"))
1061                                 stype = F_OUTBOX;
1062                         else if (!strcasecmp(attr->value, "draft"))
1063                                 stype = F_DRAFT;
1064                         else if (!strcasecmp(attr->value, "queue"))
1065                                 stype = F_QUEUE;
1066                         else if (!strcasecmp(attr->value, "trash"))
1067                                 stype = F_TRASH;
1068                 } else if (!strcmp(attr->name, "name"))
1069                         name = attr->value;
1070                 else if (!strcmp(attr->name, "path"))
1071                         path = attr->value;
1072                 else if (!strcmp(attr->name, "account_id")) {
1073                         account = account_find_from_id(atoi(attr->value));
1074                         if (!account) g_warning("account_id: %s not found\n",
1075                                                 attr->value);
1076                 } else if (!strcmp(attr->name, "mtime"))
1077                         mtime = atoi(attr->value);
1078                 else if (!strcmp(attr->name, "new"))
1079                         new = atoi(attr->value);
1080                 else if (!strcmp(attr->name, "unread"))
1081                         unread = atoi(attr->value);
1082                 else if (!strcmp(attr->name, "total"))
1083                         total = atoi(attr->value);
1084                 else if (!strcmp(attr->name, "no_sub"))
1085                         no_sub = *attr->value == '1' ? TRUE : FALSE;
1086                 else if (!strcmp(attr->name, "no_select"))
1087                         no_select = *attr->value == '1' ? TRUE : FALSE;
1088                 else if (!strcmp(attr->name, "collapsed"))
1089                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1090         }
1091
1092         item = folder_item_new(name, path);
1093         item->stype = stype;
1094         item->account = account;
1095         item->mtime = mtime;
1096         item->new = new;
1097         item->unread = unread;
1098         item->total = total;
1099         item->no_sub = no_sub;
1100         item->no_select = no_select;
1101         item->collapsed = collapsed;
1102         item->parent = FOLDER_ITEM(node->parent->data);
1103         item->folder = folder;
1104         switch (stype) {
1105         case F_INBOX:  folder->inbox  = item; break;
1106         case F_OUTBOX: folder->outbox = item; break;
1107         case F_DRAFT:  folder->draft  = item; break;
1108         case F_QUEUE:  folder->queue  = item; break;
1109         case F_TRASH:  folder->trash  = item; break;
1110         default:
1111                 break;
1112         }
1113
1114         prefs_folder_item_read_config(item);
1115
1116         node->data = item;
1117         xml_free_node(xmlnode);
1118
1119         return FALSE;
1120 }
1121
1122 static gboolean folder_read_folder_func(GNode *node, gpointer data)
1123 {
1124         Folder *folder;
1125         XMLNode *xmlnode;
1126         GList *list;
1127         FolderType type = F_UNKNOWN;
1128         const gchar *name = NULL;
1129         const gchar *path = NULL;
1130         PrefsAccount *account = NULL;
1131         gboolean collapsed = FALSE;
1132
1133         if (g_node_depth(node) != 2) return FALSE;
1134         g_return_val_if_fail(node->data != NULL, FALSE);
1135
1136         xmlnode = node->data;
1137         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1138                 g_warning("tag name != \"folder\"\n");
1139                 return TRUE;
1140         }
1141         g_node_unlink(node);
1142         list = xmlnode->tag->attr;
1143         for (; list != NULL; list = list->next) {
1144                 XMLAttr *attr = list->data;
1145
1146                 if (!attr || !attr->name || !attr->value) continue;
1147                 if (!strcmp(attr->name, "type")) {
1148                         if (!strcasecmp(attr->value, "mh"))
1149                                 type = F_MH;
1150                         else if (!strcasecmp(attr->value, "mbox"))
1151                                 type = F_MBOX;
1152                         else if (!strcasecmp(attr->value, "maildir"))
1153                                 type = F_MAILDIR;
1154                         else if (!strcasecmp(attr->value, "imap"))
1155                                 type = F_IMAP;
1156                         else if (!strcasecmp(attr->value, "news"))
1157                                 type = F_NEWS;
1158                 } else if (!strcmp(attr->name, "name"))
1159                         name = attr->value;
1160                 else if (!strcmp(attr->name, "path"))
1161                         path = attr->value;
1162                 else if (!strcmp(attr->name, "account_id")) {
1163                         account = account_find_from_id(atoi(attr->value));
1164                         if (!account) g_warning("account_id: %s not found\n",
1165                                                 attr->value);
1166                 } else if (!strcmp(attr->name, "collapsed"))
1167                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1168         }
1169
1170         folder = folder_new(type, name, path);
1171         g_return_val_if_fail(folder != NULL, FALSE);
1172         folder->account = account;
1173         if (account && (type == F_IMAP || type == F_NEWS))
1174                 account->folder = REMOTE_FOLDER(folder);
1175         node->data = folder->node->data;
1176         g_node_destroy(folder->node);
1177         folder->node = node;
1178         folder_add(folder);
1179         FOLDER_ITEM(node->data)->collapsed = collapsed;
1180
1181         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1182                         folder_build_tree, folder);
1183
1184         return FALSE;
1185 }
1186
1187 static gchar *folder_get_list_path(void)
1188 {
1189         static gchar *filename = NULL;
1190
1191         if (!filename)
1192                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1193                                         FOLDER_LIST, NULL);
1194
1195         return filename;
1196 }
1197
1198 static void folder_write_list_recursive(GNode *node, gpointer data)
1199 {
1200         FILE *fp = (FILE *)data;
1201         FolderItem *item = FOLDER_ITEM(node->data);
1202         gint i, depth;
1203         static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1204                                            "news", "unknown"};
1205         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1206                                                  "draft", "queue", "trash"};
1207
1208         g_return_if_fail(item != NULL);
1209
1210         depth = g_node_depth(node);
1211         for (i = 0; i < depth; i++)
1212                 fputs("    ", fp);
1213         if (depth == 1) {
1214                 Folder *folder = item->folder;
1215
1216                 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
1217                 if (folder->name) {
1218                         fputs(" name=\"", fp);
1219                         xml_file_put_escape_str(fp, folder->name);
1220                         fputs("\"", fp);
1221                 }
1222                 if ((folder->type == F_MH) || (folder->type == F_MBOX)) {
1223                         fputs(" path=\"", fp);
1224                         xml_file_put_escape_str
1225                                 (fp, LOCAL_FOLDER(folder)->rootpath);
1226                         fputs("\"", fp);
1227                 }
1228                 if (folder->account)
1229                         fprintf(fp, " account_id=\"%d\"",
1230                                 folder->account->account_id);
1231                 if (item->collapsed && node->children)
1232                         fputs(" collapsed=\"1\"", fp);
1233         } else {
1234                 fprintf(fp, "<folderitem type=\"%s\"",
1235                         folder_item_stype_str[item->stype]);
1236                 if (item->name) {
1237                         fputs(" name=\"", fp);
1238                         xml_file_put_escape_str(fp, item->name);
1239                         fputs("\"", fp);
1240                 }
1241                 if (item->path) {
1242                         fputs(" path=\"", fp);
1243                         xml_file_put_escape_str(fp, item->path);
1244                         fputs("\"", fp);
1245                 }
1246                 if (item->account)
1247                         fprintf(fp, " account_id=\"%d\"",
1248                                 item->account->account_id);
1249                 if (item->no_sub)
1250                         fputs(" no_sub=\"1\"", fp);
1251                 if (item->no_select)
1252                         fputs(" no_select=\"1\"", fp);
1253                 if (item->collapsed && node->children)
1254                         fputs(" collapsed=\"1\"", fp);
1255                 fprintf(fp,
1256                         " mtime=\"%ld\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1257                         item->mtime, item->new, item->unread, item->total);
1258         }
1259
1260         if (node->children) {
1261                 GNode *child;
1262                 fputs(">\n", fp);
1263
1264                 child = node->children;
1265                 while (child) {
1266                         GNode *cur;
1267
1268                         cur = child;
1269                         child = cur->next;
1270                         folder_write_list_recursive(cur, data);
1271                 }
1272
1273                 for (i = 0; i < depth; i++)
1274                         fputs("    ", fp);
1275                 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1276         } else
1277                 fputs(" />\n", fp);
1278 }
1279
1280 typedef struct _type_str {
1281         gchar * str;
1282         gint type;
1283 } type_str;
1284
1285
1286 static type_str type_str_table[] = 
1287 {
1288         {"#mh", F_MH},
1289         {"#mbox", F_MBOX},
1290         {"#maildir", F_MAILDIR},
1291         {"#imap", F_IMAP},
1292         {"#news", F_NEWS}
1293 };
1294
1295
1296 static gchar * folder_get_type_string(gint type)
1297 {
1298         gint i;
1299
1300         for(i = 0 ; i < sizeof(type_str_table) / sizeof(type_str) ; i++) {
1301                 if (type_str_table[i].type == type)
1302                         return type_str_table[i].str;
1303         }
1304         return NULL;
1305 }
1306
1307 static gint folder_get_type_from_string(gchar * str)
1308 {
1309         gint i;
1310
1311         for(i = 0 ; i < sizeof(type_str_table) / sizeof(type_str) ; i++) {
1312                 if (g_strcasecmp(type_str_table[i].str, str))
1313                         return type_str_table[i].type;
1314         }
1315         return F_UNKNOWN;
1316 }
1317
1318 gchar * folder_get_identifier(Folder * folder)
1319 {
1320         gchar * type_str;
1321         type_str = folder_get_type_string(folder->type);
1322
1323         return g_strconcat(type_str, "/", folder->name, NULL);
1324 }
1325
1326 /*
1327 static gchar * folder_item_get_tree_identifier(FolderItem * item)
1328 {
1329         if (item->parent != NULL) {
1330                 gchar * path;
1331                 gchar * id;
1332
1333                 path = folder_item_get_tree_identifier(item->parent);
1334                 if (path == NULL)
1335                         return NULL;
1336
1337                 id = g_strconcat(path, "/", item->name, NULL);
1338                 g_free(path);
1339
1340                 return id;
1341         }
1342         else {
1343                 return g_strconcat("/", item->name, NULL);
1344         }
1345 }
1346 */
1347
1348 gchar * folder_item_get_identifier(FolderItem * item)
1349 {
1350         gchar * path;
1351         gchar * id;
1352         gchar * folder_str;
1353
1354         folder_str = folder_get_identifier(item->folder);
1355
1356         if (item->path == NULL) {
1357                 g_free(folder_str);
1358                 return NULL;
1359         }
1360
1361         id = g_strconcat(folder_str, "/", item->path, NULL);
1362
1363         return id;
1364 }
1365
1366 Folder * folder_find_from_name(const gchar * name)
1367 {
1368         GList *list;
1369         Folder *folder;
1370
1371         for (list = g_list_first(folder_list); list != NULL;
1372              list = list->next) {
1373                 folder = list->data;
1374                 if (strcmp(name, folder->name) == 0)
1375                         return folder;
1376         }
1377         return NULL;
1378 }
1379
1380 FolderItem * folder_find_item_from_identifier(const gchar *identifier)
1381 {
1382         Folder *folder;
1383         gpointer d[2];
1384         gchar * str;
1385         gchar * p;
1386         gint type;
1387         gchar * name;
1388         gchar * path;
1389
1390         Xalloca(str, strlen(identifier) + 1, return NULL);
1391         strcpy(str, identifier);
1392
1393         /* extract box type */
1394
1395         p = strchr(str, '/');
1396
1397         if (p == NULL)
1398                 return NULL;
1399
1400         *p = '\0';
1401
1402         type = folder_get_type_from_string(str);
1403
1404         str = p + 1;
1405
1406         /* extract box name */
1407
1408         p = strchr(str, '/');
1409
1410         if (p == NULL)
1411                 return NULL;
1412
1413         *p = '\0';
1414
1415         name = str;
1416
1417         folder = folder_find_from_name(name);
1418         if (folder == NULL)
1419                 return;
1420
1421         path = p + 1;
1422
1423         d[0] = (gpointer)path;
1424         d[1] = NULL;
1425         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1426                         folder_item_find_func, d);
1427         return d[1];
1428 }