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