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