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