2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999,2000 Hiroyuki Yamamoto
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.
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.
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.
41 #include "prefs_account.h"
43 static GList *folder_list = NULL;
45 static void folder_init (Folder *folder,
49 static void local_folder_destroy (LocalFolder *lfolder);
50 static void remote_folder_destroy (RemoteFolder *rfolder);
51 static void mh_folder_destroy (MHFolder *folder);
52 static void imap_folder_destroy (IMAPFolder *folder);
53 static void news_folder_destroy (NewsFolder *folder);
55 static gboolean folder_read_folder_func (GNode *node,
57 static gchar *folder_get_list_path (void);
58 static void folder_write_list_recursive (GNode *node,
62 Folder *folder_new(FolderType type, const gchar *name, const gchar *path)
64 Folder *folder = NULL;
66 name = name ? name : path;
69 folder = mh_folder_new(name, path);
72 folder = imap_folder_new(name, path);
75 folder = news_folder_new(name, path);
84 Folder *mh_folder_new(const gchar *name, const gchar *path)
88 folder = (Folder *)g_new0(MHFolder, 1);
89 folder_init(folder, F_MH, name);
90 LOCAL_FOLDER(folder)->rootpath = g_strdup(path);
95 Folder *mbox_folder_new(const gchar *name, const gchar *path)
97 /* not yet implemented */
101 Folder *maildir_folder_new(const gchar *name, const gchar *path)
103 /* not yet implemented */
107 Folder *imap_folder_new(const gchar *name, const gchar *path)
111 folder = (Folder *)g_new0(IMAPFolder, 1);
112 folder_init(folder, F_IMAP, name);
117 Folder *news_folder_new(const gchar *name, const gchar *path)
121 folder = (Folder *)g_new0(NewsFolder, 1);
122 folder_init(folder, F_NEWS, name);
127 FolderItem *folder_item_new(const gchar *name, const gchar *path)
131 item = g_new0(FolderItem, 1);
133 item->stype = F_NORMAL;
134 item->name = g_strdup(name);
135 item->path = g_strdup(path);
136 item->account = NULL;
149 void folder_item_append(FolderItem *parent, FolderItem *item)
153 g_return_if_fail(parent != NULL);
154 g_return_if_fail(parent->folder != NULL);
155 g_return_if_fail(item != NULL);
157 node = parent->folder->node;
158 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, parent);
159 g_return_if_fail(node != NULL);
161 item->parent = parent;
162 item->folder = parent->folder;
163 g_node_append_data(node, item);
166 void folder_item_remove(FolderItem *item)
170 g_return_if_fail(item != NULL);
171 g_return_if_fail(item->folder != NULL);
173 node = item->folder->node;
174 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
175 g_return_if_fail(node != NULL);
177 /* TODO: free all FolderItem's first */
178 if (item->folder->node == node)
179 item->folder->node = NULL;
180 g_node_destroy(node);
183 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
185 g_return_if_fail(folder != NULL);
187 folder->ui_func = func;
188 folder->ui_func_data = data;
191 void folder_set_name(Folder *folder, const gchar *name)
193 g_return_if_fail(folder != NULL);
195 g_free(folder->name);
196 folder->name = name ? g_strdup(name) : NULL;
197 if (folder->node && folder->node->data) {
198 FolderItem *item = (FolderItem *)folder->node->data;
201 item->name = name ? g_strdup(name) : NULL;
205 void folder_destroy(Folder *folder)
207 g_return_if_fail(folder != NULL);
209 folder_list = g_list_remove(folder_list, folder);
211 switch (folder->type) {
213 mh_folder_destroy(MH_FOLDER(folder));
216 imap_folder_destroy(IMAP_FOLDER(folder));
219 news_folder_destroy(NEWS_FOLDER(folder));
224 folder_tree_destroy(folder);
225 g_free(folder->name);
229 void folder_tree_destroy(Folder *folder)
231 /* TODO: destroy all FolderItem before */
232 g_node_destroy(folder->node);
235 void folder_add(Folder *folder)
241 g_return_if_fail(folder != NULL);
243 for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
244 cur_folder = FOLDER(cur->data);
245 if (folder->type == F_MH) {
246 if (cur_folder->type != F_MH) break;
247 } else if (folder->type == F_IMAP) {
248 if (cur_folder->type != F_MH &&
249 cur_folder->type != F_IMAP) break;
250 } else if (folder->type == F_NEWS) {
251 if (cur_folder->type != F_MH &&
252 cur_folder->type != F_IMAP &&
253 cur_folder->type != F_NEWS) break;
257 folder_list = g_list_insert(folder_list, folder, i);
260 GList *folder_get_list(void)
265 gint folder_read_list(void)
270 node = xml_parse_file(folder_get_list_path());
271 if (!node) return -1;
273 xmlnode = node->data;
274 if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
275 g_warning("wrong folder list\n");
280 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
281 folder_read_folder_func, NULL);
290 void folder_write_list(void)
297 path = folder_get_list_path();
298 if ((pfile = prefs_write_open(path)) == NULL) return;
300 fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
301 conv_get_current_charset_str());
302 fputs("\n<folderlist>\n", pfile->fp);
304 for (list = folder_list; list != NULL; list = list->next) {
306 folder_write_list_recursive(folder->node, pfile->fp);
309 fputs("</folderlist>\n", pfile->fp);
311 if (prefs_write_close(pfile) < 0)
312 g_warning("failed to write folder list.\n");
315 Folder *folder_find_from_path(const gchar *path)
320 for (list = folder_list; list != NULL; list = list->next) {
322 if (folder->type == F_MH &&
323 !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
330 static gboolean folder_item_find_func(GNode *node, gpointer data)
332 FolderItem *item = node->data;
334 const gchar *path = d[0];
336 if (path_cmp(path, item->path) != 0)
344 FolderItem *folder_find_item_from_path(const gchar *path)
349 folder = folder_get_default_folder();
350 g_return_val_if_fail(folder != NULL, NULL);
352 d[0] = (gpointer)path;
354 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
355 folder_item_find_func, d);
359 Folder *folder_get_default_folder(void)
361 return FOLDER(folder_list->data);
364 FolderItem *folder_get_default_inbox(void)
368 folder = FOLDER(folder_list->data);
369 g_return_val_if_fail(folder != NULL, NULL);
370 return folder->inbox;
373 FolderItem *folder_get_default_outbox(void)
377 folder = FOLDER(folder_list->data);
378 g_return_val_if_fail(folder != NULL, NULL);
379 return folder->outbox;
382 FolderItem *folder_get_default_draft(void)
386 folder = FOLDER(folder_list->data);
387 g_return_val_if_fail(folder != NULL, NULL);
388 return folder->draft;
391 FolderItem *folder_get_default_queue(void)
395 folder = FOLDER(folder_list->data);
396 g_return_val_if_fail(folder != NULL, NULL);
397 return folder->queue;
400 FolderItem *folder_get_default_trash(void)
404 folder = FOLDER(folder_list->data);
405 g_return_val_if_fail(folder != NULL, NULL);
406 return folder->trash;
409 gchar *folder_item_get_path(FolderItem *item)
414 g_return_val_if_fail(item != NULL, NULL);
416 if (FOLDER_TYPE(item->folder) == F_MH)
417 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
418 else if (FOLDER_TYPE(item->folder) == F_IMAP) {
419 g_return_val_if_fail(item->folder->account != NULL, NULL);
420 folder_path = g_strconcat(get_imap_cache_dir(),
422 item->folder->account->recv_server,
424 item->folder->account->userid,
426 } else if (FOLDER_TYPE(item->folder) == F_NEWS) {
427 g_return_val_if_fail(item->folder->account != NULL, NULL);
428 folder_path = g_strconcat(get_news_cache_dir(),
430 item->folder->account->nntp_server,
435 g_return_val_if_fail(folder_path != NULL, NULL);
437 if (folder_path[0] == G_DIR_SEPARATOR) {
439 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
442 path = g_strdup(folder_path);
445 path = g_strconcat(g_get_home_dir(), G_DIR_SEPARATOR_S,
446 folder_path, G_DIR_SEPARATOR_S,
449 path = g_strconcat(g_get_home_dir(), G_DIR_SEPARATOR_S,
457 void folder_item_scan(FolderItem *item)
461 g_return_if_fail(item != NULL);
463 folder = item->folder;
464 folder->scan(folder, item);
467 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
470 folder_item_scan(FOLDER_ITEM(key));
473 void folder_item_scan_foreach(GHashTable *table)
475 g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
478 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
482 g_return_val_if_fail(item != NULL, NULL);
484 folder = item->folder;
485 if (item->last_num < 0) folder->scan(folder, item);
487 return folder->fetch_msg(folder, item, num);
490 gint folder_item_add_msg(FolderItem *dest, const gchar *file)
495 g_return_val_if_fail(dest != NULL, -1);
496 g_return_val_if_fail(file != NULL, -1);
497 g_return_val_if_fail(dest->folder->add_msg != NULL, -1);
499 folder = dest->folder;
500 if (dest->last_num < 0) folder->scan(folder, dest);
502 num = folder->add_msg(folder, dest, file);
503 if (num > 0) dest->last_num = num;
508 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
513 g_return_val_if_fail(dest != NULL, -1);
514 g_return_val_if_fail(msginfo != NULL, -1);
516 folder = dest->folder;
517 if (dest->last_num < 0) folder->scan(folder, dest);
519 num = folder->move_msg(folder, dest, msginfo);
520 if (num > 0) dest->last_num = num;
525 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
530 g_return_val_if_fail(dest != NULL, -1);
531 g_return_val_if_fail(msglist != NULL, -1);
533 folder = dest->folder;
534 if (dest->last_num < 0) folder->scan(folder, dest);
536 num = folder->move_msgs_with_dest(folder, dest, msglist);
537 if (num > 0) dest->last_num = num;
542 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
547 g_return_val_if_fail(dest != NULL, -1);
548 g_return_val_if_fail(msginfo != NULL, -1);
550 folder = dest->folder;
551 if (dest->last_num < 0) folder->scan(folder, dest);
553 num = folder->copy_msg(folder, dest, msginfo);
554 if (num > 0) dest->last_num = num;
559 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
564 g_return_val_if_fail(dest != NULL, -1);
565 g_return_val_if_fail(msglist != NULL, -1);
567 folder = dest->folder;
568 if (dest->last_num < 0) folder->scan(folder, dest);
570 num = folder->copy_msgs_with_dest(folder, dest, msglist);
571 if (num > 0) dest->last_num = num;
576 gint folder_item_remove_msg(FolderItem *item, gint num)
580 g_return_val_if_fail(item != NULL, -1);
582 folder = item->folder;
583 if (item->last_num < 0) folder->scan(folder, item);
585 return folder->remove_msg(folder, item, num);
588 gint folder_item_remove_all_msg(FolderItem *item)
592 g_return_val_if_fail(item != NULL, -1);
594 folder = item->folder;
595 if (item->last_num < 0) folder->scan(folder, item);
597 return folder->remove_all_msg(folder, item);
600 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
604 g_return_val_if_fail(item != NULL, FALSE);
606 folder = item->folder;
607 return folder->is_msg_changed(folder, item, msginfo);
610 gchar *folder_item_get_cache_file(FolderItem *item)
615 g_return_val_if_fail(item != NULL, NULL);
616 g_return_val_if_fail(item->path != NULL, NULL);
618 path = folder_item_get_path(item);
619 g_return_val_if_fail(path != NULL, NULL);
620 if (!is_dir_exist(path))
622 file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
628 gchar *folder_item_get_mark_file(FolderItem *item)
633 g_return_val_if_fail(item != NULL, NULL);
634 g_return_val_if_fail(item->path != NULL, NULL);
636 path = folder_item_get_path(item);
637 g_return_val_if_fail(path != NULL, NULL);
638 if (!is_dir_exist(path))
640 file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
647 static void folder_init(Folder *folder, FolderType type, const gchar *name)
651 g_return_if_fail(folder != NULL);
654 folder_set_name(folder, name);
655 folder->account = NULL;
656 folder->inbox = NULL;
657 folder->outbox = NULL;
658 folder->draft = NULL;
659 folder->queue = NULL;
660 folder->trash = NULL;
661 folder->ui_func = NULL;
662 folder->ui_func_data = NULL;
663 item = folder_item_new(name, NULL);
664 item->folder = folder;
665 folder->node = g_node_new(item);
670 folder->get_msg_list = mh_get_msg_list;
671 folder->fetch_msg = mh_fetch_msg;
672 folder->add_msg = mh_add_msg;
673 folder->move_msg = mh_move_msg;
674 folder->move_msgs_with_dest = mh_move_msgs_with_dest;
675 folder->copy_msg = mh_copy_msg;
676 folder->copy_msgs_with_dest = mh_copy_msgs_with_dest;
677 folder->remove_msg = mh_remove_msg;
678 folder->remove_all_msg = mh_remove_all_msg;
679 folder->is_msg_changed = mh_is_msg_changed;
680 folder->scan = mh_scan_folder;
681 folder->scan_tree = mh_scan_tree;
682 folder->create_tree = mh_create_tree;
683 folder->create_folder = mh_create_folder;
684 folder->rename_folder = mh_rename_folder;
685 folder->remove_folder = mh_remove_folder;
688 folder->get_msg_list = imap_get_msg_list;
689 folder->fetch_msg = imap_fetch_msg;
690 folder->move_msg = imap_move_msg;
691 folder->move_msgs_with_dest = imap_move_msgs_with_dest;
692 folder->remove_msg = imap_remove_msg;
693 folder->remove_all_msg = imap_remove_all_msg;
694 folder->scan = imap_scan_folder;
695 folder->create_folder = imap_create_folder;
698 folder->get_msg_list = news_get_article_list;
699 folder->fetch_msg = news_fetch_msg;
700 folder->scan = news_scan_group;
709 LOCAL_FOLDER(folder)->rootpath = NULL;
713 REMOTE_FOLDER(folder)->session = NULL;
719 static void local_folder_destroy(LocalFolder *lfolder)
721 g_return_if_fail(lfolder != NULL);
723 g_free(lfolder->rootpath);
726 static void remote_folder_destroy(RemoteFolder *rfolder)
728 g_return_if_fail(rfolder != NULL);
730 if (rfolder->session)
731 session_destroy(rfolder->session);
734 static void mh_folder_destroy(MHFolder *folder)
736 local_folder_destroy(LOCAL_FOLDER(folder));
739 static void imap_folder_destroy(IMAPFolder *folder)
741 remote_folder_destroy(REMOTE_FOLDER(folder));
744 static void news_folder_destroy(NewsFolder *folder)
746 remote_folder_destroy(REMOTE_FOLDER(folder));
749 static gboolean folder_build_tree(GNode *node, gpointer data)
751 Folder *folder = FOLDER(data);
755 SpecialFolderItemType stype = F_NORMAL;
756 const gchar *name = NULL;
757 const gchar *path = NULL;
758 PrefsAccount *account = NULL;
759 gint mtime = 0, new = 0, unread = 0, total = 0;
761 g_return_val_if_fail(node->data != NULL, FALSE);
762 if (!node->parent) return FALSE;
764 xmlnode = node->data;
765 if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
766 g_warning("tag name != \"folderitem\"\n");
770 list = xmlnode->tag->attr;
771 for (; list != NULL; list = list->next) {
772 XMLAttr *attr = list->data;
774 if (!attr || !attr->name || !attr->value) continue;
775 if (!strcmp(attr->name, "type")) {
776 if (!strcasecmp(attr->value, "normal"))
778 else if (!strcasecmp(attr->value, "inbox"))
780 else if (!strcasecmp(attr->value, "outbox"))
782 else if (!strcasecmp(attr->value, "draft"))
784 else if (!strcasecmp(attr->value, "queue"))
786 else if (!strcasecmp(attr->value, "trash"))
788 } else if (!strcmp(attr->name, "name"))
790 else if (!strcmp(attr->name, "path"))
792 else if (!strcmp(attr->name, "account_id")) {
793 account = account_find_from_id(atoi(attr->value));
794 if (!account) g_warning("account_id: %s not found\n",
797 else if (!strcmp(attr->name, "mtime"))
798 mtime = atoi(attr->value);
799 else if (!strcmp(attr->name, "new"))
800 new = atoi(attr->value);
801 else if (!strcmp(attr->name, "unread"))
802 unread = atoi(attr->value);
803 else if (!strcmp(attr->name, "total"))
804 total = atoi(attr->value);
807 item = folder_item_new(name, path);
809 item->account = account;
812 item->unread = unread;
814 item->parent = FOLDER_ITEM(node->parent->data);
815 item->folder = folder;
817 case F_INBOX: folder->inbox = item; break;
818 case F_OUTBOX: folder->outbox = item; break;
819 case F_DRAFT: folder->draft = item; break;
820 case F_QUEUE: folder->queue = item; break;
821 case F_TRASH: folder->trash = item; break;
825 xml_free_node(xmlnode);
830 static gboolean folder_read_folder_func(GNode *node, gpointer data)
835 FolderType type = F_UNKNOWN;
836 const gchar *name = NULL;
837 const gchar *path = NULL;
838 PrefsAccount *account = NULL;
840 if (g_node_depth(node) != 2) return FALSE;
841 g_return_val_if_fail(node->data != NULL, FALSE);
843 xmlnode = node->data;
844 if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
845 g_warning("tag name != \"folder\"\n");
849 list = xmlnode->tag->attr;
850 for (; list != NULL; list = list->next) {
851 XMLAttr *attr = list->data;
853 if (!attr || !attr->name || !attr->value) continue;
854 if (!strcmp(attr->name, "type")) {
855 if (!strcasecmp(attr->value, "mh"))
857 else if (!strcasecmp(attr->value, "mbox"))
859 else if (!strcasecmp(attr->value, "maildir"))
861 else if (!strcasecmp(attr->value, "imap"))
863 else if (!strcasecmp(attr->value, "news"))
865 } else if (!strcmp(attr->name, "name"))
867 else if (!strcmp(attr->name, "path"))
869 else if (!strcmp(attr->name, "account_id")) {
870 account = account_find_from_id(atoi(attr->value));
871 if (!account) g_warning("account_id: %s not found\n",
876 folder = folder_new(type, name, path);
877 g_return_val_if_fail(folder != NULL, FALSE);
878 folder->account = account;
879 if (account && (type == F_IMAP || type == F_NEWS))
880 account->folder = REMOTE_FOLDER(folder);
881 node->data = folder->node->data;
882 g_node_destroy(folder->node);
886 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
887 folder_build_tree, folder);
892 static gchar *folder_get_list_path(void)
894 static gchar *filename = NULL;
897 filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
903 static void folder_write_list_recursive(GNode *node, gpointer data)
905 FILE *fp = (FILE *)data;
906 FolderItem *item = FOLDER_ITEM(node->data);
908 static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
910 static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
911 "draft", "queue", "trash"};
913 g_return_if_fail(item != NULL);
915 depth = g_node_depth(node);
916 for (i = 0; i < depth; i++)
919 Folder *folder = item->folder;
921 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
923 fputs(" name=\"", fp);
924 xml_file_put_escape_str(fp, folder->name);
927 if (folder->type == F_MH) {
928 fputs(" path=\"", fp);
929 xml_file_put_escape_str
930 (fp, LOCAL_FOLDER(folder)->rootpath);
934 fprintf(fp, " account_id=\"%d\"",
935 folder->account->account_id);
937 fprintf(fp, "<folderitem type=\"%s\"",
938 folder_item_stype_str[item->stype]);
940 fputs(" name=\"", fp);
941 xml_file_put_escape_str(fp, item->name);
945 fputs(" path=\"", fp);
946 xml_file_put_escape_str(fp, item->path);
950 fprintf(fp, " account_id = \"%d\"",
951 item->account->account_id);
953 " mtime=\"%ld\" new=\"%d\" unread=\"%d\" total=\"%d\"",
954 item->mtime, item->new, item->unread, item->total);
957 if (node->children) {
961 child = node->children;
967 folder_write_list_recursive(cur, data);
970 for (i = 0; i < depth; i++)
972 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");