global names for folder / read permission for folders / filtering to mbox
[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 || folder->type == F_MBOX) &&
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 /*
552 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
553 {
554         Folder *folder;
555         gint num;
556
557         g_return_val_if_fail(dest != NULL, -1);
558         g_return_val_if_fail(msginfo != NULL, -1);
559
560         folder = dest->folder;
561
562         g_return_val_if_fail(folder->scan != NULL, -1);
563         g_return_val_if_fail(folder->move_msg != NULL, -1);
564
565         if (dest->last_num < 0) folder->scan(folder, dest);
566
567         num = folder->move_msg(folder, dest, msginfo);
568         if (num > 0) dest->last_num = num;
569
570         return num;
571 }
572 */
573
574 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
575 {
576         Folder *folder;
577         gint num;
578         gchar * filename;
579         Folder * src_folder;
580
581         g_return_val_if_fail(dest != NULL, -1);
582         g_return_val_if_fail(msginfo != NULL, -1);
583
584         folder = dest->folder;
585
586         g_return_val_if_fail(folder->scan != NULL, -1);
587         g_return_val_if_fail(folder->remove_msg != NULL, -1);
588         g_return_val_if_fail(folder->copy_msg != NULL, -1);
589
590         if (dest->last_num < 0) folder->scan(folder, dest);
591
592         src_folder = msginfo->folder->folder;
593
594         num = folder->copy_msg(folder, dest, msginfo);
595         if (num != -1)
596                 src_folder->remove_msg(src_folder,
597                                        msginfo->folder,
598                                        msginfo->msgnum);
599         
600         if (folder->finished_copy)
601                 folder->finished_copy(folder, dest);
602
603         src_folder = msginfo->folder->folder;
604
605         if (msginfo->folder && src_folder->scan)
606                 src_folder->scan(src_folder, msginfo->folder);
607         folder->scan(folder, dest);
608
609         return num;
610 }
611
612 /*
613 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
614 {
615         Folder *folder;
616         gint num;
617
618         g_return_val_if_fail(dest != NULL, -1);
619         g_return_val_if_fail(msglist != NULL, -1);
620
621         folder = dest->folder;
622
623         g_return_val_if_fail(folder->scan != NULL, -1);
624         g_return_val_if_fail(folder->move_msgs_with_dest != NULL, -1);
625
626         if (dest->last_num < 0) folder->scan(folder, dest);
627
628         num = folder->move_msgs_with_dest(folder, dest, msglist);
629         if (num > 0) dest->last_num = num;
630
631         return num;
632 }
633 */
634
635 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
636 {
637         Folder *folder;
638         FolderItem * item;
639         GSList * l;
640         gchar * filename;
641
642         g_return_val_if_fail(dest != NULL, -1);
643         g_return_val_if_fail(msglist != NULL, -1);
644
645         folder = dest->folder;
646
647         g_return_val_if_fail(folder->scan != NULL, -1);
648         g_return_val_if_fail(folder->copy_msg != NULL, -1);
649         g_return_val_if_fail(folder->remove_msg != NULL, -1);
650
651         if (dest->last_num < 0) folder->scan(folder, dest);
652
653         item = NULL;
654         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
655                 MsgInfo * msginfo = (MsgInfo *) l->data;
656
657                 if (!item && msginfo->folder != NULL)
658                         item = msginfo->folder;
659
660                 if (folder->copy_msg(folder, dest, msginfo) != -1)
661                         item->folder->remove_msg(item->folder,
662                                                  msginfo->folder,
663                                                  msginfo->msgnum);
664         }
665
666         if (folder->finished_copy)
667                 folder->finished_copy(folder, dest);
668
669         if (item && item->folder->scan)
670                 item->folder->scan(item->folder, item);
671         folder->scan(folder, dest);
672
673         return dest->last_num;
674 }
675
676 /*
677 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
678 {
679         Folder *folder;
680         gint num;
681
682         g_return_val_if_fail(dest != NULL, -1);
683         g_return_val_if_fail(msginfo != NULL, -1);
684
685         folder = dest->folder;
686
687         g_return_val_if_fail(folder->scan != NULL, -1);
688         g_return_val_if_fail(folder->copy_msg != NULL, -1);
689
690         if (dest->last_num < 0) folder->scan(folder, dest);
691
692         num = folder->copy_msg(folder, dest, msginfo);
693         if (num > 0) dest->last_num = num;
694
695         return num;
696 }
697 */
698
699 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
700 {
701         Folder *folder;
702         gint num;
703         gchar * filename;
704         Folder * src_folder;
705
706         g_return_val_if_fail(dest != NULL, -1);
707         g_return_val_if_fail(msginfo != NULL, -1);
708
709         folder = dest->folder;
710
711         g_return_val_if_fail(folder->scan != NULL, -1);
712         g_return_val_if_fail(folder->copy_msg != NULL, -1);
713
714         if (dest->last_num < 0) folder->scan(folder, dest);
715         
716         num = folder->copy_msg(folder, dest, msginfo);
717
718         if (folder->finished_copy)
719                 folder->finished_copy(folder, dest);
720
721         folder->scan(folder, dest);
722
723         return num;
724 }
725
726 /*
727 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
728 {
729         Folder *folder;
730         gint num;
731
732         g_return_val_if_fail(dest != NULL, -1);
733         g_return_val_if_fail(msglist != NULL, -1);
734
735         folder = dest->folder;
736
737         g_return_val_if_fail(folder->scan != NULL, -1);
738         g_return_val_if_fail(folder->copy_msgs_with_dest != NULL, -1);
739
740         if (dest->last_num < 0) folder->scan(folder, dest);
741
742         num = folder->copy_msgs_with_dest(folder, dest, msglist);
743         if (num > 0) dest->last_num = num;
744
745         return num;
746 }
747 */
748
749 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
750 {
751         Folder *folder;
752         gint num;
753         GSList * l;
754         gchar * filename;
755
756         g_return_val_if_fail(dest != NULL, -1);
757         g_return_val_if_fail(msglist != NULL, -1);
758
759         folder = dest->folder;
760  
761         g_return_val_if_fail(folder->scan != NULL, -1);
762         g_return_val_if_fail(folder->copy_msg != NULL, -1);
763
764         if (dest->last_num < 0) folder->scan(folder, dest);
765
766         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
767                 MsgInfo * msginfo = (MsgInfo *) l->data;
768
769                 folder->copy_msg(folder, dest, msginfo);
770         }
771
772         if (folder->finished_copy)
773                 folder->finished_copy(folder, dest);
774
775         folder->scan(folder, dest);
776
777         return dest->last_num;
778 }
779
780 gint folder_item_remove_msg(FolderItem *item, gint num)
781 {
782         Folder *folder;
783
784         g_return_val_if_fail(item != NULL, -1);
785
786         folder = item->folder;
787
788         g_return_val_if_fail(folder->scan != NULL, -1);
789         g_return_val_if_fail(folder->remove_msg != NULL, -1);
790
791         if (item->last_num < 0) folder->scan(folder, item);
792
793         return folder->remove_msg(folder, item, num);
794 }
795
796 gint folder_item_remove_all_msg(FolderItem *item)
797 {
798         Folder *folder;
799
800         g_return_val_if_fail(item != NULL, -1);
801
802         folder = item->folder;
803
804         g_return_val_if_fail(folder->scan != NULL, -1);
805         g_return_val_if_fail(folder->remove_all_msg != NULL, -1);
806
807         if (item->last_num < 0) folder->scan(folder, item);
808
809         return folder->remove_all_msg(folder, item);
810 }
811
812 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
813 {
814         Folder *folder;
815
816         g_return_val_if_fail(item != NULL, FALSE);
817
818         folder = item->folder;
819
820         g_return_val_if_fail(folder->is_msg_changed != NULL, -1);
821
822         return folder->is_msg_changed(folder, item, msginfo);
823 }
824
825 gchar *folder_item_get_cache_file(FolderItem *item)
826 {
827         gchar *path;
828         gchar *file;
829
830         g_return_val_if_fail(item != NULL, NULL);
831         g_return_val_if_fail(item->path != NULL, NULL);
832
833         path = folder_item_get_path(item);
834         g_return_val_if_fail(path != NULL, NULL);
835         if (!is_dir_exist(path))
836                 make_dir_hier(path);
837         file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
838         g_free(path);
839
840         return file;
841 }
842
843 gchar *folder_item_get_mark_file(FolderItem *item)
844 {
845         gchar *path;
846         gchar *file;
847
848         g_return_val_if_fail(item != NULL, NULL);
849         g_return_val_if_fail(item->path != NULL, NULL);
850
851         path = folder_item_get_path(item);
852         g_return_val_if_fail(path != NULL, NULL);
853         if (!is_dir_exist(path))
854                 make_dir_hier(path);
855         file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
856         g_free(path);
857
858         return file;
859 }
860
861
862 static void folder_init(Folder *folder, FolderType type, const gchar *name)
863 {
864         FolderItem *item;
865
866         g_return_if_fail(folder != NULL);
867
868         folder->type = type;
869         folder_set_name(folder, name);
870         folder->account = NULL;
871         folder->inbox = NULL;
872         folder->outbox = NULL;
873         folder->draft = NULL;
874         folder->queue = NULL;
875         folder->trash = NULL;
876         folder->ui_func = NULL;
877         folder->ui_func_data = NULL;
878         item = folder_item_new(name, NULL);
879         item->folder = folder;
880         folder->node = g_node_new(item);
881         folder->data = NULL;
882
883         switch (type) {
884         case F_MH:
885                 folder->get_msg_list        = mh_get_msg_list;
886                 folder->fetch_msg           = mh_fetch_msg;
887                 folder->add_msg             = mh_add_msg;
888                 /*
889                 folder->move_msg            = mh_move_msg;
890                 folder->move_msgs_with_dest = mh_move_msgs_with_dest;
891                 folder->copy_msg            = mh_copy_msg;
892                 folder->copy_msgs_with_dest = mh_copy_msgs_with_dest;
893                 */
894                 folder->copy_msg            = mh_copy_msg;
895                 folder->remove_msg          = mh_remove_msg;
896                 folder->remove_all_msg      = mh_remove_all_msg;
897                 folder->is_msg_changed      = mh_is_msg_changed;
898                 folder->scan                = mh_scan_folder;
899                 folder->scan_tree           = mh_scan_tree;
900                 folder->create_tree         = mh_create_tree;
901                 folder->create_folder       = mh_create_folder;
902                 folder->rename_folder       = mh_rename_folder;
903                 folder->remove_folder       = mh_remove_folder;
904                 break;
905         case F_IMAP:
906                 folder->get_msg_list        = imap_get_msg_list;
907                 folder->fetch_msg           = imap_fetch_msg;
908                 /*
909                 folder->move_msg            = imap_move_msg;
910                 folder->move_msgs_with_dest = imap_move_msgs_with_dest;
911                 */
912                 folder->remove_msg          = imap_remove_msg;
913                 folder->remove_all_msg      = imap_remove_all_msg;
914                 folder->scan                = imap_scan_folder;
915                 folder->create_folder       = imap_create_folder;
916                 folder->remove_folder       = imap_remove_folder;               
917                 break;
918         case F_NEWS:
919                 folder->get_msg_list        = news_get_article_list;
920                 folder->fetch_msg           = news_fetch_msg;
921                 folder->scan                = news_scan_group;
922                 break;
923         case F_MBOX:
924                 folder->get_msg_list        = mbox_get_msg_list;
925                 folder->fetch_msg           = mbox_fetch_msg;
926                 folder->scan                = mbox_scan_folder;
927                 folder->add_msg             = mbox_add_msg;
928                 folder->remove_all_msg      = mbox_remove_all_msg;
929                 folder->remove_msg          = mbox_remove_msg;
930                 /*
931                 folder->move_msg            = mbox_move_msg;
932                 folder->move_msgs_with_dest = mbox_move_msgs_with_dest;
933                 folder->copy_msg            = mbox_copy_msg;
934                 folder->copy_msgs_with_dest = mbox_copy_msgs_with_dest;
935                 */
936                 folder->copy_msg            = mbox_copy_msg;
937
938                 folder->create_tree         = mbox_create_tree;
939                 folder->create_folder       = mbox_create_folder;
940                 folder->rename_folder       = mbox_rename_folder;
941                 folder->remove_folder       = mbox_remove_folder;
942
943                 folder->update_mark         = mbox_update_mark;
944                 folder->change_flags        = mbox_change_flags;
945                 folder->finished_copy       = mbox_finished_copy;
946
947                 break;
948         default:
949         }
950
951         switch (type) {
952         case F_MH:
953         case F_MBOX:
954         case F_MAILDIR:
955                 LOCAL_FOLDER(folder)->rootpath = NULL;
956                 break;
957         case F_IMAP:
958         case F_NEWS:
959                 REMOTE_FOLDER(folder)->session   = NULL;
960                 break;
961         default:
962         }
963 }
964
965 static void local_folder_destroy(LocalFolder *lfolder)
966 {
967         g_return_if_fail(lfolder != NULL);
968
969         g_free(lfolder->rootpath);
970 }
971
972 static void remote_folder_destroy(RemoteFolder *rfolder)
973 {
974         g_return_if_fail(rfolder != NULL);
975
976         if (rfolder->session)
977                 session_destroy(rfolder->session);
978 }
979
980 static void mh_folder_destroy(MHFolder *folder)
981 {
982         local_folder_destroy(LOCAL_FOLDER(folder));
983 }
984
985 static void mbox_folder_destroy(MboxFolder *folder)
986 {
987         local_folder_destroy(LOCAL_FOLDER(folder));
988 }
989
990 static void imap_folder_destroy(IMAPFolder *folder)
991 {
992         remote_folder_destroy(REMOTE_FOLDER(folder));
993 }
994
995 static void news_folder_destroy(NewsFolder *folder)
996 {
997         remote_folder_destroy(REMOTE_FOLDER(folder));
998 }
999
1000 static gboolean folder_build_tree(GNode *node, gpointer data)
1001 {
1002         Folder *folder = FOLDER(data);
1003         FolderItem *item;
1004         XMLNode *xmlnode;
1005         GList *list;
1006         SpecialFolderItemType stype = F_NORMAL;
1007         const gchar *name = NULL;
1008         const gchar *path = NULL;
1009         PrefsAccount *account = NULL;
1010         gint mtime = 0, new = 0, unread = 0, total = 0;
1011
1012         g_return_val_if_fail(node->data != NULL, FALSE);
1013         if (!node->parent) return FALSE;
1014
1015         xmlnode = node->data;
1016         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1017                 g_warning("tag name != \"folderitem\"\n");
1018                 return FALSE;
1019         }
1020
1021         list = xmlnode->tag->attr;
1022         for (; list != NULL; list = list->next) {
1023                 XMLAttr *attr = list->data;
1024
1025                 if (!attr || !attr->name || !attr->value) continue;
1026                 if (!strcmp(attr->name, "type")) {
1027                         if (!strcasecmp(attr->value, "normal"))
1028                                 stype = F_NORMAL;
1029                         else if (!strcasecmp(attr->value, "inbox"))
1030                                 stype = F_INBOX;
1031                         else if (!strcasecmp(attr->value, "outbox"))
1032                                 stype = F_OUTBOX;
1033                         else if (!strcasecmp(attr->value, "draft"))
1034                                 stype = F_DRAFT;
1035                         else if (!strcasecmp(attr->value, "queue"))
1036                                 stype = F_QUEUE;
1037                         else if (!strcasecmp(attr->value, "trash"))
1038                                 stype = F_TRASH;
1039                 } else if (!strcmp(attr->name, "name"))
1040                         name = attr->value;
1041                 else if (!strcmp(attr->name, "path"))
1042                         path = attr->value;
1043                 else if (!strcmp(attr->name, "account_id")) {
1044                         account = account_find_from_id(atoi(attr->value));
1045                         if (!account) g_warning("account_id: %s not found\n",
1046                                                 attr->value);
1047                 }
1048                 else if (!strcmp(attr->name, "mtime"))
1049                         mtime = atoi(attr->value);
1050                 else if (!strcmp(attr->name, "new"))
1051                         new = atoi(attr->value);
1052                 else if (!strcmp(attr->name, "unread"))
1053                         unread = atoi(attr->value);
1054                 else if (!strcmp(attr->name, "total"))
1055                         total = atoi(attr->value);
1056         }
1057
1058         item = folder_item_new(name, path);
1059         item->stype = stype;
1060         item->account = account;
1061         item->mtime = mtime;
1062         item->new = new;
1063         item->unread = unread;
1064         item->total = total;
1065         item->parent = FOLDER_ITEM(node->parent->data);
1066         item->folder = folder;
1067         switch (stype) {
1068         case F_INBOX:  folder->inbox  = item; break;
1069         case F_OUTBOX: folder->outbox = item; break;
1070         case F_DRAFT:  folder->draft  = item; break;
1071         case F_QUEUE:  folder->queue  = item; break;
1072         case F_TRASH:  folder->trash  = item; break;
1073         default:
1074         }
1075
1076         prefs_folder_item_read_config(item);
1077
1078         node->data = item;
1079         xml_free_node(xmlnode);
1080
1081         return FALSE;
1082 }
1083
1084 static gboolean folder_read_folder_func(GNode *node, gpointer data)
1085 {
1086         Folder *folder;
1087         XMLNode *xmlnode;
1088         GList *list;
1089         FolderType type = F_UNKNOWN;
1090         const gchar *name = NULL;
1091         const gchar *path = NULL;
1092         PrefsAccount *account = NULL;
1093
1094         if (g_node_depth(node) != 2) return FALSE;
1095         g_return_val_if_fail(node->data != NULL, FALSE);
1096
1097         xmlnode = node->data;
1098         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1099                 g_warning("tag name != \"folder\"\n");
1100                 return TRUE;
1101         }
1102         g_node_unlink(node);
1103         list = xmlnode->tag->attr;
1104         for (; list != NULL; list = list->next) {
1105                 XMLAttr *attr = list->data;
1106
1107                 if (!attr || !attr->name || !attr->value) continue;
1108                 if (!strcmp(attr->name, "type")) {
1109                         if (!strcasecmp(attr->value, "mh"))
1110                                 type = F_MH;
1111                         else if (!strcasecmp(attr->value, "mbox"))
1112                                 type = F_MBOX;
1113                         else if (!strcasecmp(attr->value, "maildir"))
1114                                 type = F_MAILDIR;
1115                         else if (!strcasecmp(attr->value, "imap"))
1116                                 type = F_IMAP;
1117                         else if (!strcasecmp(attr->value, "news"))
1118                                 type = F_NEWS;
1119                 } else if (!strcmp(attr->name, "name"))
1120                         name = attr->value;
1121                 else if (!strcmp(attr->name, "path"))
1122                         path = attr->value;
1123                 else if (!strcmp(attr->name, "account_id")) {
1124                         account = account_find_from_id(atoi(attr->value));
1125                         if (!account) g_warning("account_id: %s not found\n",
1126                                                 attr->value);
1127                 }
1128         }
1129
1130         folder = folder_new(type, name, path);
1131         g_return_val_if_fail(folder != NULL, FALSE);
1132         folder->account = account;
1133         if (account && (type == F_IMAP || type == F_NEWS))
1134                 account->folder = REMOTE_FOLDER(folder);
1135         node->data = folder->node->data;
1136         g_node_destroy(folder->node);
1137         folder->node = node;
1138         folder_add(folder);
1139
1140         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1141                         folder_build_tree, folder);
1142
1143         return FALSE;
1144 }
1145
1146 static gchar *folder_get_list_path(void)
1147 {
1148         static gchar *filename = NULL;
1149
1150         if (!filename)
1151                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1152                                         FOLDER_LIST, NULL);
1153
1154         return filename;
1155 }
1156
1157 static void folder_write_list_recursive(GNode *node, gpointer data)
1158 {
1159         FILE *fp = (FILE *)data;
1160         FolderItem *item = FOLDER_ITEM(node->data);
1161         gint i, depth;
1162         static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1163                                            "news", "unknown"};
1164         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1165                                                  "draft", "queue", "trash"};
1166
1167         g_return_if_fail(item != NULL);
1168
1169         depth = g_node_depth(node);
1170         for (i = 0; i < depth; i++)
1171                 fputs("    ", fp);
1172         if (depth == 1) {
1173                 Folder *folder = item->folder;
1174
1175                 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
1176                 if (folder->name) {
1177                         fputs(" name=\"", fp);
1178                         xml_file_put_escape_str(fp, folder->name);
1179                         fputs("\"", fp);
1180                 }
1181                 if ((folder->type == F_MH) || (folder->type == F_MBOX)) {
1182                         fputs(" path=\"", fp);
1183                         xml_file_put_escape_str
1184                                 (fp, LOCAL_FOLDER(folder)->rootpath);
1185                         fputs("\"", fp);
1186                 }
1187                 if (folder->account)
1188                         fprintf(fp, " account_id=\"%d\"",
1189                                 folder->account->account_id);
1190         } else {
1191                 fprintf(fp, "<folderitem type=\"%s\"",
1192                         folder_item_stype_str[item->stype]);
1193                 if (item->name) {
1194                         fputs(" name=\"", fp);
1195                         xml_file_put_escape_str(fp, item->name);
1196                         fputs("\"", fp);
1197                 }
1198                 if (item->path) {
1199                         fputs(" path=\"", fp);
1200                         xml_file_put_escape_str(fp, item->path);
1201                         fputs("\"", fp);
1202                 }
1203                 if (item->account)
1204                         fprintf(fp, " account_id = \"%d\"",
1205                                 item->account->account_id);
1206                 fprintf(fp,
1207                         " mtime=\"%ld\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1208                         item->mtime, item->new, item->unread, item->total);
1209         }
1210
1211         if (node->children) {
1212                 GNode *child;
1213                 fputs(">\n", fp);
1214
1215                 child = node->children;
1216                 while (child) {
1217                         GNode *cur;
1218
1219                         cur = child;
1220                         child = cur->next;
1221                         folder_write_list_recursive(cur, data);
1222                 }
1223
1224                 for (i = 0; i < depth; i++)
1225                         fputs("    ", fp);
1226                 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1227         } else
1228                 fputs(" />\n", fp);
1229 }
1230
1231 typedef struct _type_str {
1232         gchar * str;
1233         gint type;
1234 } type_str;
1235
1236
1237 static type_str type_str_table[] = 
1238 {
1239         {"#mh", F_MH},
1240         {"#mbox", F_MBOX},
1241         {"#maildir", F_MAILDIR},
1242         {"#imap", F_IMAP},
1243         {"#news", F_NEWS}
1244 };
1245
1246
1247 static gchar * folder_get_type_string(gint type)
1248 {
1249         gint i;
1250
1251         for(i = 0 ; i < sizeof(type_str_table) / sizeof(type_str) ; i++) {
1252                 if (type_str_table[i].type == type)
1253                         return type_str_table[i].str;
1254         }
1255         return NULL;
1256 }
1257
1258 static gint folder_get_type_from_string(gchar * str)
1259 {
1260         gint i;
1261
1262         for(i = 0 ; i < sizeof(type_str_table) / sizeof(type_str) ; i++) {
1263                 if (g_strcasecmp(type_str_table[i].str, str))
1264                         return type_str_table[i].type;
1265         }
1266         return F_UNKNOWN;
1267 }
1268
1269 gchar * folder_get_identifier(Folder * folder)
1270 {
1271         gchar * type_str;
1272         type_str = folder_get_type_string(folder->type);
1273
1274         return g_strconcat(type_str, "/", folder->name, NULL);
1275 }
1276
1277 /*
1278 static gchar * folder_item_get_tree_identifier(FolderItem * item)
1279 {
1280         if (item->parent != NULL) {
1281                 gchar * path;
1282                 gchar * id;
1283
1284                 path = folder_item_get_tree_identifier(item->parent);
1285                 if (path == NULL)
1286                         return NULL;
1287
1288                 id = g_strconcat(path, "/", item->name, NULL);
1289                 g_free(path);
1290
1291                 return id;
1292         }
1293         else {
1294                 return g_strconcat("/", item->name, NULL);
1295         }
1296 }
1297 */
1298
1299 gchar * folder_item_get_identifier(FolderItem * item)
1300 {
1301         gchar * path;
1302         gchar * id;
1303         gchar * folder_str;
1304
1305         folder_str = folder_get_identifier(item->folder);
1306
1307         if (item->path == NULL) {
1308                 g_free(folder_str);
1309                 return NULL;
1310         }
1311
1312         id = g_strconcat(folder_str, "/", item->path, NULL);
1313
1314         return id;
1315 }
1316
1317 Folder * folder_find_from_name(const gchar * name)
1318 {
1319         GList *list;
1320         Folder *folder;
1321
1322         for (list = g_list_first(folder_list); list != NULL;
1323              list = list->next) {
1324                 folder = list->data;
1325                 if (strcmp(name, folder->name) == 0)
1326                         return folder;
1327         }
1328         return NULL;
1329 }
1330
1331 FolderItem * folder_find_item_from_identifier(const gchar *identifier)
1332 {
1333         Folder *folder;
1334         gpointer d[2];
1335         gchar * str;
1336         gchar * p;
1337         gint type;
1338         gchar * name;
1339         gchar * path;
1340
1341         Xalloca(str, strlen(identifier) + 1, return NULL);
1342         strcpy(str, identifier);
1343
1344         /* extract box type */
1345
1346         p = strchr(str, '/');
1347
1348         if (p == NULL)
1349                 return NULL;
1350
1351         *p = '\0';
1352
1353         type = folder_get_type_from_string(str);
1354
1355         str = p + 1;
1356
1357         /* extract box name */
1358
1359         p = strchr(str, '/');
1360
1361         if (p == NULL)
1362                 return NULL;
1363
1364         *p = '\0';
1365
1366         name = str;
1367
1368         folder = folder_find_from_name(name);
1369         if (folder == NULL)
1370                 return;
1371
1372         path = p + 1;
1373
1374         d[0] = (gpointer)path;
1375         d[1] = NULL;
1376         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1377                         folder_item_find_func, d);
1378         return d[1];
1379 }