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