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