/* * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client * Copyright (C) 1999-2002 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 #include #include #include "intl.h" #include "folder.h" #include "session.h" #include "imap.h" #include "news.h" #include "mh.h" #include "mbox_folder.h" #include "utils.h" #include "xml.h" #include "codeconv.h" #include "prefs_gtk.h" #include "account.h" #include "filtering.h" #include "scoring.h" #include "prefs_folder_item.h" #include "procheader.h" #include "hooks.h" #include "log.h" /* Dependecies to be removed ?! */ #include "prefs_common.h" #include "prefs_account.h" #include "prefs_folder_item.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); static void folder_update_op_count_rec (GNode *node); static void folder_get_persist_prefs_recursive (GNode *node, GHashTable *pptable); static gboolean persist_prefs_free (gpointer key, gpointer val, gpointer data); void folder_item_read_cache (FolderItem *item); void folder_item_free_cache (FolderItem *item); Folder *folder_new(FolderType type, const gchar *name, const gchar *path) { Folder *folder = NULL; FolderItem *item; name = name ? name : path; switch (type) { case F_MBOX: folder = mbox_folder_new(name, path); break; case F_MH: folder = mh_folder_new(name, path); break; case F_IMAP: folder = imap_folder_new(name, path); break; case F_NEWS: folder = news_folder_new(name, path); break; default: return NULL; } /* Create root folder item */ item = folder_item_new(folder, name, NULL); item->folder = folder; folder->node = g_node_new(item); folder->data = NULL; return folder; } static void folder_init(Folder *folder, const gchar *name) { g_return_if_fail(folder != NULL); folder_set_name(folder, name); /* Init folder data */ folder->account = NULL; folder->inbox = NULL; folder->outbox = NULL; folder->draft = NULL; folder->queue = NULL; folder->trash = NULL; /* Init Folder functions */ folder->item_new = NULL; folder->item_destroy = NULL; folder->fetch_msg = NULL; folder->get_msginfo = NULL; folder->get_msginfos = NULL; folder->get_num_list = NULL; folder->ui_func = NULL; folder->ui_func_data = NULL; folder->change_flags = NULL; folder->check_msgnum_validity = 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->destroy != NULL); folder->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(Folder *folder, const gchar *name, const gchar *path) { FolderItem *item = NULL; if (folder->item_new) { item = folder->item_new(folder); } else { item = g_new0(FolderItem, 1); } g_return_val_if_fail(item != NULL, NULL); item->stype = F_NORMAL; item->name = g_strdup(name); item->path = g_strdup(path); item->mtime = 0; item->new = 0; item->unread = 0; item->unreadmarked = 0; item->total = 0; item->last_num = -1; item->cache = NULL; item->no_sub = FALSE; item->no_select = FALSE; item->collapsed = FALSE; item->thread_collapsed = FALSE; item->threaded = TRUE; item->ret_rcpt = FALSE; item->opened = FALSE; item->parent = NULL; item->folder = NULL; item->account = NULL; item->apply_sub = FALSE; item->mark_queue = NULL; item->data = NULL; item->prefs = prefs_folder_item_new(); return item; } void folder_item_append(FolderItem *parent, FolderItem *item) { GNode *node; g_return_if_fail(parent != NULL); g_return_if_fail(parent->folder != NULL); g_return_if_fail(item != NULL); node = parent->folder->node; node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, parent); g_return_if_fail(node != NULL); item->parent = parent; item->folder = parent->folder; g_node_append_data(node, item); } void folder_item_remove(FolderItem *item) { GNode *node; g_return_if_fail(item != NULL); g_return_if_fail(item->folder != NULL); node = item->folder->node; node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item); g_return_if_fail(node != NULL); /* TODO: free all FolderItem's first */ if (item->folder->node == node) item->folder->node = NULL; g_node_destroy(node); } void folder_item_destroy(FolderItem *item) { g_return_if_fail(item != NULL); debug_print("Destroying folder item %s\n", item->path); if (item->cache) folder_item_free_cache(item); g_free(item->name); g_free(item->path); if (item->folder != NULL) { if(item->folder->item_destroy) { item->folder->item_destroy(item->folder, item); } else { 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; } } gboolean folder_tree_destroy_func(GNode *node, gpointer data) { FolderItem *item = (FolderItem *) node->data; folder_item_destroy(item); return FALSE; } void folder_tree_destroy(Folder *folder) { g_return_if_fail(folder != NULL); g_return_if_fail(folder->node != NULL); prefs_scoring_clear(); prefs_filtering_clear(); g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_tree_destroy_func, NULL); if (folder->node) g_node_destroy(folder->node); folder->inbox = NULL; folder->outbox = NULL; folder->draft = NULL; folder->queue = NULL; folder->trash = NULL; folder->node = NULL; } 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 == F_MH) { if (cur_folder->type != F_MH) break; } else if (folder->type == F_MBOX) { if (cur_folder->type != F_MH && cur_folder->type != F_MBOX) break; } else if (folder->type == F_IMAP) { if (cur_folder->type != F_MH && cur_folder->type != F_MBOX && cur_folder->type != F_IMAP) break; } else if (folder->type == F_NEWS) { if (cur_folder->type != F_MH && cur_folder->type != F_MBOX && cur_folder->type != F_IMAP && cur_folder->type != 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_write_open(path)) == NULL) return; fprintf(pfile->fp, "\n", conv_get_current_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"); } gboolean folder_scan_tree_func(GNode *node, gpointer data) { GHashTable *pptable = (GHashTable *)data; FolderItem *item = (FolderItem *)node->data; folder_item_restore_persist_prefs(item, pptable); return FALSE; } void folder_scan_tree(Folder *folder) { GHashTable *pptable; if (!folder->scan_tree) return; pptable = folder_persist_prefs_new(folder); folder_tree_destroy(folder); folder->scan_tree(folder); g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_scan_tree_func, pptable); folder_persist_prefs_free(pptable); prefs_matcher_read_config(); folder_write_list(); } FolderItem *folder_create_folder(FolderItem *parent, const gchar *name) { FolderItem *new_item; new_item = parent->folder->create_folder(parent->folder, parent, name); if (new_item) new_item->cache = msgcache_new(); return new_item; } struct TotalMsgCount { guint new; guint unread; guint unreadmarked; guint total; }; struct FuncToAllFoldersData { FolderItemFunc function; gpointer data; }; static gboolean folder_func_to_all_folders_func(GNode *node, gpointer data) { FolderItem *item; struct FuncToAllFoldersData *function_data = (struct FuncToAllFoldersData *) data; g_return_val_if_fail(node->data != NULL, FALSE); item = FOLDER_ITEM(node->data); g_return_val_if_fail(item != NULL, FALSE); function_data->function(item, function_data->data); return FALSE; } void folder_func_to_all_folders(FolderItemFunc function, gpointer data) { GList *list; Folder *folder; struct FuncToAllFoldersData function_data; function_data.function = function; function_data.data = data; 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_func_to_all_folders_func, &function_data); } } static void folder_count_total_msgs_func(FolderItem *item, gpointer data) { struct TotalMsgCount *count = (struct TotalMsgCount *)data; count->new += item->new; count->unread += item->unread; count->unreadmarked += item->unreadmarked; count->total += item->total; } void folder_count_total_msgs(guint *new, guint *unread, guint *unreadmarked, guint *total) { struct TotalMsgCount count; count.new = count.unread = count.unreadmarked = count.total = 0; debug_print("Counting total number of messages...\n"); folder_func_to_all_folders(folder_count_total_msgs_func, &count); *new = count.new; *unread = count.unread; *unreadmarked = count.unreadmarked; *total = count.total; } 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 == F_MH || folder->type == F_MBOX) && !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 == 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]; } 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); 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(folder, 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 != 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->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); switch(FOLDER_TYPE(folder)) { case F_MH: path = g_strdup(LOCAL_FOLDER(folder)->rootpath); break; case 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); break; case 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); break; default: path = NULL; break; } return path; } gchar *folder_item_get_path(FolderItem *item) { gchar *folder_path; gchar *path; g_return_val_if_fail(item != NULL, NULL); if(FOLDER_TYPE(item->folder) != F_MBOX) { folder_path = folder_get_path(item->folder); g_return_val_if_fail(folder_path != NULL, NULL); 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(folder_path); } else { gchar *itempath; itempath = mbox_get_virtual_path(item); if (itempath == NULL) return NULL; path = g_strconcat(get_mbox_cache_dir(), G_DIR_SEPARATOR_S, itempath, NULL); g_free(itempath); } return path; } void folder_item_set_default_flags(FolderItem *dest, MsgFlags *flags) { if (!(dest->stype == F_OUTBOX || dest->stype == F_QUEUE || dest->stype == F_DRAFT || dest->stype == F_TRASH)) { flags->perm_flags = MSG_NEW|MSG_UNREAD; } else { flags->perm_flags = 0; } flags->tmp_flags = MSG_CACHED; if (dest->folder->type == F_MH) { if (dest->stype == F_QUEUE) { MSG_SET_TMP_FLAGS(*flags, MSG_QUEUED); } else if (dest->stype == F_DRAFT) { MSG_SET_TMP_FLAGS(*flags, MSG_DRAFT); } } else if (dest->folder->type == F_IMAP) { MSG_SET_TMP_FLAGS(*flags, MSG_IMAP); } else if (dest->folder->type == F_NEWS) { MSG_SET_TMP_FLAGS(*flags, MSG_NEWS); } } static gint folder_sort_cache_list_by_msgnum(gconstpointer a, gconstpointer b) { MsgInfo *msginfo_a = (MsgInfo *) a; MsgInfo *msginfo_b = (MsgInfo *) b; return (msginfo_a->msgnum - msginfo_b->msgnum); } static gint folder_sort_folder_list(gconstpointer a, gconstpointer b) { guint gint_a = GPOINTER_TO_INT(a); guint gint_b = GPOINTER_TO_INT(b); return (gint_a - gint_b); } gint folder_item_open(FolderItem *item) { if(((item->folder->type == F_IMAP) && !item->no_select) || (item->folder->type == F_NEWS)) { folder_item_scan(item); } /* Processing */ if(item->prefs->processing != NULL) { gchar *buf; buf = g_strdup_printf(_("Processing (%s)...\n"), item->path); debug_print("%s\n", buf); g_free(buf); folder_item_apply_processing(item); debug_print("done.\n"); } return 0; } void folder_item_close(FolderItem *item) { GSList *mlist, *cur; g_return_if_fail(item != NULL); if (item->new) { mlist = folder_item_get_msg_list(item); for (cur = mlist ; cur != NULL ; cur = cur->next) { MsgInfo * msginfo; msginfo = (MsgInfo *) cur->data; if (MSG_IS_NEW(msginfo->flags)) procmsg_msginfo_unset_flags(msginfo, MSG_NEW, 0); procmsg_msginfo_free(msginfo); } g_slist_free(mlist); } folder_item_write_cache(item); folder_item_update(item, F_ITEM_UPDATE_MSGCNT); } gint folder_item_scan(FolderItem *item) { Folder *folder; GSList *folder_list = NULL, *cache_list = NULL, *folder_list_cur, *cache_list_cur, *new_list = NULL; guint newcnt = 0, unreadcnt = 0, totalcnt = 0, unreadmarkedcnt = 0; guint cache_max_num, folder_max_num, cache_cur_num, folder_cur_num; gboolean update_flags = 0; g_return_val_if_fail(item != NULL, -1); if (item->path == NULL) return -1; folder = item->folder; g_return_val_if_fail(folder != NULL, -1); g_return_val_if_fail(folder->get_num_list != NULL, -1); debug_print("Scanning folder %s for cache changes.\n", item->path); /* Get list of messages for folder and cache */ if (folder->get_num_list(item->folder, item, &folder_list) < 0) { debug_print("Error fetching list of message numbers\n"); return(-1); } if (!folder->check_msgnum_validity || folder->check_msgnum_validity(folder, item)) { if (!item->cache) folder_item_read_cache(item); cache_list = msgcache_get_msg_list(item->cache); } else { if (item->cache) msgcache_destroy(item->cache); item->cache = msgcache_new(); cache_list = NULL; } /* Sort both lists */ cache_list = g_slist_sort(cache_list, folder_sort_cache_list_by_msgnum); folder_list = g_slist_sort(folder_list, folder_sort_folder_list); cache_list_cur = cache_list; folder_list_cur = folder_list; if (cache_list_cur != NULL) { GSList *cache_list_last; cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum; cache_list_last = g_slist_last(cache_list); cache_max_num = ((MsgInfo *)cache_list_last->data)->msgnum; } else { cache_cur_num = G_MAXINT; cache_max_num = 0; } if (folder_list_cur != NULL) { GSList *folder_list_last; folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data); folder_list_last = g_slist_last(folder_list); folder_max_num = GPOINTER_TO_INT(folder_list_last->data); } else { folder_cur_num = G_MAXINT; folder_max_num = 0; } while ((cache_cur_num != G_MAXINT) || (folder_cur_num != G_MAXINT)) { /* * Message only exists in the folder * Remember message for fetching */ if (folder_cur_num < cache_cur_num) { gboolean add = FALSE; switch(folder->type) { case F_NEWS: if (folder_cur_num < cache_max_num) break; if (prefs_common.max_articles == 0) { add = TRUE; } if (folder_max_num <= prefs_common.max_articles) { add = TRUE; } else if (folder_cur_num > (folder_max_num - prefs_common.max_articles)) { add = TRUE; } break; default: add = TRUE; break; } if (add) { new_list = g_slist_prepend(new_list, GINT_TO_POINTER(folder_cur_num)); debug_print("Remembered message %d for fetching\n", folder_cur_num); } /* Move to next folder number */ folder_list_cur = folder_list_cur->next; if (folder_list_cur != NULL) folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data); else folder_cur_num = G_MAXINT; continue; } /* * Message only exists in the cache * Remove the message from the cache */ if (cache_cur_num < folder_cur_num) { msgcache_remove_msg(item->cache, cache_cur_num); debug_print("Removed message %d from cache.\n", cache_cur_num); /* Move to next cache number */ cache_list_cur = cache_list_cur->next; if (cache_list_cur != NULL) cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum; else cache_cur_num = G_MAXINT; update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT; continue; } /* * Message number exists in folder and cache! * Check if the message has been modified */ if (cache_cur_num == folder_cur_num) { MsgInfo *msginfo; msginfo = msgcache_get_msg(item->cache, folder_cur_num); if (folder->is_msg_changed && folder->is_msg_changed(folder, item, msginfo)) { MsgInfo *newmsginfo; msgcache_remove_msg(item->cache, msginfo->msgnum); if (NULL != (newmsginfo = folder->get_msginfo(folder, item, folder_cur_num))) { msgcache_add_msg(item->cache, newmsginfo); if (MSG_IS_NEW(newmsginfo->flags) && !MSG_IS_IGNORE_THREAD(newmsginfo->flags)) newcnt++; if (MSG_IS_UNREAD(newmsginfo->flags) && !MSG_IS_IGNORE_THREAD(newmsginfo->flags)) unreadcnt++; if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo)) unreadmarkedcnt++; if (procmsg_msg_has_flagged_parent(newmsginfo, MSG_IGNORE_THREAD)) procmsg_msginfo_set_flags(newmsginfo, MSG_IGNORE_THREAD, 0); procmsg_msginfo_free(newmsginfo); } debug_print("Updated msginfo for message %d.\n", folder_cur_num); } else { if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) newcnt++; if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) unreadcnt++; if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo)) unreadmarkedcnt++; if (!MSG_IS_IGNORE_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD)) procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0); } totalcnt++; procmsg_msginfo_free(msginfo); /* Move to next folder and cache number */ cache_list_cur = cache_list_cur->next; folder_list_cur = folder_list_cur->next; if (cache_list_cur != NULL) cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum; else cache_cur_num = G_MAXINT; if (folder_list_cur != NULL) folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data); else folder_cur_num = G_MAXINT; update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT; continue; } } for(cache_list_cur = cache_list; cache_list_cur != NULL; cache_list_cur = g_slist_next(cache_list_cur)) { procmsg_msginfo_free((MsgInfo *) cache_list_cur->data); } g_slist_free(cache_list); g_slist_free(folder_list); if (folder->get_msginfos) { GSList *elem; GSList *newmsg_list; MsgInfo *msginfo; if (new_list) { newmsg_list = folder->get_msginfos(folder, item, new_list); for (elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) { msginfo = (MsgInfo *) elem->data; msgcache_add_msg(item->cache, msginfo); if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) newcnt++; if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) unreadcnt++; if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo)) unreadmarkedcnt++; if (procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD)) procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0); totalcnt++; procmsg_msginfo_free(msginfo); update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT; } g_slist_free(newmsg_list); } } else if (folder->get_msginfo) { GSList *elem; for (elem = new_list; elem != NULL; elem = g_slist_next(elem)) { MsgInfo *msginfo; guint num; num = GPOINTER_TO_INT(elem->data); msginfo = folder->get_msginfo(folder, item, num); if (msginfo != NULL) { msgcache_add_msg(item->cache, msginfo); if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) newcnt++; if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) unreadcnt++; if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo)) unreadmarkedcnt++; if (procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD)) procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0); totalcnt++; procmsg_msginfo_free(msginfo); debug_print("Added newly found message %d to cache.\n", num); } update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT; } } item->new = newcnt; item->unread = unreadcnt; item->total = totalcnt; item->unreadmarked = unreadmarkedcnt; g_slist_free(new_list); folder_item_update(item, update_flags); return 0; } 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); } void folder_count_total_cache_memusage(FolderItem *item, gpointer data) { gint *memusage = (gint *)data; if (item->cache == NULL) return; *memusage += msgcache_get_memory_usage(item->cache); } gint folder_cache_time_compare_func(gconstpointer a, gconstpointer b) { FolderItem *fa = (FolderItem *)a; FolderItem *fb = (FolderItem *)b; return (gint) (msgcache_get_last_access_time(fa->cache) - msgcache_get_last_access_time(fb->cache)); } void folder_find_expired_caches(FolderItem *item, gpointer data) { GSList **folder_item_list = (GSList **)data; gint difftime, expiretime; if (item->cache == NULL) return; if (item->opened > 0) return; difftime = (gint) (time(NULL) - msgcache_get_last_access_time(item->cache)); expiretime = prefs_common.cache_min_keep_time * 60; debug_print("Cache unused time: %d (Expire time: %d)\n", difftime, expiretime); if (difftime > expiretime) { *folder_item_list = g_slist_insert_sorted(*folder_item_list, item, folder_cache_time_compare_func); } } void folder_item_free_cache(FolderItem *item) { g_return_if_fail(item != NULL); if (item->cache == NULL) return; if (item->opened > 0) return; folder_item_write_cache(item); msgcache_destroy(item->cache); item->cache = NULL; } void folder_clean_cache_memory() { gint memusage = 0; folder_func_to_all_folders(folder_count_total_cache_memusage, &memusage); debug_print("Total cache memory usage: %d\n", memusage); if (memusage > (prefs_common.cache_max_mem_usage * 1024)) { GSList *folder_item_list = NULL, *listitem; debug_print("Trying to free cache memory\n"); folder_func_to_all_folders(folder_find_expired_caches, &folder_item_list); listitem = folder_item_list; while((listitem != NULL) && (memusage > (prefs_common.cache_max_mem_usage * 1024))) { FolderItem *item = (FolderItem *)(listitem->data); debug_print("Freeing cache memory for %s\n", item->path); memusage -= msgcache_get_memory_usage(item->cache); folder_item_free_cache(item); listitem = listitem->next; } g_slist_free(folder_item_list); } } void folder_item_read_cache(FolderItem *item) { gchar *cache_file, *mark_file; g_return_if_fail(item != NULL); cache_file = folder_item_get_cache_file(item); mark_file = folder_item_get_mark_file(item); item->cache = msgcache_read_cache(item, cache_file); if (!item->cache) { item->cache = msgcache_new(); folder_item_scan(item); } msgcache_read_mark(item->cache, mark_file); g_free(cache_file); g_free(mark_file); folder_clean_cache_memory(); } void folder_item_write_cache(FolderItem *item) { gchar *cache_file, *mark_file; PrefsFolderItem *prefs; gint filemode = 0; gchar *id; if (!item || !item->path || !item->cache) return; id = folder_item_get_identifier(item); debug_print("Save cache for folder %s\n", id); g_free(id); cache_file = folder_item_get_cache_file(item); mark_file = folder_item_get_mark_file(item); if (msgcache_write(cache_file, mark_file, item->cache) < 0) { prefs = item->prefs; if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) { /* for cache file */ filemode = prefs->folder_chmod; if (filemode & S_IRGRP) filemode |= S_IWGRP; if (filemode & S_IROTH) filemode |= S_IWOTH; chmod(cache_file, filemode); } } g_free(cache_file); g_free(mark_file); } MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num) { Folder *folder; MsgInfo *msginfo; g_return_val_if_fail(item != NULL, NULL); folder = item->folder; if (!item->cache) folder_item_read_cache(item); if ((msginfo = msgcache_get_msg(item->cache, num)) != NULL) return msginfo; g_return_val_if_fail(folder->get_msginfo, NULL); if ((msginfo = folder->get_msginfo(folder, item, num)) != NULL) { msgcache_add_msg(item->cache, msginfo); return msginfo; } return NULL; } MsgInfo *folder_item_get_msginfo_by_msgid(FolderItem *item, const gchar *msgid) { Folder *folder; MsgInfo *msginfo; g_return_val_if_fail(item != NULL, NULL); folder = item->folder; if (!item->cache) folder_item_read_cache(item); if ((msginfo = msgcache_get_msg_by_id(item->cache, msgid)) != NULL) return msginfo; return NULL; } GSList *folder_item_get_msg_list(FolderItem *item) { g_return_val_if_fail(item != NULL, NULL); if (item->cache == 0) folder_item_read_cache(item); g_return_val_if_fail(item->cache != NULL, NULL); return msgcache_get_msg_list(item->cache); } gchar *folder_item_fetch_msg(FolderItem *item, gint num) { Folder *folder; g_return_val_if_fail(item != NULL, NULL); folder = item->folder; g_return_val_if_fail(folder->fetch_msg != NULL, NULL); return folder->fetch_msg(folder, item, num); } static gint folder_item_get_msg_num_by_file(FolderItem *dest, const gchar *file) { static HeaderEntry hentry[] = {{"Message-ID:", NULL, TRUE}, {NULL, NULL, FALSE}}; FILE *fp; MsgInfo *msginfo; gint msgnum = 0; gchar buf[BUFFSIZE]; if ((fp = fopen(file, "rb")) == NULL) return 0; if ((dest->stype == F_QUEUE) || (dest->stype == F_DRAFT)) while (fgets(buf, sizeof(buf), fp) != NULL) if (buf[0] == '\r' || buf[0] == '\n') break; procheader_get_header_fields(fp, hentry); if (hentry[0].body) { extract_parenthesis(hentry[0].body, '<', '>'); remove_space(hentry[0].body); if ((msginfo = msgcache_get_msg_by_id(dest->cache, hentry[0].body)) != NULL) { msgnum = msginfo->msgnum; procmsg_msginfo_free(msginfo); debug_print("found message as uid %d\n", msgnum); } } g_free(hentry[0].body); hentry[0].body = NULL; fclose(fp); return msgnum; } gint folder_item_add_msg(FolderItem *dest, const gchar *file, gboolean remove_source) { Folder *folder; gint num; MsgInfo *msginfo; g_return_val_if_fail(dest != NULL, -1); g_return_val_if_fail(file != NULL, -1); folder = dest->folder; g_return_val_if_fail(folder->add_msg != NULL, -1); if (!dest->cache) folder_item_read_cache(dest); num = folder->add_msg(folder, dest, file, FALSE); if (num > 0) { msginfo = folder->get_msginfo(folder, dest, num); if (msginfo != NULL) { if (MSG_IS_NEW(msginfo->flags)) dest->new++; if (MSG_IS_UNREAD(msginfo->flags)) dest->unread++; if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo)) dest->unreadmarked++; if (procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD)) procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0); dest->total++; folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT); msgcache_add_msg(dest->cache, msginfo); procmsg_msginfo_free(msginfo); } dest->last_num = num; } else if (num == 0) { folder_item_scan(dest); num = folder_item_get_msg_num_by_file(dest, file); } if (num >= 0 && remove_source) { if (unlink(file) < 0) FILE_OP_ERROR(file, "unlink"); } return num; } /* gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo) { Folder *folder; gint num; g_return_val_if_fail(dest != NULL, -1); g_return_val_if_fail(msginfo != NULL, -1); folder = dest->folder; if (dest->last_num < 0) folder->scan(folder, dest); num = folder->move_msg(folder, dest, msginfo); if (num > 0) dest->last_num = num; return num; } */ FolderItem *folder_item_move_recursive (FolderItem *src, FolderItem *dest) { GSList *mlist; GSList *cur; FolderItem *new_item; FolderItem *next_item; GNode *srcnode; int cnt = 0; gchar *old_id, *new_id; mlist = folder_item_get_msg_list(src); /* move messages */ debug_print("Moving %s to %s\n", src->path, dest->path); new_item = folder_create_folder(dest, g_basename(src->path)); if (new_item == NULL) { printf("Can't create folder\n"); return NULL; } if (new_item->folder == NULL) new_item->folder = dest->folder; /* move messages */ for (cur = mlist ; cur != NULL ; cur = cur->next) { MsgInfo * msginfo; cnt++; if (cnt%500) log_message(_("Moving %s to %s (%d%%)...\n"), src->name, new_item->path, 100*cnt/g_slist_length(mlist)); msginfo = (MsgInfo *) cur->data; folder_item_move_msg(new_item, msginfo); procmsg_msginfo_free(msginfo); } /*copy prefs*/ prefs_folder_item_copy_prefs(src, new_item); new_item->collapsed = src->collapsed; new_item->thread_collapsed = src->thread_collapsed; new_item->threaded = src->threaded; new_item->ret_rcpt = src->ret_rcpt; new_item->hide_read_msgs = src->hide_read_msgs; new_item->sort_key = src->sort_key; new_item->sort_type = src->sort_type; prefs_matcher_write_config(); /* recurse */ srcnode = src->folder->node; srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, src); srcnode = srcnode->children; while (srcnode != NULL) { if (srcnode && srcnode->data) { next_item = (FolderItem*) srcnode->data; srcnode = srcnode->next; if (folder_item_move_recursive(next_item, new_item) == NULL) return NULL; } } old_id = folder_item_get_identifier(src); new_id = folder_item_get_identifier(new_item); debug_print("updating rules : %s => %s\n", old_id, new_id); src->folder->remove_folder(src->folder, src); folder_write_list(); if (old_id != NULL && new_id != NULL) prefs_filtering_rename_path(old_id, new_id); g_free(old_id); g_free(new_id); return new_item; } gint folder_item_move_to(FolderItem *src, FolderItem *dest, FolderItem **new_item) { FolderItem *tmp = dest->parent; gchar * src_identifier, * dst_identifier; gchar * phys_srcpath, * phys_dstpath; GNode *src_node; while (tmp) { if (tmp == src) { return F_MOVE_FAILED_DEST_IS_CHILD; } tmp = tmp->parent; } tmp = src->parent; src_identifier = folder_item_get_identifier(src); dst_identifier = folder_item_get_identifier(dest); if(dst_identifier == NULL && dest->folder && dest->parent == NULL) { /* dest can be a root folder */ dst_identifier = folder_get_identifier(dest->folder); } if (src_identifier == NULL || dst_identifier == NULL) { printf("Can't get identifiers\n"); return F_MOVE_FAILED; } if (src->folder != dest->folder) { return F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX; } phys_srcpath = folder_item_get_path(src); phys_dstpath = g_strconcat(folder_item_get_path(dest),G_DIR_SEPARATOR_S,g_basename(phys_srcpath),NULL); if (src->parent == dest || src == dest) { g_free(src_identifier); g_free(dst_identifier); g_free(phys_srcpath); g_free(phys_dstpath); return F_MOVE_FAILED_DEST_IS_PARENT; } debug_print("moving \"%s\" to \"%s\"\n", phys_srcpath, phys_dstpath); if ((tmp = folder_item_move_recursive(src, dest)) == NULL) { return F_MOVE_FAILED; } /* update rules */ src_node = g_node_find(src->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, src); if (src_node) g_node_destroy(src_node); else debug_print("can't remove node: it's null!\n"); /* not to much worry if remove fails, move has been done */ g_free(src_identifier); g_free(dst_identifier); g_free(phys_srcpath); g_free(phys_dstpath); *new_item = tmp; return F_MOVE_OK; } gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo) { Folder *folder; gint num; Folder *src_folder; g_return_val_if_fail(dest != NULL, -1); g_return_val_if_fail(msginfo != NULL, -1); folder = dest->folder; g_return_val_if_fail(folder->remove_msg != NULL, -1); g_return_val_if_fail(folder->copy_msg != NULL, -1); if (!dest->cache) folder_item_read_cache(dest); src_folder = msginfo->folder->folder; num = folder->copy_msg(folder, dest, msginfo); if (num > 0) { MsgInfo *newmsginfo; /* Add new msginfo to dest folder */ if (NULL != (newmsginfo = folder->get_msginfo(folder, dest, num))) { newmsginfo->flags.perm_flags = msginfo->flags.perm_flags; if (dest->stype == F_OUTBOX || dest->stype == F_QUEUE || dest->stype == F_DRAFT || dest->stype == F_TRASH) MSG_UNSET_PERM_FLAGS(newmsginfo->flags, MSG_NEW|MSG_UNREAD|MSG_DELETED); msgcache_add_msg(dest->cache, newmsginfo); if (MSG_IS_NEW(newmsginfo->flags)) dest->new++; if (MSG_IS_UNREAD(newmsginfo->flags)) dest->unread++; if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo)) dest->unreadmarked++; if (procmsg_msg_has_flagged_parent(newmsginfo, MSG_IGNORE_THREAD)) procmsg_msginfo_set_flags(newmsginfo, MSG_IGNORE_THREAD, 0); dest->total++; folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT); procmsg_msginfo_free(newmsginfo); } /* remove source message from it's folder */ if (src_folder->remove_msg) { src_folder->remove_msg(src_folder, msginfo->folder, msginfo->msgnum); msgcache_remove_msg(msginfo->folder->cache, msginfo->msgnum); if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) msginfo->folder->new--; if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) msginfo->folder->unread--; if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo)) msginfo->folder->unreadmarked--; msginfo->folder->total--; folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT); } } if (folder->finished_copy) folder->finished_copy(folder, dest); return num; } /* gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist) { Folder *folder; gint num; g_return_val_if_fail(dest != NULL, -1); g_return_val_if_fail(msglist != NULL, -1); folder = dest->folder; if (dest->last_num < 0) folder->scan(folder, dest); num = folder->move_msgs_with_dest(folder, dest, msglist); if (num > 0) dest->last_num = num; else dest->op_count = 0; return num; } */ gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist) { Folder *folder; FolderItem *item; GSList *newmsgnums = NULL; GSList *l, *l2; gint num; g_return_val_if_fail(dest != NULL, -1); g_return_val_if_fail(msglist != NULL, -1); folder = dest->folder; g_return_val_if_fail(folder->copy_msg != NULL, -1); g_return_val_if_fail(folder->remove_msg != NULL, -1); /* * Copy messages to destination folder and * store new message numbers in newmsgnums */ item = NULL; for (l = msglist ; l != NULL ; l = g_slist_next(l)) { MsgInfo * msginfo = (MsgInfo *) l->data; if (!item && msginfo->folder != NULL) item = msginfo->folder; num = folder->copy_msg(folder, dest, msginfo); newmsgnums = g_slist_append(newmsgnums, GINT_TO_POINTER(num)); } /* Read cache for dest folder */ if (!dest->cache) folder_item_read_cache(dest); /* * Fetch new MsgInfos for new messages in dest folder, * add them to the msgcache and update folder message counts */ l2 = newmsgnums; for (l = msglist; l != NULL; l = g_slist_next(l)) { MsgInfo *msginfo = (MsgInfo *) l->data; num = GPOINTER_TO_INT(l2->data); if (num > 0) { MsgInfo *newmsginfo; newmsginfo = folder->get_msginfo(folder, dest, num); if (newmsginfo) { newmsginfo->flags.perm_flags = msginfo->flags.perm_flags; if (dest->stype == F_OUTBOX || dest->stype == F_QUEUE || dest->stype == F_DRAFT || dest->stype == F_TRASH) MSG_UNSET_PERM_FLAGS(newmsginfo->flags, MSG_NEW|MSG_UNREAD|MSG_DELETED); msgcache_add_msg(dest->cache, newmsginfo); if (MSG_IS_NEW(newmsginfo->flags)) dest->new++; if (MSG_IS_UNREAD(newmsginfo->flags)) dest->unread++; if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo)) dest->unreadmarked++; if (procmsg_msg_has_flagged_parent(newmsginfo, MSG_IGNORE_THREAD)) procmsg_msginfo_set_flags(newmsginfo, MSG_IGNORE_THREAD, 0); dest->total++; folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT); procmsg_msginfo_free(newmsginfo); } } l2 = g_slist_next(l2); } /* * Remove source messages from their folders if * copying was successfull and update folder * message counts */ l2 = newmsgnums; for (l = msglist; l != NULL; l = g_slist_next(l)) { MsgInfo *msginfo = (MsgInfo *) l->data; num = GPOINTER_TO_INT(l2->data); if (num > 0) { item->folder->remove_msg(item->folder, msginfo->folder, msginfo->msgnum); if (!item->cache) folder_item_read_cache(item); msgcache_remove_msg(item->cache, msginfo->msgnum); if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) msginfo->folder->new--; if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) msginfo->folder->unread--; if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo)) msginfo->folder->unreadmarked--; msginfo->folder->total--; folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT); } l2 = g_slist_next(l2); } if (folder->finished_copy) folder->finished_copy(folder, dest); g_slist_free(newmsgnums); return dest->last_num; } /* gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo) { Folder *folder; gint num; g_return_val_if_fail(dest != NULL, -1); g_return_val_if_fail(msginfo != NULL, -1); folder = dest->folder; if (dest->last_num < 0) folder->scan(folder, dest); num = folder->copy_msg(folder, dest, msginfo); if (num > 0) dest->last_num = num; return num; } */ gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo) { Folder *folder; gint num; g_return_val_if_fail(dest != NULL, -1); g_return_val_if_fail(msginfo != NULL, -1); folder = dest->folder; g_return_val_if_fail(folder->copy_msg != NULL, -1); if (!dest->cache) folder_item_read_cache(dest); num = folder->copy_msg(folder, dest, msginfo); if (num > 0) { MsgInfo *newmsginfo; if (NULL != (newmsginfo = folder->get_msginfo(folder, dest, num))) { newmsginfo->flags.perm_flags = msginfo->flags.perm_flags; if (dest->stype == F_OUTBOX || dest->stype == F_QUEUE || dest->stype == F_DRAFT || dest->stype == F_TRASH) MSG_UNSET_PERM_FLAGS(newmsginfo->flags, MSG_NEW|MSG_UNREAD|MSG_DELETED); msgcache_add_msg(dest->cache, newmsginfo); if (MSG_IS_NEW(newmsginfo->flags)) dest->new++; if (MSG_IS_UNREAD(newmsginfo->flags)) dest->unread++; if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo)) dest->unreadmarked++; if (procmsg_msg_has_flagged_parent(newmsginfo, MSG_IGNORE_THREAD)) procmsg_msginfo_set_flags(newmsginfo, MSG_IGNORE_THREAD, 0); dest->total++; folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT); procmsg_msginfo_free(newmsginfo); } } if (folder->finished_copy) folder->finished_copy(folder, dest); return num; } /* gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist) { Folder *folder; gint num; g_return_val_if_fail(dest != NULL, -1); g_return_val_if_fail(msglist != NULL, -1); folder = dest->folder; if (dest->last_num < 0) folder->scan(folder, dest); num = folder->copy_msgs_with_dest(folder, dest, msglist); if (num > 0) dest->last_num = num; else dest->op_count = 0; return num; } */ gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist) { Folder *folder; gint num; GSList *newmsgnums = NULL; GSList *l, *l2; g_return_val_if_fail(dest != NULL, -1); g_return_val_if_fail(msglist != NULL, -1); folder = dest->folder; g_return_val_if_fail(folder->copy_msg != NULL, -1); /* * Copy messages to destination folder and * store new message numbers in newmsgnums */ for (l = msglist ; l != NULL ; l = g_slist_next(l)) { MsgInfo * msginfo = (MsgInfo *) l->data; num = folder->copy_msg(folder, dest, msginfo); newmsgnums = g_slist_append(newmsgnums, GINT_TO_POINTER(num)); } /* Read cache for dest folder */ if (!dest->cache) folder_item_read_cache(dest); /* * Fetch new MsgInfos for new messages in dest folder, * add them to the msgcache and update folder message counts */ l2 = newmsgnums; for (l = msglist; l != NULL; l = g_slist_next(l)) { MsgInfo *msginfo = (MsgInfo *) l->data; num = GPOINTER_TO_INT(l2->data); if (num > 0) { MsgInfo *newmsginfo; newmsginfo = folder->get_msginfo(folder, dest, num); if (newmsginfo) { newmsginfo->flags.perm_flags = msginfo->flags.perm_flags; if (dest->stype == F_OUTBOX || dest->stype == F_QUEUE || dest->stype == F_DRAFT || dest->stype == F_TRASH) MSG_UNSET_PERM_FLAGS(newmsginfo->flags, MSG_NEW|MSG_UNREAD|MSG_DELETED); msgcache_add_msg(dest->cache, newmsginfo); if (MSG_IS_NEW(newmsginfo->flags)) dest->new++; if (MSG_IS_UNREAD(newmsginfo->flags)) dest->unread++; if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo)) dest->unreadmarked++; if (procmsg_msg_has_flagged_parent(newmsginfo, MSG_IGNORE_THREAD)) procmsg_msginfo_set_flags(newmsginfo, MSG_IGNORE_THREAD, 0); dest->total++; folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT); procmsg_msginfo_free(newmsginfo); } } l2 = g_slist_next(l2); } if (folder->finished_copy) folder->finished_copy(folder, dest); g_slist_free(newmsgnums); return dest->last_num; } gint folder_item_remove_msg(FolderItem *item, gint num) { Folder *folder; gint ret; MsgInfo *msginfo; g_return_val_if_fail(item != NULL, -1); folder = item->folder; if (!item->cache) folder_item_read_cache(item); ret = folder->remove_msg(folder, item, num); msginfo = msgcache_get_msg(item->cache, num); if (msginfo != NULL) { if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) item->new--; if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) item->unread--; if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo)) item->unreadmarked--; procmsg_msginfo_free(msginfo); msgcache_remove_msg(item->cache, num); } item->total--; folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT); return ret; } 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; g_return_val_if_fail(folder != NULL, -1); if (!item->cache) folder_item_read_cache(item); if (folder->remove_msgs) { ret = folder->remove_msgs(folder, item, msglist); if (ret == 0) folder_item_scan(item); return ret; } while (msglist != NULL) { MsgInfo *msginfo = (MsgInfo *)msglist->data; ret = folder_item_remove_msg(item, msginfo->msgnum); if (ret != 0) break; msgcache_remove_msg(item->cache, msginfo->msgnum); msglist = msglist->next; } return ret; } gint folder_item_remove_all_msg(FolderItem *item) { Folder *folder; gint result; g_return_val_if_fail(item != NULL, -1); folder = item->folder; g_return_val_if_fail(folder->remove_all_msg != NULL, -1); result = folder->remove_all_msg(folder, item); if (result == 0) { if (folder->finished_remove) folder->finished_remove(folder, item); folder_item_free_cache(item); item->cache = msgcache_new(); item->new = 0; item->unread = 0; item->unreadmarked = 0; item->total = 0; folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT); } return result; } gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo) { Folder *folder; g_return_val_if_fail(item != NULL, FALSE); folder = item->folder; g_return_val_if_fail(folder->is_msg_changed != NULL, -1); return folder->is_msg_changed(folder, item, msginfo); } 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, apply_sub = FALSE; gboolean ret_rcpt = FALSE, hidereadmsgs = FALSE, thread_collapsed = FALSE; /* CLAWS */ FolderSortKey sort_key = SORT_BY_NONE; FolderSortType sort_type = SORT_ASCENDING; gint new = 0, unread = 0, total = 0, unreadmarked = 0; time_t mtime = 0; 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, "unreadmarked")) unreadmarked = 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, "thread_collapsed")) thread_collapsed = *attr->value == '1' ? TRUE : FALSE; else if (!strcmp(attr->name, "threaded")) threaded = *attr->value == '1' ? TRUE : FALSE; else if (!strcmp(attr->name, "hidereadmsgs")) hidereadmsgs = *attr->value == '1' ? TRUE : FALSE; else if (!strcmp(attr->name, "reqretrcpt")) ret_rcpt = *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_STATUS; 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->value, "locked")) sort_key = SORT_BY_LOCKED; } 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, "apply_sub")) apply_sub = *attr->value == '1' ? TRUE : FALSE; } item = folder_item_new(folder, name, path); item->stype = stype; item->mtime = mtime; item->new = new; item->unread = unread; item->unreadmarked = unreadmarked; item->total = total; item->no_sub = no_sub; item->no_select = no_select; item->collapsed = collapsed; item->thread_collapsed = thread_collapsed; item->threaded = threaded; item->hide_read_msgs = hidereadmsgs; item->ret_rcpt = ret_rcpt; item->sort_key = sort_key; item->sort_type = sort_type; 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->apply_sub = apply_sub; prefs_folder_item_read_config(item); node->data = item; xml_free_node(xmlnode); return FALSE; } static gboolean folder_read_folder_func(GNode *node, gpointer data) { Folder *folder; XMLNode *xmlnode; GList *list; FolderType type = F_UNKNOWN; const gchar *name = NULL; const gchar *path = NULL; PrefsAccount *account = NULL; gboolean collapsed = FALSE, threaded = TRUE, apply_sub = FALSE; gboolean ret_rcpt = FALSE, thread_collapsed = FALSE; /* CLAWS */ 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, "thread_collapsed")) thread_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, "apply_sub")) apply_sub = *attr->value == '1' ? TRUE : FALSE; else if (!strcmp(attr->name, "reqretrcpt")) ret_rcpt = *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); node->data = folder->node->data; g_node_destroy(folder->node); folder->node = node; folder_add(folder); FOLDER_ITEM(node->data)->collapsed = collapsed; FOLDER_ITEM(node->data)->thread_collapsed = thread_collapsed; FOLDER_ITEM(node->data)->threaded = threaded; FOLDER_ITEM(node->data)->account = account; FOLDER_ITEM(node->data)->apply_sub = apply_sub; FOLDER_ITEM(node->data)->ret_rcpt = ret_rcpt; 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", "locked"}; 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, "type]); if (folder->name) PUT_ESCAPE_STR(fp, "name", folder->name); if (folder->type == F_MH || folder->type == F_MBOX) 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->apply_sub) fputs(" apply_sub=\"1\"", fp); if (item->ret_rcpt) fputs(" reqretrcpt=\"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); else fputs(" collapsed=\"0\"", fp); if (item->thread_collapsed) fputs(" thread_collapsed=\"1\"", fp); else fputs(" thread_collapsed=\"0\"", fp); if (item->threaded) fputs(" threaded=\"1\"", fp); else fputs(" threaded=\"0\"", fp); if (item->hide_read_msgs) fputs(" hidereadmsgs=\"1\"", fp); else fputs(" hidereadmsgs=\"0\"", fp); if (item->ret_rcpt) fputs(" reqretrcpt=\"1\"", 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\" unreadmarked=\"%d\" total=\"%d\"", item->mtime, item->new, item->unread, item->unreadmarked, item->total); if (item->account) fprintf(fp, " account_id=\"%d\"", item->account->account_id); if (item->apply_sub) fputs(" apply_sub=\"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); } static void folder_update_op_count_rec(GNode *node) { FolderItem *fitem = FOLDER_ITEM(node->data); if (g_node_depth(node) > 0) { if (fitem->op_count > 0) { fitem->op_count = 0; folder_item_update(fitem, F_ITEM_UPDATE_MSGCNT); } if (node->children) { GNode *child; child = node->children; while (child) { GNode *cur; cur = child; child = cur->next; folder_update_op_count_rec(cur); } } } } void folder_update_op_count() { GList *cur; Folder *folder; for (cur = folder_list; cur != NULL; cur = cur->next) { folder = cur->data; folder_update_op_count_rec(folder->node); } } typedef struct _type_str { gchar * str; gint type; } type_str; /* static gchar * folder_item_get_tree_identifier(FolderItem * item) { if (item->parent != NULL) { gchar * path; gchar * id; path = folder_item_get_tree_identifier(item->parent); if (path == NULL) return NULL; id = g_strconcat(path, "/", item->name, NULL); g_free(path); return id; } else { return g_strconcat("/", item->name, NULL); } } */ /* CLAWS: temporary local folder for filtering */ static Folder *processing_folder; static FolderItem *processing_folder_item; static void folder_create_processing_folder(void) { #define PROCESSING_FOLDER ".processing" Folder *tmpparent; gchar *tmpname; tmpparent = folder_get_default_folder(); g_assert(tmpparent); debug_print("tmpparentroot %s\n", LOCAL_FOLDER(tmpparent)->rootpath); if (LOCAL_FOLDER(tmpparent)->rootpath[0] == '/') tmpname = g_strconcat(LOCAL_FOLDER(tmpparent)->rootpath, G_DIR_SEPARATOR_S, PROCESSING_FOLDER, NULL); else tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, LOCAL_FOLDER(tmpparent)->rootpath, G_DIR_SEPARATOR_S, PROCESSING_FOLDER, NULL); processing_folder = folder_new(F_MH, "PROCESSING", LOCAL_FOLDER(tmpparent)->rootpath); g_assert(processing_folder); if (!is_dir_exist(tmpname)) { debug_print("*TMP* creating %s\n", tmpname); processing_folder_item = processing_folder->create_folder(processing_folder, processing_folder->node->data, PROCESSING_FOLDER); g_assert(processing_folder_item); } else { debug_print("*TMP* already created\n"); processing_folder_item = folder_item_new(processing_folder, ".processing", ".processing"); g_assert(processing_folder_item); folder_item_append(processing_folder->node->data, processing_folder_item); } g_free(tmpname); } FolderItem *folder_get_default_processing(void) { if (!processing_folder_item) { folder_create_processing_folder(); } return processing_folder_item; } /* folder_persist_prefs_new() - return hash table with persistent * settings (and folder name as key). * (note that in claws other options are in the PREFS_FOLDER_ITEM_RC * file, so those don't need to be included in PersistPref yet) */ GHashTable *folder_persist_prefs_new(Folder *folder) { GHashTable *pptable; g_return_val_if_fail(folder, NULL); pptable = g_hash_table_new(g_str_hash, g_str_equal); folder_get_persist_prefs_recursive(folder->node, pptable); return pptable; } void folder_persist_prefs_free(GHashTable *pptable) { g_return_if_fail(pptable); g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL); g_hash_table_destroy(pptable); } const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name) { if (pptable == NULL || name == NULL) return NULL; return g_hash_table_lookup(pptable, name); } void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable) { const PersistPrefs *pp; gchar *id = folder_item_get_identifier(item); pp = folder_get_persist_prefs(pptable, id); g_free(id); if (!pp) return; /* CLAWS: since not all folder properties have been migrated to * folderlist.xml, we need to call the old stuff first before * setting things that apply both to Main and Claws. */ prefs_folder_item_read_config(item); item->collapsed = pp->collapsed; item->thread_collapsed = pp->thread_collapsed; item->threaded = pp->threaded; item->ret_rcpt = pp->ret_rcpt; item->hide_read_msgs = pp->hide_read_msgs; item->sort_key = pp->sort_key; item->sort_type = pp->sort_type; } static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable) { FolderItem *item = FOLDER_ITEM(node->data); PersistPrefs *pp; GNode *child, *cur; gchar *id; g_return_if_fail(node != NULL); g_return_if_fail(item != NULL); /* NOTE: item->path == NULL means top level folder; not interesting * to store preferences of that one. */ if (item->path) { id = folder_item_get_identifier(item); pp = g_new0(PersistPrefs, 1); g_return_if_fail(pp != NULL); pp->collapsed = item->collapsed; pp->thread_collapsed = item->thread_collapsed; pp->threaded = item->threaded; pp->ret_rcpt = item->ret_rcpt; pp->hide_read_msgs = item->hide_read_msgs; pp->sort_key = item->sort_key; pp->sort_type = item->sort_type; g_hash_table_insert(pptable, id, pp); } if (node->children) { child = node->children; while (child) { cur = child; child = cur->next; folder_get_persist_prefs_recursive(cur, pptable); } } } static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data) { if (key) g_free(key); if (val) g_free(val); return TRUE; } void folder_item_apply_processing(FolderItem *item) { GSList *processing_list; GSList *mlist, *cur; g_return_if_fail(item != NULL); processing_list = item->prefs->processing; if (processing_list == NULL) return; folder_item_update_freeze(); mlist = folder_item_get_msg_list(item); for (cur = mlist ; cur != NULL ; cur = cur->next) { MsgInfo * msginfo; msginfo = (MsgInfo *) cur->data; filter_message_by_msginfo(processing_list, msginfo); procmsg_msginfo_free(msginfo); } g_slist_free(mlist); folder_item_update_thaw(); } /* * functions for handling FolderItem content changes */ static gint folder_item_update_freeze_cnt = 0; /** * Notify the folder system about changes to a folder. If the * update system is not frozen the FOLDER_ITEM_UPDATE_HOOKLIST will * be invoked, otherwise the changes will be remebered until * the folder system is thawed. * * \param item The FolderItem that was changed * \param update_flags Type of changed that was made */ void folder_item_update(FolderItem *item, FolderItemUpdateFlags update_flags) { if (folder_item_update_freeze_cnt == 0) { FolderItemUpdateData source; source.item = item; source.update_flags = update_flags; hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source); } else { item->update_flags |= update_flags; } } void folder_item_update_recursive(FolderItem *item, FolderItemUpdateFlags update_flags) { GNode *node = item->folder->node; node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item); node = node->children; folder_item_update(item, update_flags); while (node != NULL) { if (node && node->data) { FolderItem *next_item = (FolderItem*) node->data; folder_item_update(next_item, update_flags); } node = node->next; } } void folder_item_update_freeze() { folder_item_update_freeze_cnt++; } static void folder_item_update_func(FolderItem *item, gpointer data) { FolderItemUpdateData source; if (item->update_flags) { source.item = item; source.update_flags = item->update_flags; hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source); item->update_flags = 0; } } void folder_item_update_thaw() { if (folder_item_update_freeze_cnt > 0) folder_item_update_freeze_cnt--; if (folder_item_update_freeze_cnt == 0) { /* Update all folders */ folder_func_to_all_folders(folder_item_update_func, NULL); } } #undef PUT_ESCAPE_STR