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