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