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