sync with sylpheed 0.4.99cvs8
[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 "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                 folder->add_msg             = imap_add_msg;
928                 folder->move_msg            = imap_move_msg;
929                 folder->move_msgs_with_dest = imap_move_msgs_with_dest;
930                 folder->copy_msg            = imap_copy_msg;
931                 folder->copy_msgs_with_dest = imap_copy_msgs_with_dest;
932                 folder->remove_msg          = imap_remove_msg;
933                 folder->remove_all_msg      = imap_remove_all_msg;
934                 folder->scan                = imap_scan_folder;
935                 folder->create_folder       = imap_create_folder;
936                 folder->remove_folder       = imap_remove_folder;
937                 break;
938         case F_NEWS:
939                 folder->get_msg_list        = news_get_article_list;
940                 folder->fetch_msg           = news_fetch_msg;
941                 folder->scan                = news_scan_group;
942                 break;
943         case F_MBOX:
944                 folder->get_msg_list        = mbox_get_msg_list;
945                 folder->fetch_msg           = mbox_fetch_msg;
946                 folder->scan                = mbox_scan_folder;
947                 folder->add_msg             = mbox_add_msg;
948                 folder->remove_all_msg      = mbox_remove_all_msg;
949                 folder->remove_msg          = mbox_remove_msg;
950                 /*
951                 folder->move_msg            = mbox_move_msg;
952                 folder->move_msgs_with_dest = mbox_move_msgs_with_dest;
953                 folder->copy_msg            = mbox_copy_msg;
954                 folder->copy_msgs_with_dest = mbox_copy_msgs_with_dest;
955                 */
956                 folder->copy_msg            = mbox_copy_msg;
957
958                 folder->create_tree         = mbox_create_tree;
959                 folder->create_folder       = mbox_create_folder;
960                 folder->rename_folder       = mbox_rename_folder;
961                 folder->remove_folder       = mbox_remove_folder;
962
963                 folder->update_mark         = mbox_update_mark;
964                 folder->change_flags        = mbox_change_flags;
965                 folder->finished_copy       = mbox_finished_copy;
966
967                 break;
968         default:
969         }
970
971         switch (type) {
972         case F_MH:
973         case F_MBOX:
974         case F_MAILDIR:
975                 LOCAL_FOLDER(folder)->rootpath = NULL;
976                 break;
977         case F_IMAP:
978         case F_NEWS:
979                 REMOTE_FOLDER(folder)->session = NULL;
980                 break;
981         default:
982         }
983 }
984
985 static void local_folder_destroy(LocalFolder *lfolder)
986 {
987         g_return_if_fail(lfolder != NULL);
988
989         g_free(lfolder->rootpath);
990 }
991
992 static void remote_folder_destroy(RemoteFolder *rfolder)
993 {
994         g_return_if_fail(rfolder != NULL);
995
996         if (rfolder->session)
997                 session_destroy(rfolder->session);
998 }
999
1000 static void mh_folder_destroy(MHFolder *folder)
1001 {
1002         local_folder_destroy(LOCAL_FOLDER(folder));
1003 }
1004
1005 static void mbox_folder_destroy(MboxFolder *folder)
1006 {
1007         local_folder_destroy(LOCAL_FOLDER(folder));
1008 }
1009
1010 static void imap_folder_destroy(IMAPFolder *folder)
1011 {
1012         remote_folder_destroy(REMOTE_FOLDER(folder));
1013 }
1014
1015 static void news_folder_destroy(NewsFolder *folder)
1016 {
1017         remote_folder_destroy(REMOTE_FOLDER(folder));
1018 }
1019
1020 static gboolean folder_build_tree(GNode *node, gpointer data)
1021 {
1022         Folder *folder = FOLDER(data);
1023         FolderItem *item;
1024         XMLNode *xmlnode;
1025         GList *list;
1026         SpecialFolderItemType stype = F_NORMAL;
1027         const gchar *name = NULL;
1028         const gchar *path = NULL;
1029         PrefsAccount *account = NULL;
1030         gint mtime = 0, new = 0, unread = 0, total = 0;
1031
1032         g_return_val_if_fail(node->data != NULL, FALSE);
1033         if (!node->parent) return FALSE;
1034
1035         xmlnode = node->data;
1036         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1037                 g_warning("tag name != \"folderitem\"\n");
1038                 return FALSE;
1039         }
1040
1041         list = xmlnode->tag->attr;
1042         for (; list != NULL; list = list->next) {
1043                 XMLAttr *attr = list->data;
1044
1045                 if (!attr || !attr->name || !attr->value) continue;
1046                 if (!strcmp(attr->name, "type")) {
1047                         if (!strcasecmp(attr->value, "normal"))
1048                                 stype = F_NORMAL;
1049                         else if (!strcasecmp(attr->value, "inbox"))
1050                                 stype = F_INBOX;
1051                         else if (!strcasecmp(attr->value, "outbox"))
1052                                 stype = F_OUTBOX;
1053                         else if (!strcasecmp(attr->value, "draft"))
1054                                 stype = F_DRAFT;
1055                         else if (!strcasecmp(attr->value, "queue"))
1056                                 stype = F_QUEUE;
1057                         else if (!strcasecmp(attr->value, "trash"))
1058                                 stype = F_TRASH;
1059                 } else if (!strcmp(attr->name, "name"))
1060                         name = attr->value;
1061                 else if (!strcmp(attr->name, "path"))
1062                         path = attr->value;
1063                 else if (!strcmp(attr->name, "account_id")) {
1064                         account = account_find_from_id(atoi(attr->value));
1065                         if (!account) g_warning("account_id: %s not found\n",
1066                                                 attr->value);
1067                 }
1068                 else if (!strcmp(attr->name, "mtime"))
1069                         mtime = atoi(attr->value);
1070                 else if (!strcmp(attr->name, "new"))
1071                         new = atoi(attr->value);
1072                 else if (!strcmp(attr->name, "unread"))
1073                         unread = atoi(attr->value);
1074                 else if (!strcmp(attr->name, "total"))
1075                         total = atoi(attr->value);
1076         }
1077
1078         item = folder_item_new(name, path);
1079         item->stype = stype;
1080         item->account = account;
1081         item->mtime = mtime;
1082         item->new = new;
1083         item->unread = unread;
1084         item->total = total;
1085         item->parent = FOLDER_ITEM(node->parent->data);
1086         item->folder = folder;
1087         switch (stype) {
1088         case F_INBOX:  folder->inbox  = item; break;
1089         case F_OUTBOX: folder->outbox = item; break;
1090         case F_DRAFT:  folder->draft  = item; break;
1091         case F_QUEUE:  folder->queue  = item; break;
1092         case F_TRASH:  folder->trash  = item; break;
1093         default:
1094         }
1095
1096         prefs_folder_item_read_config(item);
1097
1098         node->data = item;
1099         xml_free_node(xmlnode);
1100
1101         return FALSE;
1102 }
1103
1104 static gboolean folder_read_folder_func(GNode *node, gpointer data)
1105 {
1106         Folder *folder;
1107         XMLNode *xmlnode;
1108         GList *list;
1109         FolderType type = F_UNKNOWN;
1110         const gchar *name = NULL;
1111         const gchar *path = NULL;
1112         PrefsAccount *account = NULL;
1113
1114         if (g_node_depth(node) != 2) return FALSE;
1115         g_return_val_if_fail(node->data != NULL, FALSE);
1116
1117         xmlnode = node->data;
1118         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1119                 g_warning("tag name != \"folder\"\n");
1120                 return TRUE;
1121         }
1122         g_node_unlink(node);
1123         list = xmlnode->tag->attr;
1124         for (; list != NULL; list = list->next) {
1125                 XMLAttr *attr = list->data;
1126
1127                 if (!attr || !attr->name || !attr->value) continue;
1128                 if (!strcmp(attr->name, "type")) {
1129                         if (!strcasecmp(attr->value, "mh"))
1130                                 type = F_MH;
1131                         else if (!strcasecmp(attr->value, "mbox"))
1132                                 type = F_MBOX;
1133                         else if (!strcasecmp(attr->value, "maildir"))
1134                                 type = F_MAILDIR;
1135                         else if (!strcasecmp(attr->value, "imap"))
1136                                 type = F_IMAP;
1137                         else if (!strcasecmp(attr->value, "news"))
1138                                 type = F_NEWS;
1139                 } else if (!strcmp(attr->name, "name"))
1140                         name = attr->value;
1141                 else if (!strcmp(attr->name, "path"))
1142                         path = attr->value;
1143                 else if (!strcmp(attr->name, "account_id")) {
1144                         account = account_find_from_id(atoi(attr->value));
1145                         if (!account) g_warning("account_id: %s not found\n",
1146                                                 attr->value);
1147                 }
1148         }
1149
1150         folder = folder_new(type, name, path);
1151         g_return_val_if_fail(folder != NULL, FALSE);
1152         folder->account = account;
1153         if (account && (type == F_IMAP || type == F_NEWS))
1154                 account->folder = REMOTE_FOLDER(folder);
1155         node->data = folder->node->data;
1156         g_node_destroy(folder->node);
1157         folder->node = node;
1158         folder_add(folder);
1159
1160         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1161                         folder_build_tree, folder);
1162
1163         return FALSE;
1164 }
1165
1166 static gchar *folder_get_list_path(void)
1167 {
1168         static gchar *filename = NULL;
1169
1170         if (!filename)
1171                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1172                                         FOLDER_LIST, NULL);
1173
1174         return filename;
1175 }
1176
1177 static void folder_write_list_recursive(GNode *node, gpointer data)
1178 {
1179         FILE *fp = (FILE *)data;
1180         FolderItem *item = FOLDER_ITEM(node->data);
1181         gint i, depth;
1182         static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1183                                            "news", "unknown"};
1184         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1185                                                  "draft", "queue", "trash"};
1186
1187         g_return_if_fail(item != NULL);
1188
1189         depth = g_node_depth(node);
1190         for (i = 0; i < depth; i++)
1191                 fputs("    ", fp);
1192         if (depth == 1) {
1193                 Folder *folder = item->folder;
1194
1195                 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
1196                 if (folder->name) {
1197                         fputs(" name=\"", fp);
1198                         xml_file_put_escape_str(fp, folder->name);
1199                         fputs("\"", fp);
1200                 }
1201                 if ((folder->type == F_MH) || (folder->type == F_MBOX)) {
1202                         fputs(" path=\"", fp);
1203                         xml_file_put_escape_str
1204                                 (fp, LOCAL_FOLDER(folder)->rootpath);
1205                         fputs("\"", fp);
1206                 }
1207                 if (folder->account)
1208                         fprintf(fp, " account_id=\"%d\"",
1209                                 folder->account->account_id);
1210         } else {
1211                 fprintf(fp, "<folderitem type=\"%s\"",
1212                         folder_item_stype_str[item->stype]);
1213                 if (item->name) {
1214                         fputs(" name=\"", fp);
1215                         xml_file_put_escape_str(fp, item->name);
1216                         fputs("\"", fp);
1217                 }
1218                 if (item->path) {
1219                         fputs(" path=\"", fp);
1220                         xml_file_put_escape_str(fp, item->path);
1221                         fputs("\"", fp);
1222                 }
1223                 if (item->account)
1224                         fprintf(fp, " account_id = \"%d\"",
1225                                 item->account->account_id);
1226                 fprintf(fp,
1227                         " mtime=\"%ld\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1228                         item->mtime, item->new, item->unread, item->total);
1229         }
1230
1231         if (node->children) {
1232                 GNode *child;
1233                 fputs(">\n", fp);
1234
1235                 child = node->children;
1236                 while (child) {
1237                         GNode *cur;
1238
1239                         cur = child;
1240                         child = cur->next;
1241                         folder_write_list_recursive(cur, data);
1242                 }
1243
1244                 for (i = 0; i < depth; i++)
1245                         fputs("    ", fp);
1246                 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1247         } else
1248                 fputs(" />\n", fp);
1249 }
1250
1251 typedef struct _type_str {
1252         gchar * str;
1253         gint type;
1254 } type_str;
1255
1256
1257 static type_str type_str_table[] = 
1258 {
1259         {"#mh", F_MH},
1260         {"#mbox", F_MBOX},
1261         {"#maildir", F_MAILDIR},
1262         {"#imap", F_IMAP},
1263         {"#news", F_NEWS}
1264 };
1265
1266
1267 static gchar * folder_get_type_string(gint type)
1268 {
1269         gint i;
1270
1271         for(i = 0 ; i < sizeof(type_str_table) / sizeof(type_str) ; i++) {
1272                 if (type_str_table[i].type == type)
1273                         return type_str_table[i].str;
1274         }
1275         return NULL;
1276 }
1277
1278 static gint folder_get_type_from_string(gchar * str)
1279 {
1280         gint i;
1281
1282         for(i = 0 ; i < sizeof(type_str_table) / sizeof(type_str) ; i++) {
1283                 if (g_strcasecmp(type_str_table[i].str, str))
1284                         return type_str_table[i].type;
1285         }
1286         return F_UNKNOWN;
1287 }
1288
1289 gchar * folder_get_identifier(Folder * folder)
1290 {
1291         gchar * type_str;
1292         type_str = folder_get_type_string(folder->type);
1293
1294         return g_strconcat(type_str, "/", folder->name, NULL);
1295 }
1296
1297 /*
1298 static gchar * folder_item_get_tree_identifier(FolderItem * item)
1299 {
1300         if (item->parent != NULL) {
1301                 gchar * path;
1302                 gchar * id;
1303
1304                 path = folder_item_get_tree_identifier(item->parent);
1305                 if (path == NULL)
1306                         return NULL;
1307
1308                 id = g_strconcat(path, "/", item->name, NULL);
1309                 g_free(path);
1310
1311                 return id;
1312         }
1313         else {
1314                 return g_strconcat("/", item->name, NULL);
1315         }
1316 }
1317 */
1318
1319 gchar * folder_item_get_identifier(FolderItem * item)
1320 {
1321         gchar * path;
1322         gchar * id;
1323         gchar * folder_str;
1324
1325         folder_str = folder_get_identifier(item->folder);
1326
1327         if (item->path == NULL) {
1328                 g_free(folder_str);
1329                 return NULL;
1330         }
1331
1332         id = g_strconcat(folder_str, "/", item->path, NULL);
1333
1334         return id;
1335 }
1336
1337 Folder * folder_find_from_name(const gchar * name)
1338 {
1339         GList *list;
1340         Folder *folder;
1341
1342         for (list = g_list_first(folder_list); list != NULL;
1343              list = list->next) {
1344                 folder = list->data;
1345                 if (strcmp(name, folder->name) == 0)
1346                         return folder;
1347         }
1348         return NULL;
1349 }
1350
1351 FolderItem * folder_find_item_from_identifier(const gchar *identifier)
1352 {
1353         Folder *folder;
1354         gpointer d[2];
1355         gchar * str;
1356         gchar * p;
1357         gint type;
1358         gchar * name;
1359         gchar * path;
1360
1361         Xalloca(str, strlen(identifier) + 1, return NULL);
1362         strcpy(str, identifier);
1363
1364         /* extract box type */
1365
1366         p = strchr(str, '/');
1367
1368         if (p == NULL)
1369                 return NULL;
1370
1371         *p = '\0';
1372
1373         type = folder_get_type_from_string(str);
1374
1375         str = p + 1;
1376
1377         /* extract box name */
1378
1379         p = strchr(str, '/');
1380
1381         if (p == NULL)
1382                 return NULL;
1383
1384         *p = '\0';
1385
1386         name = str;
1387
1388         folder = folder_find_from_name(name);
1389         if (folder == NULL)
1390                 return;
1391
1392         path = p + 1;
1393
1394         d[0] = (gpointer)path;
1395         d[1] = NULL;
1396         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1397                         folder_item_find_func, d);
1398         return d[1];
1399 }