From 0b3cbf57875fd692e4ba0b336fefa4bee1ed00dc Mon Sep 17 00:00:00 2001 From: Simeon Simeonov Date: Mon, 26 Feb 2018 11:23:00 +0100 Subject: Initial commit for sylpheed 3.7.0 --- libsylph/virtual.c | 603 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 603 insertions(+) create mode 100644 libsylph/virtual.c (limited to 'libsylph/virtual.c') diff --git a/libsylph/virtual.c b/libsylph/virtual.c new file mode 100644 index 0000000..ead73ee --- /dev/null +++ b/libsylph/virtual.c @@ -0,0 +1,603 @@ +/* + * LibSylph -- E-Mail client library + * Copyright (C) 1999-2010 Hiroyuki Yamamoto + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef MEASURE_TIME + +#include "folder.h" +#include "virtual.h" +#include "mh.h" +#include "procmsg.h" +#include "procheader.h" +#include "filter.h" +#include "utils.h" + +typedef struct _VirtualSearchInfo VirtualSearchInfo; +typedef struct _SearchCacheInfo SearchCacheInfo; + +struct _VirtualSearchInfo { + FilterRule *rule; + GSList *mlist; + GHashTable *search_cache_table; + FILE *fp; + gboolean requires_full_headers; + gboolean exclude_trash; +}; + +struct _SearchCacheInfo { + FolderItem *folder; + guint msgnum; + off_t size; + time_t mtime; + MsgFlags flags; +}; + +enum +{ + SCACHE_NOT_EXIST = 0, + SCACHE_MATCHED = 1, + SCACHE_NOT_MATCHED = 2 +}; + +static void virtual_folder_init (Folder *folder, + const gchar *name, + const gchar *path); + +static GHashTable *virtual_read_search_cache + (FolderItem *item); +static void virtual_write_search_cache (FILE *fp, + FolderItem *item, + MsgInfo *msginfo, + gint matched); + +static GSList *virtual_search_folder (VirtualSearchInfo *info, + FolderItem *item); +static gboolean virtual_search_recursive_func + (GNode *node, + gpointer data); + +static Folder *virtual_folder_new (const gchar *name, + const gchar *path); +static void virtual_folder_destroy (Folder *folder); + +static GSList *virtual_get_msg_list (Folder *folder, + FolderItem *item, + gboolean use_cache); +static gchar *virtual_fetch_msg (Folder *folder, + FolderItem *item, + gint num); +static MsgInfo *virtual_get_msginfo (Folder *folder, + FolderItem *item, + gint num); +static gint virtual_close (Folder *folder, + FolderItem *item); + +static gint virtual_scan_folder (Folder *folder, + FolderItem *item); + +static gint virtual_rename_folder (Folder *folder, + FolderItem *item, + const gchar *name); +static gint virtual_move_folder (Folder *folder, + FolderItem *item, + FolderItem *new_parent); +static gint virtual_remove_folder (Folder *folder, + FolderItem *item); + +static FolderClass virtual_class = +{ + F_VIRTUAL, + + virtual_folder_new, + virtual_folder_destroy, + + NULL, + NULL, + + virtual_get_msg_list, + NULL, + virtual_fetch_msg, + virtual_get_msginfo, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + virtual_close, + virtual_scan_folder, + + NULL, + virtual_rename_folder, + virtual_move_folder, + virtual_remove_folder, +}; + + +FolderClass *virtual_get_class(void) +{ + return &virtual_class; +} + +static Folder *virtual_folder_new(const gchar *name, const gchar *path) +{ + Folder *folder; + + folder = (Folder *)g_new0(VirtualFolder, 1); + virtual_folder_init(folder, name, path); + + return folder; +} + +static void virtual_folder_destroy(Folder *folder) +{ + folder_local_folder_destroy(LOCAL_FOLDER(folder)); +} + +static void virtual_folder_init(Folder *folder, const gchar *name, + const gchar *path) +{ + folder->klass = virtual_get_class(); + folder_local_folder_init(folder, name, path); +} + +guint sinfo_hash(gconstpointer key) +{ + const SearchCacheInfo *sinfo = key; + guint h; + + h = (guint)sinfo->folder; + h ^= sinfo->msgnum; + h ^= (guint)sinfo->size; + h ^= (guint)sinfo->mtime; + /* h ^= (guint)sinfo->flags.tmp_flags; */ + h ^= (guint)sinfo->flags.perm_flags; + + /* g_print("path: %s, n = %u, hash = %u\n", + sinfo->folder->path, sinfo->msgnum, h); */ + + return h; +} + +gint sinfo_equal(gconstpointer v, gconstpointer v2) +{ + const SearchCacheInfo *s1 = v; + const SearchCacheInfo *s2 = v2; + + return (s1->folder == s2->folder && s1->msgnum == s2->msgnum && + s1->size == s2->size && s1->mtime == s2->mtime && + /* s1->flags.tmp_flags == s2->flags.tmp_flags && */ + s1->flags.perm_flags == s2->flags.perm_flags); +} + +#define READ_CACHE_DATA_INT(n, fp) \ +{ \ + guint32 idata; \ + \ + if (fread(&idata, sizeof(idata), 1, fp) != 1) { \ + g_warning("Cache data is corrupted\n"); \ + fclose(fp); \ + return table; \ + } else \ + n = idata; \ +} + +static GHashTable *virtual_read_search_cache(FolderItem *item) +{ + GHashTable *table; + gchar *path, *file; + FILE *fp; + gchar *id; + gint count = 0; + + g_return_val_if_fail(item != NULL, NULL); + + path = folder_item_get_path(item); + file = g_strconcat(path, G_DIR_SEPARATOR_S, SEARCH_CACHE, NULL); + debug_print("reading search cache: %s\n", file); + fp = procmsg_open_data_file(file, SEARCH_CACHE_VERSION, DATA_READ, + NULL, 0); + g_free(file); + g_free(path); + if (!fp) + return NULL; + + table = g_hash_table_new(sinfo_hash, sinfo_equal); + + while (procmsg_read_cache_data_str(fp, &id) == 0) { + FolderItem *folder; + guint32 msgnum; + off_t size; + time_t mtime; + MsgFlags flags; + gint matched; + SearchCacheInfo *sinfo; + + folder = folder_find_item_from_identifier(id); + g_free(id); + + while (fread(&msgnum, sizeof(msgnum), 1, fp) == 1) { + if (msgnum == 0) + break; + + READ_CACHE_DATA_INT(size, fp); + READ_CACHE_DATA_INT(mtime, fp); + READ_CACHE_DATA_INT(flags.tmp_flags, fp); + READ_CACHE_DATA_INT(flags.perm_flags, fp); + READ_CACHE_DATA_INT(matched, fp); + + if (folder) { + sinfo = g_new(SearchCacheInfo, 1); + sinfo->folder = folder; + sinfo->msgnum = msgnum; + sinfo->size = size; + sinfo->mtime = mtime; + sinfo->flags = flags; + g_hash_table_insert(table, sinfo, + GINT_TO_POINTER(matched)); + ++count; + } + } + } + + debug_print("%d cache items read.\n", count); + + fclose(fp); + return table; +} + +static void virtual_write_search_cache(FILE *fp, FolderItem *item, + MsgInfo *msginfo, gint matched) +{ + if (!item && !msginfo) { + WRITE_CACHE_DATA_INT(0, fp); + return; + } + + if (item) { + gchar *id; + + id = folder_item_get_identifier(item); + if (id) { + WRITE_CACHE_DATA(id, fp); + g_free(id); + } + } + + if (msginfo) { + WRITE_CACHE_DATA_INT(msginfo->msgnum, fp); + WRITE_CACHE_DATA_INT(msginfo->size, fp); + WRITE_CACHE_DATA_INT(msginfo->mtime, fp); + WRITE_CACHE_DATA_INT + ((msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK), fp); + WRITE_CACHE_DATA_INT(msginfo->flags.perm_flags, fp); + WRITE_CACHE_DATA_INT(matched, fp); + } +} + +static void search_cache_free_func(gpointer key, gpointer value, gpointer data) +{ + g_free(key); +} + +static void virtual_search_cache_free(GHashTable *table) +{ + if (table) { + g_hash_table_foreach(table, search_cache_free_func, NULL); + g_hash_table_destroy(table); + } +} + +static GSList *virtual_search_folder(VirtualSearchInfo *info, FolderItem *item) +{ + GSList *match_list = NULL; + GSList *mlist; + GSList *cur; + FilterInfo fltinfo; + gint count = 1, total, ncachehit = 0; + GTimeVal tv_prev, tv_cur; + + g_return_val_if_fail(info != NULL, NULL); + g_return_val_if_fail(info->rule != NULL, NULL); + g_return_val_if_fail(item != NULL, NULL); + g_return_val_if_fail(item->path != NULL, NULL); + + /* prevent circular reference */ + if (item->stype == F_VIRTUAL) + return NULL; + + g_get_current_time(&tv_prev); + status_print(_("Searching %s ..."), item->path); + + mlist = folder_item_get_msg_list(item, TRUE); + total = g_slist_length(mlist); + + memset(&fltinfo, 0, sizeof(FilterInfo)); + + debug_print("start query search: %s\n", item->path); + + virtual_write_search_cache(info->fp, item, NULL, 0); + + for (cur = mlist; cur != NULL; cur = cur->next) { + MsgInfo *msginfo = (MsgInfo *)cur->data; + GSList *hlist; + + g_get_current_time(&tv_cur); + if (tv_cur.tv_sec > tv_prev.tv_sec || + tv_cur.tv_usec - tv_prev.tv_usec > + PROGRESS_UPDATE_INTERVAL * 1000) { + status_print(_("Searching %s (%d / %d)..."), + item->path, count, total); + tv_prev = tv_cur; + } + ++count; + + if (info->search_cache_table) { + gint matched; + SearchCacheInfo sinfo; + + sinfo.folder = item; + sinfo.msgnum = msginfo->msgnum; + sinfo.size = msginfo->size; + sinfo.mtime = msginfo->mtime; + sinfo.flags = msginfo->flags; + + matched = (gint)g_hash_table_lookup + (info->search_cache_table, &sinfo); + if (matched == SCACHE_MATCHED) { + match_list = g_slist_prepend + (match_list, msginfo); + cur->data = NULL; + virtual_write_search_cache(info->fp, NULL, + msginfo, matched); + ++ncachehit; + continue; + } else if (matched == SCACHE_NOT_MATCHED) { + virtual_write_search_cache(info->fp, NULL, + msginfo, matched); + ++ncachehit; + continue; + } + } + + fltinfo.flags = msginfo->flags; + if (info->requires_full_headers) { + gchar *file; + + file = procmsg_get_message_file(msginfo); + hlist = procheader_get_header_list_from_file(file); + g_free(file); + } else + hlist = procheader_get_header_list_from_msginfo + (msginfo); + if (!hlist) + continue; + + if (filter_match_rule(info->rule, msginfo, hlist, &fltinfo)) { + match_list = g_slist_prepend(match_list, msginfo); + cur->data = NULL; + virtual_write_search_cache(info->fp, NULL, msginfo, + SCACHE_MATCHED); + } else { + virtual_write_search_cache(info->fp, NULL, msginfo, + SCACHE_NOT_MATCHED); + } + + procheader_header_list_destroy(hlist); + } + + debug_print("%d cache hits (%d total)\n", ncachehit, total); + + virtual_write_search_cache(info->fp, NULL, NULL, 0); + procmsg_msg_list_free(mlist); + + return g_slist_reverse(match_list); +} + +static gboolean virtual_search_recursive_func(GNode *node, gpointer data) +{ + VirtualSearchInfo *info = (VirtualSearchInfo *)data; + FolderItem *item; + GSList *mlist; + + g_return_val_if_fail(node->data != NULL, FALSE); + + item = FOLDER_ITEM(node->data); + + if (!item->path) + return FALSE; + if (info->exclude_trash && item->stype == F_TRASH) + return FALSE; + + mlist = virtual_search_folder(info, item); + info->mlist = g_slist_concat(info->mlist, mlist); + + return FALSE; +} + +static GSList *virtual_get_msg_list(Folder *folder, FolderItem *item, + gboolean use_cache) +{ + GSList *mlist = NULL; + GSList *flist; + GSList *cur; + FilterRule *rule; + gchar *path; + gchar *rule_file; + gchar *cache_file; + FolderItem *target; + gint new = 0, unread = 0, total = 0; + VirtualSearchInfo info; + + g_return_val_if_fail(item != NULL, NULL); + g_return_val_if_fail(item->stype == F_VIRTUAL, NULL); + + path = folder_item_get_path(item); + rule_file = g_strconcat(path, G_DIR_SEPARATOR_S, "filter.xml", NULL); + flist = filter_read_file(rule_file); + g_free(rule_file); + + g_free(path); + + if (!flist) { + g_warning("filter rule not found\n"); + return NULL; + } + + rule = (FilterRule *)flist->data; + target = folder_find_item_from_identifier(rule->target_folder); + + if (!target || target == item) { + g_warning("invalid target folder\n"); + goto finish; + } + + info.rule = rule; + info.mlist = NULL; + if (use_cache) + info.search_cache_table = virtual_read_search_cache(item); + else + info.search_cache_table = NULL; + + path = folder_item_get_path(item); + cache_file = g_strconcat(path, G_DIR_SEPARATOR_S, SEARCH_CACHE, NULL); + info.fp = procmsg_open_data_file(cache_file, SEARCH_CACHE_VERSION, + DATA_WRITE, NULL, 0); + g_free(cache_file); + g_free(path); + if (!info.fp) + goto finish; + + info.requires_full_headers = + filter_rule_requires_full_headers(rule); + + if (rule->recursive) { + if (target->stype == F_TRASH) + info.exclude_trash = FALSE; + else + info.exclude_trash = TRUE; + } else + info.exclude_trash = FALSE; + + if (rule->recursive) { + g_node_traverse(target->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + virtual_search_recursive_func, &info); + mlist = info.mlist; + } else + mlist = virtual_search_folder(&info, target); + + fclose(info.fp); + virtual_search_cache_free(info.search_cache_table); + + for (cur = mlist; cur != NULL; cur = cur->next) { + MsgInfo *msginfo = (MsgInfo *)cur->data; + + if (MSG_IS_NEW(msginfo->flags)) + ++new; + if (MSG_IS_UNREAD(msginfo->flags)) + ++unread; + ++total; + } + + item->new = new; + item->unread = unread; + item->total = total; + item->updated = TRUE; + +finish: + filter_rule_list_free(flist); + return mlist; +} + +static gchar *virtual_fetch_msg(Folder *folder, FolderItem *item, gint num) +{ + return NULL; +} + +static MsgInfo *virtual_get_msginfo(Folder *folder, FolderItem *item, gint num) +{ + return NULL; +} + +static gint virtual_close(Folder *folder, FolderItem *item) +{ + return 0; +} + +static gint virtual_scan_folder(Folder *folder, FolderItem *item) +{ + return 0; +} + +static gint virtual_rename_folder(Folder *folder, FolderItem *item, + const gchar *name) +{ + g_return_val_if_fail(item != NULL, -1); + g_return_val_if_fail(item->stype == F_VIRTUAL, -1); + + return mh_get_class()->rename_folder(folder, item, name); +} + +static gint virtual_move_folder(Folder *folder, FolderItem *item, + FolderItem *new_parent) +{ + g_return_val_if_fail(item != NULL, -1); + g_return_val_if_fail(item->stype == F_VIRTUAL, -1); + + return mh_get_class()->move_folder(folder, item, new_parent); +} + +static gint virtual_remove_folder(Folder *folder, FolderItem *item) +{ + gchar *path; + + g_return_val_if_fail(item != NULL, -1); + g_return_val_if_fail(item->stype == F_VIRTUAL, -1); + + path = folder_item_get_path(item); + if (remove_dir_recursive(path) < 0) { + g_warning("can't remove directory '%s'\n", path); + g_free(path); + return -1; + } + + g_free(path); + folder_item_remove(item); + return 0; +} -- cgit v1.3