From b9ca7b1ef5cd1f96ae6e28ae78d12c1e3258c23f Mon Sep 17 00:00:00 2001 From: hiro Date: Wed, 12 Jan 2005 11:22:08 +0000 Subject: Initial import of Sylpheed (GTK2 version). git-svn-id: svn://sylpheed.sraoss.jp/sylpheed/trunk@1 ee746299-78ed-0310-b773-934348b2243d --- src/folder.c | 1529 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1529 insertions(+) create mode 100644 src/folder.c (limited to 'src/folder.c') diff --git a/src/folder.c b/src/folder.c new file mode 100644 index 00000000..08800e53 --- /dev/null +++ b/src/folder.c @@ -0,0 +1,1529 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2003 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include + +#include "intl.h" +#include "folder.h" +#include "session.h" +#include "imap.h" +#include "news.h" +#include "mh.h" +#include "utils.h" +#include "xml.h" +#include "codeconv.h" +#include "prefs.h" +#include "account.h" +#include "prefs_account.h" + +static GList *folder_list = NULL; + +static void folder_init (Folder *folder, + const gchar *name); + +static gboolean folder_read_folder_func (GNode *node, + gpointer data); +static gchar *folder_get_list_path (void); +static void folder_write_list_recursive (GNode *node, + gpointer data); + + +Folder *folder_new(FolderType type, const gchar *name, const gchar *path) +{ + Folder *folder = NULL; + + name = name ? name : path; + switch (type) { + case F_MH: + folder = mh_get_class()->folder_new(name, path); + break; + case F_IMAP: + folder = imap_get_class()->folder_new(name, path); + break; + case F_NEWS: + folder = news_get_class()->folder_new(name, path); + break; + default: + return NULL; + } + + return folder; +} + +static void folder_init(Folder *folder, const gchar *name) +{ + FolderItem *item; + + g_return_if_fail(folder != NULL); + + folder_set_name(folder, name); + folder->account = NULL; + folder->inbox = NULL; + folder->outbox = NULL; + folder->draft = NULL; + folder->queue = NULL; + folder->trash = NULL; + folder->ui_func = NULL; + folder->ui_func_data = NULL; + item = folder_item_new(name, NULL); + item->folder = folder; + folder->node = item->node = g_node_new(item); + folder->data = NULL; +} + +void folder_local_folder_init(Folder *folder, const gchar *name, + const gchar *path) +{ + folder_init(folder, name); + LOCAL_FOLDER(folder)->rootpath = g_strdup(path); +} + +void folder_remote_folder_init(Folder *folder, const gchar *name, + const gchar *path) +{ + folder_init(folder, name); + REMOTE_FOLDER(folder)->session = NULL; +} + +void folder_destroy(Folder *folder) +{ + g_return_if_fail(folder != NULL); + g_return_if_fail(folder->klass->destroy != NULL); + + folder->klass->destroy(folder); + + folder_list = g_list_remove(folder_list, folder); + + folder_tree_destroy(folder); + g_free(folder->name); + g_free(folder); +} + +void folder_local_folder_destroy(LocalFolder *lfolder) +{ + g_return_if_fail(lfolder != NULL); + + g_free(lfolder->rootpath); +} + +void folder_remote_folder_destroy(RemoteFolder *rfolder) +{ + g_return_if_fail(rfolder != NULL); + + if (rfolder->session) + session_destroy(rfolder->session); +} + +#if 0 +Folder *mbox_folder_new(const gchar *name, const gchar *path) +{ + /* not yet implemented */ + return NULL; +} + +Folder *maildir_folder_new(const gchar *name, const gchar *path) +{ + /* not yet implemented */ + return NULL; +} +#endif + +FolderItem *folder_item_new(const gchar *name, const gchar *path) +{ + FolderItem *item; + + item = g_new0(FolderItem, 1); + + item->stype = F_NORMAL; + item->name = g_strdup(name); + item->path = g_strdup(path); + item->mtime = 0; + item->new = 0; + item->unread = 0; + item->total = 0; + item->unmarked_num = 0; + item->last_num = -1; + item->no_sub = FALSE; + item->no_select = FALSE; + item->collapsed = FALSE; + item->threaded = TRUE; + item->opened = FALSE; + item->node = NULL; + item->parent = NULL; + item->folder = NULL; + item->account = NULL; + item->ac_apply_sub = FALSE; + item->auto_to = NULL; + item->use_auto_to_on_reply = FALSE; + item->auto_cc = NULL; + item->auto_bcc = NULL; + item->auto_replyto = NULL; + item->mark_queue = NULL; + item->data = NULL; + + return item; +} + +void folder_item_append(FolderItem *parent, FolderItem *item) +{ + g_return_if_fail(parent != NULL); + g_return_if_fail(parent->folder != NULL); + g_return_if_fail(parent->node != NULL); + g_return_if_fail(item != NULL); + + item->parent = parent; + item->folder = parent->folder; + item->node = g_node_append_data(parent->node, item); +} + +static gboolean folder_item_remove_func(GNode *node, gpointer data) +{ + FolderItem *item = FOLDER_ITEM(node->data); + + folder_item_destroy(item); + return FALSE; +} + +void folder_item_remove(FolderItem *item) +{ + GNode *node; + + g_return_if_fail(item != NULL); + g_return_if_fail(item->folder != NULL); + g_return_if_fail(item->node != NULL); + + node = item->node; + + if (item->folder->node == node) + item->folder->node = NULL; + + g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1, + folder_item_remove_func, NULL); + g_node_destroy(node); +} + +void folder_item_remove_children(FolderItem *item) +{ + GNode *node, *next; + + g_return_if_fail(item != NULL); + g_return_if_fail(item->folder != NULL); + g_return_if_fail(item->node != NULL); + + node = item->node->children; + while (node != NULL) { + next = node->next; + folder_item_remove(FOLDER_ITEM(node->data)); + node = next; + } +} + +void folder_item_destroy(FolderItem *item) +{ + Folder *folder; + + g_return_if_fail(item != NULL); + + folder = item->folder; + if (folder) { + if (folder->inbox == item) + folder->inbox = NULL; + else if (folder->outbox == item) + folder->outbox = NULL; + else if (folder->draft == item) + folder->draft = NULL; + else if (folder->queue == item) + folder->queue = NULL; + else if (folder->trash == item) + folder->trash = NULL; + } + + g_free(item->name); + g_free(item->path); + g_free(item->auto_to); + g_free(item->auto_cc); + g_free(item->auto_bcc); + g_free(item->auto_replyto); + g_free(item); +} + +void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data) +{ + g_return_if_fail(folder != NULL); + + folder->ui_func = func; + folder->ui_func_data = data; +} + +void folder_set_name(Folder *folder, const gchar *name) +{ + g_return_if_fail(folder != NULL); + + g_free(folder->name); + folder->name = name ? g_strdup(name) : NULL; + if (folder->node && folder->node->data) { + FolderItem *item = (FolderItem *)folder->node->data; + + g_free(item->name); + item->name = name ? g_strdup(name) : NULL; + } +} + +void folder_tree_destroy(Folder *folder) +{ + g_return_if_fail(folder != NULL); + + if (folder->node) + folder_item_remove(FOLDER_ITEM(folder->node->data)); +} + +void folder_add(Folder *folder) +{ + Folder *cur_folder; + GList *cur; + gint i; + + g_return_if_fail(folder != NULL); + + for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) { + cur_folder = FOLDER(cur->data); + if (FOLDER_TYPE(folder) == F_MH) { + if (FOLDER_TYPE(cur_folder) != F_MH) break; + } else if (FOLDER_TYPE(folder) == F_IMAP) { + if (FOLDER_TYPE(cur_folder) != F_MH && + FOLDER_TYPE(cur_folder) != F_IMAP) break; + } else if (FOLDER_TYPE(folder) == F_NEWS) { + if (FOLDER_TYPE(cur_folder) != F_MH && + FOLDER_TYPE(cur_folder) != F_IMAP && + FOLDER_TYPE(cur_folder) != F_NEWS) break; + } + } + + folder_list = g_list_insert(folder_list, folder, i); +} + +GList *folder_get_list(void) +{ + return folder_list; +} + +gint folder_read_list(void) +{ + GNode *node; + XMLNode *xmlnode; + gchar *path; + + path = folder_get_list_path(); + if (!is_file_exist(path)) return -1; + node = xml_parse_file(path); + if (!node) return -1; + + xmlnode = node->data; + if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) { + g_warning("wrong folder list\n"); + xml_free_tree(node); + return -1; + } + + g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2, + folder_read_folder_func, NULL); + + xml_free_tree(node); + if (folder_list) + return 0; + else + return -1; +} + +void folder_write_list(void) +{ + GList *list; + Folder *folder; + gchar *path; + PrefFile *pfile; + + path = folder_get_list_path(); + if ((pfile = prefs_file_open(path)) == NULL) return; + + fprintf(pfile->fp, "\n", + conv_get_internal_charset_str()); + fputs("\n\n", pfile->fp); + + for (list = folder_list; list != NULL; list = list->next) { + folder = list->data; + folder_write_list_recursive(folder->node, pfile->fp); + } + + fputs("\n", pfile->fp); + + if (prefs_file_close(pfile) < 0) + g_warning("failed to write folder list.\n"); +} + +struct TotalMsgStatus +{ + guint new; + guint unread; + guint total; + GString *str; +}; + +static gboolean folder_get_status_full_all_func(GNode *node, gpointer data) +{ + FolderItem *item; + struct TotalMsgStatus *status = (struct TotalMsgStatus *)data; + gchar *id; + + g_return_val_if_fail(node->data != NULL, FALSE); + + item = FOLDER_ITEM(node->data); + + if (!item->path) return FALSE; + + status->new += item->new; + status->unread += item->unread; + status->total += item->total; + + if (status->str) { + id = folder_item_get_identifier(item); + g_string_sprintfa(status->str, "%5d %5d %5d %s\n", + item->new, item->unread, + item->total, id); + g_free(id); + } + + return FALSE; +} + +static void folder_get_status_full_all(GString *str, guint *new, guint *unread, + guint *total) +{ + GList *list; + Folder *folder; + struct TotalMsgStatus status; + + status.new = status.unread = status.total = 0; + status.str = str; + + debug_print("Counting total number of messages...\n"); + + for (list = folder_list; list != NULL; list = list->next) { + folder = FOLDER(list->data); + if (folder->node) + g_node_traverse(folder->node, G_PRE_ORDER, + G_TRAVERSE_ALL, -1, + folder_get_status_full_all_func, + &status); + } + + *new = status.new; + *unread = status.unread; + *total = status.total; +} + +gchar *folder_get_status(GPtrArray *folders, gboolean full) +{ + guint new, unread, total; + GString *str; + gint i; + gchar *ret; + + new = unread = total = 0; + + str = g_string_new(NULL); + + if (folders) { + for (i = 0; i < folders->len; i++) { + FolderItem *item; + + item = g_ptr_array_index(folders, i); + new += item->new; + unread += item->unread; + total += item->total; + + if (full) { + gchar *id; + + id = folder_item_get_identifier(item); + g_string_sprintfa(str, "%5d %5d %5d %s\n", + item->new, item->unread, + item->total, id); + g_free(id); + } + } + } else { + folder_get_status_full_all(full ? str : NULL, + &new, &unread, &total); + } + + if (full) + g_string_sprintfa(str, "%5d %5d %5d\n", new, unread, total); + else + g_string_sprintfa(str, "%d %d %d\n", new, unread, total); + + ret = str->str; + g_string_free(str, FALSE); + + return ret; +} + +Folder *folder_find_from_path(const gchar *path) +{ + GList *list; + Folder *folder; + + for (list = folder_list; list != NULL; list = list->next) { + folder = list->data; + if (FOLDER_TYPE(folder) == F_MH && + !path_cmp(LOCAL_FOLDER(folder)->rootpath, path)) + return folder; + } + + return NULL; +} + +Folder *folder_find_from_name(const gchar *name, FolderType type) +{ + GList *list; + Folder *folder; + + for (list = folder_list; list != NULL; list = list->next) { + folder = list->data; + if (FOLDER_TYPE(folder) == type && + strcmp2(name, folder->name) == 0) + return folder; + } + + return NULL; +} + +static gboolean folder_item_find_func(GNode *node, gpointer data) +{ + FolderItem *item = node->data; + gpointer *d = data; + const gchar *path = d[0]; + + if (path_cmp(path, item->path) != 0) + return FALSE; + + d[1] = item; + + return TRUE; +} + +FolderItem *folder_find_item_from_path(const gchar *path) +{ + Folder *folder; + gpointer d[2]; + + folder = folder_get_default_folder(); + g_return_val_if_fail(folder != NULL, NULL); + + d[0] = (gpointer)path; + d[1] = NULL; + g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + folder_item_find_func, d); + return d[1]; +} + +FolderItem *folder_find_child_item_by_name(FolderItem *item, const gchar *name) +{ + GNode *node; + FolderItem *child; + + for (node = item->node->children; node != NULL; node = node->next) { + child = FOLDER_ITEM(node->data); + if (strcmp2(g_basename(child->path), name) == 0) + return child; + } + + return NULL; +} + +static const struct { + gchar *str; + FolderType type; +} type_str_table[] = { + {"#mh" , F_MH}, + {"#mbox" , F_MBOX}, + {"#maildir", F_MAILDIR}, + {"#imap" , F_IMAP}, + {"#news" , F_NEWS} +}; + +static gchar *folder_get_type_string(FolderType type) +{ + gint i; + + for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]); + i++) { + if (type_str_table[i].type == type) + return type_str_table[i].str; + } + + return NULL; +} + +static FolderType folder_get_type_from_string(const gchar *str) +{ + gint i; + + for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]); + i++) { + if (g_strcasecmp(type_str_table[i].str, str) == 0) + return type_str_table[i].type; + } + + return F_UNKNOWN; +} + +gchar *folder_get_identifier(Folder *folder) +{ + gchar *type_str; + + g_return_val_if_fail(folder != NULL, NULL); + + type_str = folder_get_type_string(FOLDER_TYPE(folder)); + return g_strconcat(type_str, "/", folder->name, NULL); +} + +gchar *folder_item_get_identifier(FolderItem *item) +{ + gchar *id; + gchar *folder_id; + + g_return_val_if_fail(item != NULL, NULL); + g_return_val_if_fail(item->path != NULL, NULL); + + folder_id = folder_get_identifier(item->folder); + id = g_strconcat(folder_id, "/", item->path, NULL); + g_free(folder_id); + + return id; +} + +FolderItem *folder_find_item_from_identifier(const gchar *identifier) +{ + Folder *folder; + gpointer d[2]; + gchar *str; + gchar *p; + gchar *name; + gchar *path; + FolderType type; + + g_return_val_if_fail(identifier != NULL, NULL); + + if (*identifier != '#') + return folder_find_item_from_path(identifier); + + Xstrdup_a(str, identifier, return NULL); + + p = strchr(str, '/'); + if (!p) + return folder_find_item_from_path(identifier); + *p = '\0'; + p++; + type = folder_get_type_from_string(str); + if (type == F_UNKNOWN) + return folder_find_item_from_path(identifier); + + name = p; + p = strchr(p, '/'); + if (!p) + return folder_find_item_from_path(identifier); + *p = '\0'; + p++; + + folder = folder_find_from_name(name, type); + if (!folder) + return folder_find_item_from_path(identifier); + + path = p; + + d[0] = (gpointer)path; + d[1] = NULL; + g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + folder_item_find_func, d); + return d[1]; +} + +Folder *folder_get_default_folder(void) +{ + return folder_list ? FOLDER(folder_list->data) : NULL; +} + +FolderItem *folder_get_default_inbox(void) +{ + Folder *folder; + + if (!folder_list) return NULL; + folder = FOLDER(folder_list->data); + g_return_val_if_fail(folder != NULL, NULL); + return folder->inbox; +} + +FolderItem *folder_get_default_outbox(void) +{ + Folder *folder; + + if (!folder_list) return NULL; + folder = FOLDER(folder_list->data); + g_return_val_if_fail(folder != NULL, NULL); + return folder->outbox; +} + +FolderItem *folder_get_default_draft(void) +{ + Folder *folder; + + if (!folder_list) return NULL; + folder = FOLDER(folder_list->data); + g_return_val_if_fail(folder != NULL, NULL); + return folder->draft; +} + +FolderItem *folder_get_default_queue(void) +{ + Folder *folder; + + if (!folder_list) return NULL; + folder = FOLDER(folder_list->data); + g_return_val_if_fail(folder != NULL, NULL); + return folder->queue; +} + +FolderItem *folder_get_default_trash(void) +{ + Folder *folder; + + if (!folder_list) return NULL; + folder = FOLDER(folder_list->data); + g_return_val_if_fail(folder != NULL, NULL); + return folder->trash; +} + +#define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type) \ +{ \ + if (!folder->member) { \ + item = folder_item_new(dir, dir); \ + item->stype = type; \ + folder_item_append(rootitem, item); \ + folder->member = item; \ + } \ +} + +void folder_set_missing_folders(void) +{ + Folder *folder; + FolderItem *rootitem; + FolderItem *item; + GList *list; + + for (list = folder_list; list != NULL; list = list->next) { + folder = list->data; + if (FOLDER_TYPE(folder) != F_MH) continue; + rootitem = FOLDER_ITEM(folder->node->data); + g_return_if_fail(rootitem != NULL); + + if (folder->inbox && folder->outbox && folder->draft && + folder->queue && folder->trash) + continue; + + if (folder->klass->create_tree(folder) < 0) { + g_warning("%s: can't create the folder tree.\n", + LOCAL_FOLDER(folder)->rootpath); + continue; + } + + CREATE_FOLDER_IF_NOT_EXIST(inbox, INBOX_DIR, F_INBOX); + CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX); + CREATE_FOLDER_IF_NOT_EXIST(draft, DRAFT_DIR, F_DRAFT); + CREATE_FOLDER_IF_NOT_EXIST(queue, QUEUE_DIR, F_QUEUE); + CREATE_FOLDER_IF_NOT_EXIST(trash, TRASH_DIR, F_TRASH); + } +} + +static gboolean folder_unref_account_func(GNode *node, gpointer data) +{ + FolderItem *item = node->data; + PrefsAccount *account = data; + + if (item->account == account) + item->account = NULL; + + return FALSE; +} + +void folder_unref_account_all(PrefsAccount *account) +{ + Folder *folder; + GList *list; + + if (!account) return; + + for (list = folder_list; list != NULL; list = list->next) { + folder = list->data; + if (folder->account == account) + folder->account = NULL; + g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + folder_unref_account_func, account); + } +} + +#undef CREATE_FOLDER_IF_NOT_EXIST + +gchar *folder_get_path(Folder *folder) +{ + gchar *path; + + g_return_val_if_fail(folder != NULL, NULL); + + if (FOLDER_TYPE(folder) == F_MH) { + path = g_filename_from_utf8(LOCAL_FOLDER(folder)->rootpath, + -1, NULL, NULL, NULL); + if (!path) { + g_warning("folder_get_path: faild to convert character set\n"); + path = g_strdup(LOCAL_FOLDER(folder)->rootpath); + } + } else if (FOLDER_TYPE(folder) == F_IMAP) { + g_return_val_if_fail(folder->account != NULL, NULL); + path = g_strconcat(get_imap_cache_dir(), + G_DIR_SEPARATOR_S, + folder->account->recv_server, + G_DIR_SEPARATOR_S, + folder->account->userid, + NULL); + } else if (FOLDER_TYPE(folder) == F_NEWS) { + g_return_val_if_fail(folder->account != NULL, NULL); + path = g_strconcat(get_news_cache_dir(), + G_DIR_SEPARATOR_S, + folder->account->nntp_server, + NULL); + } else + path = NULL; + + return path; +} + +gchar *folder_item_get_path(FolderItem *item) +{ + gchar *folder_path; + gchar *item_path = NULL, *path; + + g_return_val_if_fail(item != NULL, NULL); + + folder_path = folder_get_path(item->folder); + g_return_val_if_fail(folder_path != NULL, NULL); + + if (item->path) { + item_path = g_filename_from_utf8(item->path, -1, + NULL, NULL, NULL); + if (!item_path) { + g_warning("folder_item_get_path: faild to convert character set\n"); + item_path = g_strdup(item->path); + } + } + + if (folder_path[0] == G_DIR_SEPARATOR) { + if (item_path) + path = g_strconcat(folder_path, G_DIR_SEPARATOR_S, + item_path, NULL); + else + path = g_strdup(folder_path); + } else { + if (item_path) + path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, + folder_path, G_DIR_SEPARATOR_S, + item_path, NULL); + else + path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, + folder_path, NULL); + } + + g_free(item_path); + g_free(folder_path); + return path; +} + +gint folder_item_scan(FolderItem *item) +{ + Folder *folder; + + g_return_val_if_fail(item != NULL, -1); + + folder = item->folder; + return folder->klass->scan(folder, item); +} + +static void folder_item_scan_foreach_func(gpointer key, gpointer val, + gpointer data) +{ + folder_item_scan(FOLDER_ITEM(key)); +} + +void folder_item_scan_foreach(GHashTable *table) +{ + g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL); +} + +GSList *folder_item_get_msg_list(FolderItem *item, gboolean use_cache) +{ + Folder *folder; + + g_return_val_if_fail(item != NULL, NULL); + + folder = item->folder; + return folder->klass->get_msg_list(folder, item, use_cache); +} + +gchar *folder_item_fetch_msg(FolderItem *item, gint num) +{ + Folder *folder; + + g_return_val_if_fail(item != NULL, NULL); + + folder = item->folder; + + return folder->klass->fetch_msg(folder, item, num); +} + +gint folder_item_fetch_all_msg(FolderItem *item) +{ + Folder *folder; + GSList *mlist; + GSList *cur; + gint num = 0; + gint ret = 0; + + g_return_val_if_fail(item != NULL, -1); + + debug_print("fetching all messages in %s ...\n", item->path); + + folder = item->folder; + + if (folder->ui_func) + folder->ui_func(folder, item, folder->ui_func_data ? + folder->ui_func_data : GINT_TO_POINTER(num)); + + mlist = folder_item_get_msg_list(item, TRUE); + + for (cur = mlist; cur != NULL; cur = cur->next) { + MsgInfo *msginfo = (MsgInfo *)cur->data; + gchar *msg; + + num++; + if (folder->ui_func) + folder->ui_func(folder, item, + folder->ui_func_data ? + folder->ui_func_data : + GINT_TO_POINTER(num)); + + msg = folder_item_fetch_msg(item, msginfo->msgnum); + if (!msg) { + g_warning("Can't fetch message %d. Aborting.\n", + msginfo->msgnum); + ret = -1; + break; + } + g_free(msg); + } + + procmsg_msg_list_free(mlist); + + return ret; +} + +MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num) +{ + Folder *folder; + + g_return_val_if_fail(item != NULL, NULL); + + folder = item->folder; + + return folder->klass->get_msginfo(folder, item, num); +} + +gint folder_item_add_msg(FolderItem *dest, const gchar *file, MsgFlags *flags, + gboolean remove_source) +{ + Folder *folder; + + g_return_val_if_fail(dest != NULL, -1); + g_return_val_if_fail(file != NULL, -1); + g_return_val_if_fail(dest->folder->klass->add_msg != NULL, -1); + + folder = dest->folder; + + return folder->klass->add_msg(folder, dest, file, flags, remove_source); +} + +gint folder_item_add_msgs(FolderItem *dest, GSList *file_list, + gboolean remove_source, gint *first) +{ + Folder *folder; + + g_return_val_if_fail(dest != NULL, -1); + g_return_val_if_fail(file_list != NULL, -1); + g_return_val_if_fail(dest->folder->klass->add_msgs != NULL, -1); + + folder = dest->folder; + + return folder->klass->add_msgs(folder, dest, file_list, remove_source, + first); +} + +gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo) +{ + Folder *folder; + + g_return_val_if_fail(dest != NULL, -1); + g_return_val_if_fail(msginfo != NULL, -1); + g_return_val_if_fail(dest->folder->klass->move_msg != NULL, -1); + + folder = dest->folder; + + return folder->klass->move_msg(folder, dest, msginfo); +} + +gint folder_item_move_msgs(FolderItem *dest, GSList *msglist) +{ + Folder *folder; + + g_return_val_if_fail(dest != NULL, -1); + g_return_val_if_fail(msglist != NULL, -1); + g_return_val_if_fail(dest->folder->klass->move_msgs != NULL, -1); + + folder = dest->folder; + + return folder->klass->move_msgs(folder, dest, msglist); +} + +gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo) +{ + Folder *folder; + + g_return_val_if_fail(dest != NULL, -1); + g_return_val_if_fail(msginfo != NULL, -1); + g_return_val_if_fail(dest->folder->klass->copy_msg != NULL, -1); + + folder = dest->folder; + + return folder->klass->copy_msg(folder, dest, msginfo); +} + +gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist) +{ + Folder *folder; + + g_return_val_if_fail(dest != NULL, -1); + g_return_val_if_fail(msglist != NULL, -1); + g_return_val_if_fail(dest->folder->klass->copy_msgs != NULL, -1); + + folder = dest->folder; + + return folder->klass->copy_msgs(folder, dest, msglist); +} + +gint folder_item_remove_msg(FolderItem *item, MsgInfo *msginfo) +{ + Folder *folder; + + g_return_val_if_fail(item != NULL, -1); + g_return_val_if_fail(item->folder->klass->remove_msg != NULL, -1); + + folder = item->folder; + + return folder->klass->remove_msg(folder, item, msginfo); +} + +gint folder_item_remove_msgs(FolderItem *item, GSList *msglist) +{ + Folder *folder; + gint ret = 0; + + g_return_val_if_fail(item != NULL, -1); + + folder = item->folder; + if (folder->klass->remove_msgs) { + return folder->klass->remove_msgs(folder, item, msglist); + } + + while (msglist != NULL) { + MsgInfo *msginfo = (MsgInfo *)msglist->data; + + ret = folder_item_remove_msg(item, msginfo); + if (ret != 0) break; + msglist = msglist->next; + } + + return ret; +} + +gint folder_item_remove_all_msg(FolderItem *item) +{ + Folder *folder; + + g_return_val_if_fail(item != NULL, -1); + g_return_val_if_fail(item->folder->klass->remove_all_msg != NULL, -1); + + folder = item->folder; + + return folder->klass->remove_all_msg(folder, item); +} + +gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo) +{ + Folder *folder; + + g_return_val_if_fail(item != NULL, FALSE); + g_return_val_if_fail(item->folder->klass->is_msg_changed != NULL, + FALSE); + + folder = item->folder; + return folder->klass->is_msg_changed(folder, item, msginfo); +} + +gint folder_item_close(FolderItem *item) +{ + Folder *folder; + + g_return_val_if_fail(item != NULL, -1); + + item->opened = FALSE; + folder = item->folder; + return folder->klass->close(folder, item); +} + +gchar *folder_item_get_cache_file(FolderItem *item) +{ + gchar *path; + gchar *file; + + g_return_val_if_fail(item != NULL, NULL); + g_return_val_if_fail(item->path != NULL, NULL); + + path = folder_item_get_path(item); + g_return_val_if_fail(path != NULL, NULL); + if (!is_dir_exist(path)) + make_dir_hier(path); + file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL); + g_free(path); + + return file; +} + +gchar *folder_item_get_mark_file(FolderItem *item) +{ + gchar *path; + gchar *file; + + g_return_val_if_fail(item != NULL, NULL); + g_return_val_if_fail(item->path != NULL, NULL); + + path = folder_item_get_path(item); + g_return_val_if_fail(path != NULL, NULL); + if (!is_dir_exist(path)) + make_dir_hier(path); + file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL); + g_free(path); + + return file; +} + +static gboolean folder_build_tree(GNode *node, gpointer data) +{ + Folder *folder = FOLDER(data); + FolderItem *item; + XMLNode *xmlnode; + GList *list; + SpecialFolderItemType stype = F_NORMAL; + const gchar *name = NULL; + const gchar *path = NULL; + PrefsAccount *account = NULL; + gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE, + threaded = TRUE, ac_apply_sub = FALSE; + FolderSortKey sort_key = SORT_BY_NONE; + FolderSortType sort_type = SORT_ASCENDING; + gint new = 0, unread = 0, total = 0; + time_t mtime = 0; + gboolean use_auto_to_on_reply = FALSE; + gchar *auto_to = NULL, *auto_cc = NULL, *auto_bcc = NULL, + *auto_replyto = NULL; + gboolean trim_summary_subject = FALSE, trim_compose_subject = FALSE; + + g_return_val_if_fail(node->data != NULL, FALSE); + if (!node->parent) return FALSE; + + xmlnode = node->data; + if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) { + g_warning("tag name != \"folderitem\"\n"); + return FALSE; + } + + list = xmlnode->tag->attr; + for (; list != NULL; list = list->next) { + XMLAttr *attr = list->data; + + if (!attr || !attr->name || !attr->value) continue; + if (!strcmp(attr->name, "type")) { + if (!strcasecmp(attr->value, "normal")) + stype = F_NORMAL; + else if (!strcasecmp(attr->value, "inbox")) + stype = F_INBOX; + else if (!strcasecmp(attr->value, "outbox")) + stype = F_OUTBOX; + else if (!strcasecmp(attr->value, "draft")) + stype = F_DRAFT; + else if (!strcasecmp(attr->value, "queue")) + stype = F_QUEUE; + else if (!strcasecmp(attr->value, "trash")) + stype = F_TRASH; + } else if (!strcmp(attr->name, "name")) + name = attr->value; + else if (!strcmp(attr->name, "path")) + path = attr->value; + else if (!strcmp(attr->name, "mtime")) + mtime = strtoul(attr->value, NULL, 10); + else if (!strcmp(attr->name, "new")) + new = atoi(attr->value); + else if (!strcmp(attr->name, "unread")) + unread = atoi(attr->value); + else if (!strcmp(attr->name, "total")) + total = atoi(attr->value); + else if (!strcmp(attr->name, "no_sub")) + no_sub = *attr->value == '1' ? TRUE : FALSE; + else if (!strcmp(attr->name, "no_select")) + no_select = *attr->value == '1' ? TRUE : FALSE; + else if (!strcmp(attr->name, "collapsed")) + collapsed = *attr->value == '1' ? TRUE : FALSE; + else if (!strcmp(attr->name, "threaded")) + threaded = *attr->value == '1' ? TRUE : FALSE; + else if (!strcmp(attr->name, "sort_key")) { + if (!strcmp(attr->value, "none")) + sort_key = SORT_BY_NONE; + else if (!strcmp(attr->value, "number")) + sort_key = SORT_BY_NUMBER; + else if (!strcmp(attr->value, "size")) + sort_key = SORT_BY_SIZE; + else if (!strcmp(attr->value, "date")) + sort_key = SORT_BY_DATE; + else if (!strcmp(attr->value, "from")) + sort_key = SORT_BY_FROM; + else if (!strcmp(attr->value, "subject")) + sort_key = SORT_BY_SUBJECT; + else if (!strcmp(attr->value, "score")) + sort_key = SORT_BY_SCORE; + else if (!strcmp(attr->value, "label")) + sort_key = SORT_BY_LABEL; + else if (!strcmp(attr->value, "mark")) + sort_key = SORT_BY_MARK; + else if (!strcmp(attr->value, "unread")) + sort_key = SORT_BY_UNREAD; + else if (!strcmp(attr->value, "mime")) + sort_key = SORT_BY_MIME; + else if (!strcmp(attr->value, "to")) + sort_key = SORT_BY_TO; + } else if (!strcmp(attr->name, "sort_type")) { + if (!strcmp(attr->value, "ascending")) + sort_type = SORT_ASCENDING; + else + sort_type = SORT_DESCENDING; + } else if (!strcmp(attr->name, "account_id")) { + account = account_find_from_id(atoi(attr->value)); + if (!account) g_warning("account_id: %s not found\n", + attr->value); + } else if (!strcmp(attr->name, "account_apply_sub")) + ac_apply_sub = *attr->value == '1' ? TRUE : FALSE; + else if (!strcmp(attr->name, "to")) + auto_to = g_strdup(attr->value); + else if (!strcmp(attr->name, "use_auto_to_on_reply")) + use_auto_to_on_reply = + *attr->value == '1' ? TRUE : FALSE; + else if (!strcmp(attr->name, "cc")) + auto_cc = g_strdup(attr->value); + else if (!strcmp(attr->name, "bcc")) + auto_bcc = g_strdup(attr->value); + else if (!strcmp(attr->name, "replyto")) + auto_replyto = g_strdup(attr->value); + else if (!strcmp(attr->name, "trim_summary_subject")) { + trim_summary_subject = + *attr->value == '1' ? TRUE : FALSE; + } else if (!strcmp(attr->name, "trim_compose_subject")) { + trim_compose_subject = + *attr->value = '1' ? TRUE : FALSE; + } + } + + item = folder_item_new(name, path); + item->stype = stype; + item->mtime = mtime; + item->new = new; + item->unread = unread; + item->total = total; + item->no_sub = no_sub; + item->no_select = no_select; + item->collapsed = collapsed; + item->threaded = threaded; + item->sort_key = sort_key; + item->sort_type = sort_type; + item->node = node; + item->parent = FOLDER_ITEM(node->parent->data); + item->folder = folder; + switch (stype) { + case F_INBOX: folder->inbox = item; break; + case F_OUTBOX: folder->outbox = item; break; + case F_DRAFT: folder->draft = item; break; + case F_QUEUE: folder->queue = item; break; + case F_TRASH: folder->trash = item; break; + default: break; + } + item->account = account; + item->ac_apply_sub = ac_apply_sub; + item->auto_to = auto_to; + item->use_auto_to_on_reply = use_auto_to_on_reply; + item->auto_cc = auto_cc; + item->auto_bcc = auto_bcc; + item->auto_replyto = auto_replyto; + item->trim_summary_subject = trim_summary_subject; + item->trim_compose_subject = trim_compose_subject; + node->data = item; + xml_free_node(xmlnode); + + return FALSE; +} + +static gboolean folder_read_folder_func(GNode *node, gpointer data) +{ + Folder *folder; + FolderItem *item; + XMLNode *xmlnode; + GList *list; + FolderType type = F_UNKNOWN; + const gchar *name = NULL; + const gchar *path = NULL; + PrefsAccount *account = NULL; + gboolean collapsed = FALSE, threaded = TRUE, ac_apply_sub = FALSE; + + if (g_node_depth(node) != 2) return FALSE; + g_return_val_if_fail(node->data != NULL, FALSE); + + xmlnode = node->data; + if (strcmp2(xmlnode->tag->tag, "folder") != 0) { + g_warning("tag name != \"folder\"\n"); + return TRUE; + } + g_node_unlink(node); + list = xmlnode->tag->attr; + for (; list != NULL; list = list->next) { + XMLAttr *attr = list->data; + + if (!attr || !attr->name || !attr->value) continue; + if (!strcmp(attr->name, "type")) { + if (!strcasecmp(attr->value, "mh")) + type = F_MH; + else if (!strcasecmp(attr->value, "mbox")) + type = F_MBOX; + else if (!strcasecmp(attr->value, "maildir")) + type = F_MAILDIR; + else if (!strcasecmp(attr->value, "imap")) + type = F_IMAP; + else if (!strcasecmp(attr->value, "news")) + type = F_NEWS; + } else if (!strcmp(attr->name, "name")) + name = attr->value; + else if (!strcmp(attr->name, "path")) + path = attr->value; + else if (!strcmp(attr->name, "collapsed")) + collapsed = *attr->value == '1' ? TRUE : FALSE; + else if (!strcmp(attr->name, "threaded")) + threaded = *attr->value == '1' ? TRUE : FALSE; + else if (!strcmp(attr->name, "account_id")) { + account = account_find_from_id(atoi(attr->value)); + if (!account) g_warning("account_id: %s not found\n", + attr->value); + } else if (!strcmp(attr->name, "account_apply_sub")) + ac_apply_sub = *attr->value == '1' ? TRUE : FALSE; + } + + folder = folder_new(type, name, path); + g_return_val_if_fail(folder != NULL, FALSE); + folder->account = account; + if (account && (type == F_IMAP || type == F_NEWS)) + account->folder = REMOTE_FOLDER(folder); + item = FOLDER_ITEM(folder->node->data); + node->data = item; + item->node = node; + g_node_destroy(folder->node); + folder->node = node; + folder_add(folder); + item->collapsed = collapsed; + item->threaded = threaded; + item->account = account; + item->ac_apply_sub = ac_apply_sub; + + g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + folder_build_tree, folder); + + return FALSE; +} + +static gchar *folder_get_list_path(void) +{ + static gchar *filename = NULL; + + if (!filename) + filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, + FOLDER_LIST, NULL); + + return filename; +} + +#define PUT_ESCAPE_STR(fp, attr, str) \ +{ \ + fputs(" " attr "=\"", fp); \ + xml_file_put_escape_str(fp, str); \ + fputs("\"", fp); \ +} + +static void folder_write_list_recursive(GNode *node, gpointer data) +{ + FILE *fp = (FILE *)data; + FolderItem *item; + gint i, depth; + static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap", + "news", "unknown"}; + static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox", + "draft", "queue", "trash"}; + static gchar *sort_key_str[] = {"none", "number", "size", "date", + "from", "subject", "score", "label", + "mark", "unread", "mime", "to"}; + + g_return_if_fail(node != NULL); + g_return_if_fail(fp != NULL); + + item = FOLDER_ITEM(node->data); + g_return_if_fail(item != NULL); + + depth = g_node_depth(node); + for (i = 0; i < depth; i++) + fputs(" ", fp); + if (depth == 1) { + Folder *folder = item->folder; + + fprintf(fp, "name) + PUT_ESCAPE_STR(fp, "name", folder->name); + if (FOLDER_TYPE(folder) == F_MH) + PUT_ESCAPE_STR(fp, "path", + LOCAL_FOLDER(folder)->rootpath); + if (item->collapsed && node->children) + fputs(" collapsed=\"1\"", fp); + if (folder->account) + fprintf(fp, " account_id=\"%d\"", + folder->account->account_id); + if (item->ac_apply_sub) + fputs(" account_apply_sub=\"1\"", fp); + } else { + fprintf(fp, "stype]); + if (item->name) + PUT_ESCAPE_STR(fp, "name", item->name); + if (item->path) + PUT_ESCAPE_STR(fp, "path", item->path); + + if (item->no_sub) + fputs(" no_sub=\"1\"", fp); + if (item->no_select) + fputs(" no_select=\"1\"", fp); + if (item->collapsed && node->children) + fputs(" collapsed=\"1\"", fp); + if (item->threaded) + fputs(" threaded=\"1\"", fp); + else + fputs(" threaded=\"0\"", fp); + + if (item->sort_key != SORT_BY_NONE) { + fprintf(fp, " sort_key=\"%s\"", + sort_key_str[item->sort_key]); + if (item->sort_type == SORT_ASCENDING) + fprintf(fp, " sort_type=\"ascending\""); + else + fprintf(fp, " sort_type=\"descending\""); + } + + fprintf(fp, + " mtime=\"%lu\" new=\"%d\" unread=\"%d\" total=\"%d\"", + item->mtime, item->new, item->unread, item->total); + + if (item->account) + fprintf(fp, " account_id=\"%d\"", + item->account->account_id); + if (item->ac_apply_sub) + fputs(" account_apply_sub=\"1\"", fp); + + if (item->auto_to) + PUT_ESCAPE_STR(fp, "to", item->auto_to); + if (item->use_auto_to_on_reply) + fputs(" use_auto_to_on_reply=\"1\"", fp); + if (item->auto_cc) + PUT_ESCAPE_STR(fp, "cc", item->auto_cc); + if (item->auto_bcc) + PUT_ESCAPE_STR(fp, "bcc", item->auto_bcc); + if (item->auto_replyto) + PUT_ESCAPE_STR(fp, "replyto", item->auto_replyto); + + if (item->trim_summary_subject) + fputs(" trim_summary_subject=\"1\"", fp); + if (item->trim_compose_subject) + fputs(" trim_compose_subject=\"1\"", fp); + } + + if (node->children) { + GNode *child; + fputs(">\n", fp); + + child = node->children; + while (child) { + GNode *cur; + + cur = child; + child = cur->next; + folder_write_list_recursive(cur, data); + } + + for (i = 0; i < depth; i++) + fputs(" ", fp); + fprintf(fp, "\n", depth == 1 ? "folder" : "folderitem"); + } else + fputs(" />\n", fp); +} + +#undef PUT_ESCAPE_STR -- cgit v1.2.3