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)
497 g_return_val_if_fail(dest != NULL, -1);
498 g_return_val_if_fail(file != NULL, -1);
499 g_return_val_if_fail(dest->folder->add_msg != NULL, -1);
501 folder = dest->folder;
502 if (dest->last_num < 0) folder->scan(folder, dest);
504 num = folder->add_msg(folder, dest, file);
505 if (num > 0) dest->last_num = num;
510 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
515 g_return_val_if_fail(dest != NULL, -1);
516 g_return_val_if_fail(msginfo != NULL, -1);
518 folder = dest->folder;
519 if (dest->last_num < 0) folder->scan(folder, dest);
521 num = folder->move_msg(folder, dest, msginfo);
522 if (num > 0) dest->last_num = num;
527 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
532 g_return_val_if_fail(dest != NULL, -1);
533 g_return_val_if_fail(msglist != NULL, -1);
535 folder = dest->folder;
536 if (dest->last_num < 0) folder->scan(folder, dest);
538 num = folder->move_msgs_with_dest(folder, dest, msglist);
539 if (num > 0) dest->last_num = num;
544 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
549 g_return_val_if_fail(dest != NULL, -1);
550 g_return_val_if_fail(msginfo != NULL, -1);
552 folder = dest->folder;
553 if (dest->last_num < 0) folder->scan(folder, dest);
555 num = folder->copy_msg(folder, dest, msginfo);
556 if (num > 0) dest->last_num = num;
561 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
566 g_return_val_if_fail(dest != NULL, -1);
567 g_return_val_if_fail(msglist != NULL, -1);
569 folder = dest->folder;
570 if (dest->last_num < 0) folder->scan(folder, dest);
572 num = folder->copy_msgs_with_dest(folder, dest, msglist);
573 if (num > 0) dest->last_num = num;
578 gint folder_item_remove_msg(FolderItem *item, gint num)
582 g_return_val_if_fail(item != NULL, -1);
584 folder = item->folder;
585 if (item->last_num < 0) folder->scan(folder, item);
587 return folder->remove_msg(folder, item, num);
590 gint folder_item_remove_all_msg(FolderItem *item)
594 g_return_val_if_fail(item != NULL, -1);
596 folder = item->folder;
597 if (item->last_num < 0) folder->scan(folder, item);
599 return folder->remove_all_msg(folder, item);
602 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
606 g_return_val_if_fail(item != NULL, FALSE);
608 folder = item->folder;
609 return folder->is_msg_changed(folder, item, msginfo);
612 gchar *folder_item_get_cache_file(FolderItem *item)
617 g_return_val_if_fail(item != NULL, NULL);
618 g_return_val_if_fail(item->path != NULL, NULL);
620 path = folder_item_get_path(item);
621 g_return_val_if_fail(path != NULL, NULL);
622 if (!is_dir_exist(path))
624 file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
630 gchar *folder_item_get_mark_file(FolderItem *item)
635 g_return_val_if_fail(item != NULL, NULL);
636 g_return_val_if_fail(item->path != NULL, NULL);
638 path = folder_item_get_path(item);
639 g_return_val_if_fail(path != NULL, NULL);
640 if (!is_dir_exist(path))
642 file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
649 static void folder_init(Folder *folder, FolderType type, const gchar *name)
653 g_return_if_fail(folder != NULL);
656 folder_set_name(folder, name);
657 folder->account = NULL;
658 folder->inbox = NULL;
659 folder->outbox = NULL;
660 folder->draft = NULL;
661 folder->queue = NULL;
662 folder->trash = NULL;
663 folder->ui_func = NULL;
664 folder->ui_func_data = NULL;
665 item = folder_item_new(name, NULL);
666 item->folder = folder;
667 folder->node = g_node_new(item);
672 folder->get_msg_list = mh_get_msg_list;
673 folder->fetch_msg = mh_fetch_msg;
674 folder->add_msg = mh_add_msg;
675 folder->move_msg = mh_move_msg;
676 folder->move_msgs_with_dest = mh_move_msgs_with_dest;
677 folder->copy_msg = mh_copy_msg;
678 folder->copy_msgs_with_dest = mh_copy_msgs_with_dest;
679 folder->remove_msg = mh_remove_msg;
680 folder->remove_all_msg = mh_remove_all_msg;
681 folder->is_msg_changed = mh_is_msg_changed;
682 folder->scan = mh_scan_folder;
683 folder->scan_tree = mh_scan_tree;
684 folder->create_tree = mh_create_tree;
685 folder->create_folder = mh_create_folder;
686 folder->rename_folder = mh_rename_folder;
687 folder->remove_folder = mh_remove_folder;
690 folder->get_msg_list = imap_get_msg_list;
691 folder->fetch_msg = imap_fetch_msg;
692 folder->move_msg = imap_move_msg;
693 folder->move_msgs_with_dest = imap_move_msgs_with_dest;
694 folder->remove_msg = imap_remove_msg;
695 folder->remove_all_msg = imap_remove_all_msg;
696 folder->scan = imap_scan_folder;
697 folder->create_folder = imap_create_folder;
698 folder->remove_folder = imap_remove_folder;
701 folder->get_msg_list = news_get_article_list;
702 folder->fetch_msg = news_fetch_msg;
703 folder->scan = news_scan_group;
712 LOCAL_FOLDER(folder)->rootpath = NULL;
716 REMOTE_FOLDER(folder)->session = NULL;
722 static void local_folder_destroy(LocalFolder *lfolder)
724 g_return_if_fail(lfolder != NULL);
726 g_free(lfolder->rootpath);
729 static void remote_folder_destroy(RemoteFolder *rfolder)
731 g_return_if_fail(rfolder != NULL);
733 if (rfolder->session)
734 session_destroy(rfolder->session);
737 static void mh_folder_destroy(MHFolder *folder)
739 local_folder_destroy(LOCAL_FOLDER(folder));
742 static void imap_folder_destroy(IMAPFolder *folder)
744 remote_folder_destroy(REMOTE_FOLDER(folder));
747 static void news_folder_destroy(NewsFolder *folder)
749 remote_folder_destroy(REMOTE_FOLDER(folder));
752 static gboolean folder_build_tree(GNode *node, gpointer data)
754 Folder *folder = FOLDER(data);
758 SpecialFolderItemType stype = F_NORMAL;
759 const gchar *name = NULL;
760 const gchar *path = NULL;
761 PrefsAccount *account = NULL;
762 gint mtime = 0, new = 0, unread = 0, total = 0;
764 g_return_val_if_fail(node->data != NULL, FALSE);
765 if (!node->parent) return FALSE;
767 xmlnode = node->data;
768 if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
769 g_warning("tag name != \"folderitem\"\n");
773 list = xmlnode->tag->attr;
774 for (; list != NULL; list = list->next) {
775 XMLAttr *attr = list->data;
777 if (!attr || !attr->name || !attr->value) continue;
778 if (!strcmp(attr->name, "type")) {
779 if (!strcasecmp(attr->value, "normal"))
781 else if (!strcasecmp(attr->value, "inbox"))
783 else if (!strcasecmp(attr->value, "outbox"))
785 else if (!strcasecmp(attr->value, "draft"))
787 else if (!strcasecmp(attr->value, "queue"))
789 else if (!strcasecmp(attr->value, "trash"))
791 } else if (!strcmp(attr->name, "name"))
793 else if (!strcmp(attr->name, "path"))
795 else if (!strcmp(attr->name, "account_id")) {
796 account = account_find_from_id(atoi(attr->value));
797 if (!account) g_warning("account_id: %s not found\n",
800 else if (!strcmp(attr->name, "mtime"))
801 mtime = atoi(attr->value);
802 else if (!strcmp(attr->name, "new"))
803 new = atoi(attr->value);
804 else if (!strcmp(attr->name, "unread"))
805 unread = atoi(attr->value);
806 else if (!strcmp(attr->name, "total"))
807 total = atoi(attr->value);
810 item = folder_item_new(name, path);
812 item->account = account;
815 item->unread = unread;
817 item->parent = FOLDER_ITEM(node->parent->data);
818 item->folder = folder;
820 case F_INBOX: folder->inbox = item; break;
821 case F_OUTBOX: folder->outbox = item; break;
822 case F_DRAFT: folder->draft = item; break;
823 case F_QUEUE: folder->queue = item; break;
824 case F_TRASH: folder->trash = item; break;
828 prefs_folder_item_read_config(item);
831 xml_free_node(xmlnode);
836 static gboolean folder_read_folder_func(GNode *node, gpointer data)
841 FolderType type = F_UNKNOWN;
842 const gchar *name = NULL;
843 const gchar *path = NULL;
844 PrefsAccount *account = NULL;
846 if (g_node_depth(node) != 2) return FALSE;
847 g_return_val_if_fail(node->data != NULL, FALSE);
849 xmlnode = node->data;
850 if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
851 g_warning("tag name != \"folder\"\n");
855 list = xmlnode->tag->attr;
856 for (; list != NULL; list = list->next) {
857 XMLAttr *attr = list->data;
859 if (!attr || !attr->name || !attr->value) continue;
860 if (!strcmp(attr->name, "type")) {
861 if (!strcasecmp(attr->value, "mh"))
863 else if (!strcasecmp(attr->value, "mbox"))
865 else if (!strcasecmp(attr->value, "maildir"))
867 else if (!strcasecmp(attr->value, "imap"))
869 else if (!strcasecmp(attr->value, "news"))
871 } else if (!strcmp(attr->name, "name"))
873 else if (!strcmp(attr->name, "path"))
875 else if (!strcmp(attr->name, "account_id")) {
876 account = account_find_from_id(atoi(attr->value));
877 if (!account) g_warning("account_id: %s not found\n",
882 folder = folder_new(type, name, path);
883 g_return_val_if_fail(folder != NULL, FALSE);
884 folder->account = account;
885 if (account && (type == F_IMAP || type == F_NEWS))
886 account->folder = REMOTE_FOLDER(folder);
887 node->data = folder->node->data;
888 g_node_destroy(folder->node);
892 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
893 folder_build_tree, folder);
898 static gchar *folder_get_list_path(void)
900 static gchar *filename = NULL;
903 filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
909 static void folder_write_list_recursive(GNode *node, gpointer data)
911 FILE *fp = (FILE *)data;
912 FolderItem *item = FOLDER_ITEM(node->data);
914 static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
916 static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
917 "draft", "queue", "trash"};
919 g_return_if_fail(item != NULL);
921 depth = g_node_depth(node);
922 for (i = 0; i < depth; i++)
925 Folder *folder = item->folder;
927 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
929 fputs(" name=\"", fp);
930 xml_file_put_escape_str(fp, folder->name);
933 if (folder->type == F_MH) {
934 fputs(" path=\"", fp);
935 xml_file_put_escape_str
936 (fp, LOCAL_FOLDER(folder)->rootpath);
940 fprintf(fp, " account_id=\"%d\"",
941 folder->account->account_id);
943 fprintf(fp, "<folderitem type=\"%s\"",
944 folder_item_stype_str[item->stype]);
946 fputs(" name=\"", fp);
947 xml_file_put_escape_str(fp, item->name);
951 fputs(" path=\"", fp);
952 xml_file_put_escape_str(fp, item->path);
956 fprintf(fp, " account_id = \"%d\"",
957 item->account->account_id);
959 " mtime=\"%ld\" new=\"%d\" unread=\"%d\" total=\"%d\"",
960 item->mtime, item->new, item->unread, item->total);
963 if (node->children) {
967 child = node->children;
973 folder_write_list_recursive(cur, data);
976 for (i = 0; i < depth; i++)
978 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");