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;
146 item->prefs = prefs_folder_item_new();
151 void folder_item_append(FolderItem *parent, FolderItem *item)
155 g_return_if_fail(parent != NULL);
156 g_return_if_fail(parent->folder != NULL);
157 g_return_if_fail(item != NULL);
159 node = parent->folder->node;
160 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, parent);
161 g_return_if_fail(node != NULL);
163 item->parent = parent;
164 item->folder = parent->folder;
165 g_node_append_data(node, item);
168 void folder_item_remove(FolderItem *item)
172 g_return_if_fail(item != NULL);
173 g_return_if_fail(item->folder != NULL);
175 node = item->folder->node;
176 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
177 g_return_if_fail(node != NULL);
179 /* TODO: free all FolderItem's first */
180 if (item->folder->node == node)
181 item->folder->node = NULL;
182 g_node_destroy(node);
185 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
187 g_return_if_fail(folder != NULL);
189 folder->ui_func = func;
190 folder->ui_func_data = data;
193 void folder_set_name(Folder *folder, const gchar *name)
195 g_return_if_fail(folder != NULL);
197 g_free(folder->name);
198 folder->name = name ? g_strdup(name) : NULL;
199 if (folder->node && folder->node->data) {
200 FolderItem *item = (FolderItem *)folder->node->data;
203 item->name = name ? g_strdup(name) : NULL;
207 void folder_destroy(Folder *folder)
209 g_return_if_fail(folder != NULL);
211 folder_list = g_list_remove(folder_list, folder);
213 switch (folder->type) {
215 mh_folder_destroy(MH_FOLDER(folder));
218 imap_folder_destroy(IMAP_FOLDER(folder));
221 news_folder_destroy(NEWS_FOLDER(folder));
226 folder_tree_destroy(folder);
227 g_free(folder->name);
231 void folder_tree_destroy(Folder *folder)
233 /* TODO: destroy all FolderItem before */
234 g_node_destroy(folder->node);
237 void folder_add(Folder *folder)
243 g_return_if_fail(folder != NULL);
245 for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
246 cur_folder = FOLDER(cur->data);
247 if (folder->type == F_MH) {
248 if (cur_folder->type != F_MH) break;
249 } else if (folder->type == F_IMAP) {
250 if (cur_folder->type != F_MH &&
251 cur_folder->type != F_IMAP) break;
252 } else if (folder->type == F_NEWS) {
253 if (cur_folder->type != F_MH &&
254 cur_folder->type != F_IMAP &&
255 cur_folder->type != F_NEWS) break;
259 folder_list = g_list_insert(folder_list, folder, i);
262 GList *folder_get_list(void)
267 gint folder_read_list(void)
272 node = xml_parse_file(folder_get_list_path());
273 if (!node) return -1;
275 xmlnode = node->data;
276 if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
277 g_warning("wrong folder list\n");
282 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
283 folder_read_folder_func, NULL);
292 void folder_write_list(void)
299 path = folder_get_list_path();
300 if ((pfile = prefs_write_open(path)) == NULL) return;
302 fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
303 conv_get_current_charset_str());
304 fputs("\n<folderlist>\n", pfile->fp);
306 for (list = folder_list; list != NULL; list = list->next) {
308 folder_write_list_recursive(folder->node, pfile->fp);
311 fputs("</folderlist>\n", pfile->fp);
313 if (prefs_write_close(pfile) < 0)
314 g_warning("failed to write folder list.\n");
317 Folder *folder_find_from_path(const gchar *path)
322 for (list = folder_list; list != NULL; list = list->next) {
324 if (folder->type == F_MH &&
325 !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
332 static gboolean folder_item_find_func(GNode *node, gpointer data)
334 FolderItem *item = node->data;
336 const gchar *path = d[0];
338 if (path_cmp(path, item->path) != 0)
346 FolderItem *folder_find_item_from_path(const gchar *path)
351 folder = folder_get_default_folder();
352 g_return_val_if_fail(folder != NULL, NULL);
354 d[0] = (gpointer)path;
356 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
357 folder_item_find_func, d);
361 Folder *folder_get_default_folder(void)
363 return FOLDER(folder_list->data);
366 FolderItem *folder_get_default_inbox(void)
370 folder = FOLDER(folder_list->data);
371 g_return_val_if_fail(folder != NULL, NULL);
372 return folder->inbox;
375 FolderItem *folder_get_default_outbox(void)
379 folder = FOLDER(folder_list->data);
380 g_return_val_if_fail(folder != NULL, NULL);
381 return folder->outbox;
384 FolderItem *folder_get_default_draft(void)
388 folder = FOLDER(folder_list->data);
389 g_return_val_if_fail(folder != NULL, NULL);
390 return folder->draft;
393 FolderItem *folder_get_default_queue(void)
397 folder = FOLDER(folder_list->data);
398 g_return_val_if_fail(folder != NULL, NULL);
399 return folder->queue;
402 FolderItem *folder_get_default_trash(void)
406 folder = FOLDER(folder_list->data);
407 g_return_val_if_fail(folder != NULL, NULL);
408 return folder->trash;
411 gchar *folder_item_get_path(FolderItem *item)
416 g_return_val_if_fail(item != NULL, NULL);
418 if (FOLDER_TYPE(item->folder) == F_MH)
419 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
420 else if (FOLDER_TYPE(item->folder) == F_IMAP) {
421 g_return_val_if_fail(item->folder->account != NULL, NULL);
422 folder_path = g_strconcat(get_imap_cache_dir(),
424 item->folder->account->recv_server,
426 item->folder->account->userid,
428 } else if (FOLDER_TYPE(item->folder) == F_NEWS) {
429 g_return_val_if_fail(item->folder->account != NULL, NULL);
430 folder_path = g_strconcat(get_news_cache_dir(),
432 item->folder->account->nntp_server,
437 g_return_val_if_fail(folder_path != NULL, NULL);
439 if (folder_path[0] == G_DIR_SEPARATOR) {
441 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
444 path = g_strdup(folder_path);
447 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
448 folder_path, G_DIR_SEPARATOR_S,
451 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
459 void folder_item_scan(FolderItem *item)
463 g_return_if_fail(item != NULL);
465 folder = item->folder;
466 folder->scan(folder, item);
469 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
472 folder_item_scan(FOLDER_ITEM(key));
475 void folder_item_scan_foreach(GHashTable *table)
477 g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
480 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
484 g_return_val_if_fail(item != NULL, NULL);
486 folder = item->folder;
487 if (item->last_num < 0) folder->scan(folder, item);
489 return folder->fetch_msg(folder, item, num);
492 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
493 gboolean remove_source)
498 g_return_val_if_fail(dest != NULL, -1);
499 g_return_val_if_fail(file != NULL, -1);
500 g_return_val_if_fail(dest->folder->add_msg != NULL, -1);
502 folder = dest->folder;
503 if (dest->last_num < 0) folder->scan(folder, dest);
505 num = folder->add_msg(folder, dest, file, remove_source);
506 if (num > 0) dest->last_num = num;
511 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
516 g_return_val_if_fail(dest != NULL, -1);
517 g_return_val_if_fail(msginfo != NULL, -1);
519 folder = dest->folder;
520 if (dest->last_num < 0) folder->scan(folder, dest);
522 num = folder->move_msg(folder, dest, msginfo);
523 if (num > 0) dest->last_num = num;
528 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
533 g_return_val_if_fail(dest != NULL, -1);
534 g_return_val_if_fail(msglist != NULL, -1);
536 folder = dest->folder;
537 if (dest->last_num < 0) folder->scan(folder, dest);
539 num = folder->move_msgs_with_dest(folder, dest, msglist);
540 if (num > 0) dest->last_num = num;
545 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
550 g_return_val_if_fail(dest != NULL, -1);
551 g_return_val_if_fail(msginfo != NULL, -1);
553 folder = dest->folder;
554 if (dest->last_num < 0) folder->scan(folder, dest);
556 num = folder->copy_msg(folder, dest, msginfo);
557 if (num > 0) dest->last_num = num;
562 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
567 g_return_val_if_fail(dest != NULL, -1);
568 g_return_val_if_fail(msglist != NULL, -1);
570 folder = dest->folder;
571 if (dest->last_num < 0) folder->scan(folder, dest);
573 num = folder->copy_msgs_with_dest(folder, dest, msglist);
574 if (num > 0) dest->last_num = num;
579 gint folder_item_remove_msg(FolderItem *item, gint num)
583 g_return_val_if_fail(item != NULL, -1);
585 folder = item->folder;
586 if (item->last_num < 0) folder->scan(folder, item);
588 return folder->remove_msg(folder, item, num);
591 gint folder_item_remove_all_msg(FolderItem *item)
595 g_return_val_if_fail(item != NULL, -1);
597 folder = item->folder;
598 if (item->last_num < 0) folder->scan(folder, item);
600 return folder->remove_all_msg(folder, item);
603 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
607 g_return_val_if_fail(item != NULL, FALSE);
609 folder = item->folder;
610 return folder->is_msg_changed(folder, item, msginfo);
613 gchar *folder_item_get_cache_file(FolderItem *item)
618 g_return_val_if_fail(item != NULL, NULL);
619 g_return_val_if_fail(item->path != NULL, NULL);
621 path = folder_item_get_path(item);
622 g_return_val_if_fail(path != NULL, NULL);
623 if (!is_dir_exist(path))
625 file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
631 gchar *folder_item_get_mark_file(FolderItem *item)
636 g_return_val_if_fail(item != NULL, NULL);
637 g_return_val_if_fail(item->path != NULL, NULL);
639 path = folder_item_get_path(item);
640 g_return_val_if_fail(path != NULL, NULL);
641 if (!is_dir_exist(path))
643 file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
650 static void folder_init(Folder *folder, FolderType type, const gchar *name)
654 g_return_if_fail(folder != NULL);
657 folder_set_name(folder, name);
658 folder->account = NULL;
659 folder->inbox = NULL;
660 folder->outbox = NULL;
661 folder->draft = NULL;
662 folder->queue = NULL;
663 folder->trash = NULL;
664 folder->ui_func = NULL;
665 folder->ui_func_data = NULL;
666 item = folder_item_new(name, NULL);
667 item->folder = folder;
668 folder->node = g_node_new(item);
673 folder->get_msg_list = mh_get_msg_list;
674 folder->fetch_msg = mh_fetch_msg;
675 folder->add_msg = mh_add_msg;
676 folder->move_msg = mh_move_msg;
677 folder->move_msgs_with_dest = mh_move_msgs_with_dest;
678 folder->copy_msg = mh_copy_msg;
679 folder->copy_msgs_with_dest = mh_copy_msgs_with_dest;
680 folder->remove_msg = mh_remove_msg;
681 folder->remove_all_msg = mh_remove_all_msg;
682 folder->is_msg_changed = mh_is_msg_changed;
683 folder->scan = mh_scan_folder;
684 folder->scan_tree = mh_scan_tree;
685 folder->create_tree = mh_create_tree;
686 folder->create_folder = mh_create_folder;
687 folder->rename_folder = mh_rename_folder;
688 folder->remove_folder = mh_remove_folder;
691 folder->get_msg_list = imap_get_msg_list;
692 folder->fetch_msg = imap_fetch_msg;
693 folder->move_msg = imap_move_msg;
694 folder->move_msgs_with_dest = imap_move_msgs_with_dest;
695 folder->remove_msg = imap_remove_msg;
696 folder->remove_all_msg = imap_remove_all_msg;
697 folder->scan = imap_scan_folder;
698 folder->create_folder = imap_create_folder;
699 folder->remove_folder = imap_remove_folder;
702 folder->get_msg_list = news_get_article_list;
703 folder->fetch_msg = news_fetch_msg;
704 folder->scan = news_scan_group;
713 LOCAL_FOLDER(folder)->rootpath = NULL;
717 REMOTE_FOLDER(folder)->session = NULL;
723 static void local_folder_destroy(LocalFolder *lfolder)
725 g_return_if_fail(lfolder != NULL);
727 g_free(lfolder->rootpath);
730 static void remote_folder_destroy(RemoteFolder *rfolder)
732 g_return_if_fail(rfolder != NULL);
734 if (rfolder->session)
735 session_destroy(rfolder->session);
738 static void mh_folder_destroy(MHFolder *folder)
740 local_folder_destroy(LOCAL_FOLDER(folder));
743 static void imap_folder_destroy(IMAPFolder *folder)
745 remote_folder_destroy(REMOTE_FOLDER(folder));
748 static void news_folder_destroy(NewsFolder *folder)
750 remote_folder_destroy(REMOTE_FOLDER(folder));
753 static gboolean folder_build_tree(GNode *node, gpointer data)
755 Folder *folder = FOLDER(data);
759 SpecialFolderItemType stype = F_NORMAL;
760 const gchar *name = NULL;
761 const gchar *path = NULL;
762 PrefsAccount *account = NULL;
763 gint mtime = 0, new = 0, unread = 0, total = 0;
765 g_return_val_if_fail(node->data != NULL, FALSE);
766 if (!node->parent) return FALSE;
768 xmlnode = node->data;
769 if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
770 g_warning("tag name != \"folderitem\"\n");
774 list = xmlnode->tag->attr;
775 for (; list != NULL; list = list->next) {
776 XMLAttr *attr = list->data;
778 if (!attr || !attr->name || !attr->value) continue;
779 if (!strcmp(attr->name, "type")) {
780 if (!strcasecmp(attr->value, "normal"))
782 else if (!strcasecmp(attr->value, "inbox"))
784 else if (!strcasecmp(attr->value, "outbox"))
786 else if (!strcasecmp(attr->value, "draft"))
788 else if (!strcasecmp(attr->value, "queue"))
790 else if (!strcasecmp(attr->value, "trash"))
792 } else if (!strcmp(attr->name, "name"))
794 else if (!strcmp(attr->name, "path"))
796 else if (!strcmp(attr->name, "account_id")) {
797 account = account_find_from_id(atoi(attr->value));
798 if (!account) g_warning("account_id: %s not found\n",
801 else if (!strcmp(attr->name, "mtime"))
802 mtime = atoi(attr->value);
803 else if (!strcmp(attr->name, "new"))
804 new = atoi(attr->value);
805 else if (!strcmp(attr->name, "unread"))
806 unread = atoi(attr->value);
807 else if (!strcmp(attr->name, "total"))
808 total = atoi(attr->value);
811 item = folder_item_new(name, path);
813 item->account = account;
816 item->unread = unread;
818 item->parent = FOLDER_ITEM(node->parent->data);
819 item->folder = folder;
821 case F_INBOX: folder->inbox = item; break;
822 case F_OUTBOX: folder->outbox = item; break;
823 case F_DRAFT: folder->draft = item; break;
824 case F_QUEUE: folder->queue = item; break;
825 case F_TRASH: folder->trash = item; break;
829 prefs_folder_item_read_config(item);
832 xml_free_node(xmlnode);
837 static gboolean folder_read_folder_func(GNode *node, gpointer data)
842 FolderType type = F_UNKNOWN;
843 const gchar *name = NULL;
844 const gchar *path = NULL;
845 PrefsAccount *account = NULL;
847 if (g_node_depth(node) != 2) return FALSE;
848 g_return_val_if_fail(node->data != NULL, FALSE);
850 xmlnode = node->data;
851 if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
852 g_warning("tag name != \"folder\"\n");
856 list = xmlnode->tag->attr;
857 for (; list != NULL; list = list->next) {
858 XMLAttr *attr = list->data;
860 if (!attr || !attr->name || !attr->value) continue;
861 if (!strcmp(attr->name, "type")) {
862 if (!strcasecmp(attr->value, "mh"))
864 else if (!strcasecmp(attr->value, "mbox"))
866 else if (!strcasecmp(attr->value, "maildir"))
868 else if (!strcasecmp(attr->value, "imap"))
870 else if (!strcasecmp(attr->value, "news"))
872 } else if (!strcmp(attr->name, "name"))
874 else if (!strcmp(attr->name, "path"))
876 else if (!strcmp(attr->name, "account_id")) {
877 account = account_find_from_id(atoi(attr->value));
878 if (!account) g_warning("account_id: %s not found\n",
883 folder = folder_new(type, name, path);
884 g_return_val_if_fail(folder != NULL, FALSE);
885 folder->account = account;
886 if (account && (type == F_IMAP || type == F_NEWS))
887 account->folder = REMOTE_FOLDER(folder);
888 node->data = folder->node->data;
889 g_node_destroy(folder->node);
893 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
894 folder_build_tree, folder);
899 static gchar *folder_get_list_path(void)
901 static gchar *filename = NULL;
904 filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
910 static void folder_write_list_recursive(GNode *node, gpointer data)
912 FILE *fp = (FILE *)data;
913 FolderItem *item = FOLDER_ITEM(node->data);
915 static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
917 static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
918 "draft", "queue", "trash"};
920 g_return_if_fail(item != NULL);
922 depth = g_node_depth(node);
923 for (i = 0; i < depth; i++)
926 Folder *folder = item->folder;
928 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
930 fputs(" name=\"", fp);
931 xml_file_put_escape_str(fp, folder->name);
934 if (folder->type == F_MH) {
935 fputs(" path=\"", fp);
936 xml_file_put_escape_str
937 (fp, LOCAL_FOLDER(folder)->rootpath);
941 fprintf(fp, " account_id=\"%d\"",
942 folder->account->account_id);
944 fprintf(fp, "<folderitem type=\"%s\"",
945 folder_item_stype_str[item->stype]);
947 fputs(" name=\"", fp);
948 xml_file_put_escape_str(fp, item->name);
952 fputs(" path=\"", fp);
953 xml_file_put_escape_str(fp, item->path);
957 fprintf(fp, " account_id = \"%d\"",
958 item->account->account_id);
960 " mtime=\"%ld\" new=\"%d\" unread=\"%d\" total=\"%d\"",
961 item->mtime, item->new, item->unread, item->total);
964 if (node->children) {
968 child = node->children;
974 folder_write_list_recursive(cur, data);
977 for (i = 0; i < depth; i++)
979 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");