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(get_home_dir(), G_DIR_SEPARATOR_S,
446 folder_path, G_DIR_SEPARATOR_S,
449 path = g_strconcat(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;
696 folder->remove_folder = imap_remove_folder;
699 folder->get_msg_list = news_get_article_list;
700 folder->fetch_msg = news_fetch_msg;
701 folder->scan = news_scan_group;
710 LOCAL_FOLDER(folder)->rootpath = NULL;
714 REMOTE_FOLDER(folder)->session = NULL;
720 static void local_folder_destroy(LocalFolder *lfolder)
722 g_return_if_fail(lfolder != NULL);
724 g_free(lfolder->rootpath);
727 static void remote_folder_destroy(RemoteFolder *rfolder)
729 g_return_if_fail(rfolder != NULL);
731 if (rfolder->session)
732 session_destroy(rfolder->session);
735 static void mh_folder_destroy(MHFolder *folder)
737 local_folder_destroy(LOCAL_FOLDER(folder));
740 static void imap_folder_destroy(IMAPFolder *folder)
742 remote_folder_destroy(REMOTE_FOLDER(folder));
745 static void news_folder_destroy(NewsFolder *folder)
747 remote_folder_destroy(REMOTE_FOLDER(folder));
750 static gboolean folder_build_tree(GNode *node, gpointer data)
752 Folder *folder = FOLDER(data);
756 SpecialFolderItemType stype = F_NORMAL;
757 const gchar *name = NULL;
758 const gchar *path = NULL;
759 PrefsAccount *account = NULL;
760 gint mtime = 0, new = 0, unread = 0, total = 0;
762 g_return_val_if_fail(node->data != NULL, FALSE);
763 if (!node->parent) return FALSE;
765 xmlnode = node->data;
766 if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
767 g_warning("tag name != \"folderitem\"\n");
771 list = xmlnode->tag->attr;
772 for (; list != NULL; list = list->next) {
773 XMLAttr *attr = list->data;
775 if (!attr || !attr->name || !attr->value) continue;
776 if (!strcmp(attr->name, "type")) {
777 if (!strcasecmp(attr->value, "normal"))
779 else if (!strcasecmp(attr->value, "inbox"))
781 else if (!strcasecmp(attr->value, "outbox"))
783 else if (!strcasecmp(attr->value, "draft"))
785 else if (!strcasecmp(attr->value, "queue"))
787 else if (!strcasecmp(attr->value, "trash"))
789 } else if (!strcmp(attr->name, "name"))
791 else if (!strcmp(attr->name, "path"))
793 else if (!strcmp(attr->name, "account_id")) {
794 account = account_find_from_id(atoi(attr->value));
795 if (!account) g_warning("account_id: %s not found\n",
798 else if (!strcmp(attr->name, "mtime"))
799 mtime = atoi(attr->value);
800 else if (!strcmp(attr->name, "new"))
801 new = atoi(attr->value);
802 else if (!strcmp(attr->name, "unread"))
803 unread = atoi(attr->value);
804 else if (!strcmp(attr->name, "total"))
805 total = atoi(attr->value);
808 item = folder_item_new(name, path);
810 item->account = account;
813 item->unread = unread;
815 item->parent = FOLDER_ITEM(node->parent->data);
816 item->folder = folder;
818 case F_INBOX: folder->inbox = item; break;
819 case F_OUTBOX: folder->outbox = item; break;
820 case F_DRAFT: folder->draft = item; break;
821 case F_QUEUE: folder->queue = item; break;
822 case F_TRASH: folder->trash = item; break;
826 xml_free_node(xmlnode);
831 static gboolean folder_read_folder_func(GNode *node, gpointer data)
836 FolderType type = F_UNKNOWN;
837 const gchar *name = NULL;
838 const gchar *path = NULL;
839 PrefsAccount *account = NULL;
841 if (g_node_depth(node) != 2) return FALSE;
842 g_return_val_if_fail(node->data != NULL, FALSE);
844 xmlnode = node->data;
845 if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
846 g_warning("tag name != \"folder\"\n");
850 list = xmlnode->tag->attr;
851 for (; list != NULL; list = list->next) {
852 XMLAttr *attr = list->data;
854 if (!attr || !attr->name || !attr->value) continue;
855 if (!strcmp(attr->name, "type")) {
856 if (!strcasecmp(attr->value, "mh"))
858 else if (!strcasecmp(attr->value, "mbox"))
860 else if (!strcasecmp(attr->value, "maildir"))
862 else if (!strcasecmp(attr->value, "imap"))
864 else if (!strcasecmp(attr->value, "news"))
866 } else if (!strcmp(attr->name, "name"))
868 else if (!strcmp(attr->name, "path"))
870 else if (!strcmp(attr->name, "account_id")) {
871 account = account_find_from_id(atoi(attr->value));
872 if (!account) g_warning("account_id: %s not found\n",
877 folder = folder_new(type, name, path);
878 g_return_val_if_fail(folder != NULL, FALSE);
879 folder->account = account;
880 if (account && (type == F_IMAP || type == F_NEWS))
881 account->folder = REMOTE_FOLDER(folder);
882 node->data = folder->node->data;
883 g_node_destroy(folder->node);
887 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
888 folder_build_tree, folder);
893 static gchar *folder_get_list_path(void)
895 static gchar *filename = NULL;
898 filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
904 static void folder_write_list_recursive(GNode *node, gpointer data)
906 FILE *fp = (FILE *)data;
907 FolderItem *item = FOLDER_ITEM(node->data);
909 static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
911 static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
912 "draft", "queue", "trash"};
914 g_return_if_fail(item != NULL);
916 depth = g_node_depth(node);
917 for (i = 0; i < depth; i++)
920 Folder *folder = item->folder;
922 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
924 fputs(" name=\"", fp);
925 xml_file_put_escape_str(fp, folder->name);
928 if (folder->type == F_MH) {
929 fputs(" path=\"", fp);
930 xml_file_put_escape_str
931 (fp, LOCAL_FOLDER(folder)->rootpath);
935 fprintf(fp, " account_id=\"%d\"",
936 folder->account->account_id);
938 fprintf(fp, "<folderitem type=\"%s\"",
939 folder_item_stype_str[item->stype]);
941 fputs(" name=\"", fp);
942 xml_file_put_escape_str(fp, item->name);
946 fputs(" path=\"", fp);
947 xml_file_put_escape_str(fp, item->path);
951 fprintf(fp, " account_id = \"%d\"",
952 item->account->account_id);
954 " mtime=\"%ld\" new=\"%d\" unread=\"%d\" total=\"%d\"",
955 item->mtime, item->new, item->unread, item->total);
958 if (node->children) {
962 child = node->children;
968 folder_write_list_recursive(cur, data);
971 for (i = 0; i < depth; i++)
973 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");