From 76d8bbcdc47c6cf2c343d21a694110026ecab98d Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Wed, 30 May 2012 18:45:01 +0200 Subject: [PATCH] Simplify vFolder's code and other bunch of related time optimizations --- camel/Makefile.am | 2 + camel/camel-db.c | 209 --- camel/camel-db.h | 9 - camel/camel-folder-search.c | 70 +- camel/camel-folder-summary.c | 279 +++- camel/camel-folder-summary.h | 17 +- camel/camel-folder.c | 4 + camel/camel-imapx-server.c | 2 +- camel/camel-store.c | 13 +- camel/camel-string-utils.c | 79 +- camel/camel-string-utils.h | 1 + camel/camel-subscribable.c | 4 +- camel/camel-vee-data-cache.c | 638 ++++++++ camel/camel-vee-data-cache.h | 177 ++ camel/camel-vee-folder.c | 2597 ++++++++++-------------------- camel/camel-vee-folder.h | 92 +- camel/camel-vee-store.c | 532 +++++- camel/camel-vee-store.h | 40 +- camel/camel-vee-summary.c | 293 +++- camel/camel-vee-summary.h | 18 +- camel/camel-vtrash-folder.c | 4 +- camel/camel.h | 1 + camel/providers/imap/camel-imap-folder.c | 2 +- 23 files changed, 2824 insertions(+), 2259 deletions(-) create mode 100644 camel/camel-vee-data-cache.c create mode 100644 camel/camel-vee-data-cache.h diff --git a/camel/Makefile.am b/camel/Makefile.am index e9ceed4..d1652dc 100644 --- a/camel/Makefile.am +++ b/camel/Makefile.am @@ -168,6 +168,7 @@ libcamel_1_2_la_SOURCES = \ camel-url-scanner.c \ camel-url.c \ camel-utf8.c \ + camel-vee-data-cache.c \ camel-vee-folder.c \ camel-vee-store.c \ camel-vee-summary.c \ @@ -302,6 +303,7 @@ libcamelinclude_HEADERS = \ camel-url-scanner.h \ camel-url.h \ camel-utf8.h \ + camel-vee-data-cache.h \ camel-vee-folder.h \ camel-vee-store.h \ camel-vee-summary.h \ diff --git a/camel/camel-db.c b/camel/camel-db.c index 8fb123e..c8ef256 100644 --- a/camel/camel-db.c +++ b/camel/camel-db.c @@ -994,103 +994,6 @@ camel_db_select (CamelDB *cdb, return ret; } -/** - * camel_db_create_vfolder: - * - * Since: 2.24 - **/ -gint -camel_db_create_vfolder (CamelDB *db, - const gchar *folder_name, - GError **error) -{ - gint ret; - gchar *table_creation_query, *safe_index; - - table_creation_query = sqlite3_mprintf ("CREATE TABLE IF NOT EXISTS %Q ( vuid TEXT PRIMARY KEY)", folder_name); - - ret = camel_db_command (db, table_creation_query, error); - - sqlite3_free (table_creation_query); - - safe_index = g_strdup_printf("VINDEX-%s", folder_name); - table_creation_query = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS %Q ON %Q (vuid)", safe_index, folder_name); - ret = camel_db_command (db, table_creation_query, error); - - sqlite3_free (table_creation_query); - g_free (safe_index); - CAMEL_DB_RELEASE_SQLITE_MEMORY; - - return ret; -} - -/** - * camel_db_recreate_vfolder: - * - * Since: 2.24 - **/ -gint -camel_db_recreate_vfolder (CamelDB *db, - const gchar *folder_name, - GError **error) -{ - gchar *table_query; - - table_query = sqlite3_mprintf ("DROP TABLE %Q", folder_name); - - camel_db_command (db, table_query, error); - - sqlite3_free (table_query); - - return camel_db_create_vfolder (db, folder_name, error); -} - -/** - * camel_db_delete_uid_from_vfolder: - * - * Since: 2.24 - **/ -gint -camel_db_delete_uid_from_vfolder (CamelDB *db, - gchar *folder_name, - gchar *vuid, - GError **error) -{ - gchar *del_query; - gint ret; - - del_query = sqlite3_mprintf ("DELETE FROM %Q WHERE vuid = %Q", folder_name, vuid); - - ret = camel_db_command (db, del_query, error); - - sqlite3_free (del_query); - CAMEL_DB_RELEASE_SQLITE_MEMORY; - return ret; -} - -/** - * camel_db_delete_uid_from_vfolder_transaction: - * - * Since: 2.24 - **/ -gint -camel_db_delete_uid_from_vfolder_transaction (CamelDB *db, - const gchar *folder_name, - const gchar *vuid, - GError **error) -{ - gchar *del_query; - gint ret; - - del_query = sqlite3_mprintf ("DELETE FROM %Q WHERE vuid = %Q", folder_name, vuid); - - ret = camel_db_add_to_transaction (db, del_query, error); - - sqlite3_free (del_query); - - return ret; -} - static gint read_uids_callback (gpointer ref_array, gint ncol, @@ -1299,103 +1202,6 @@ camel_db_write_preview_record (CamelDB *db, return ret; } -static gint -read_vuids_callback (gpointer ref, - gint ncol, - gchar **cols, - gchar **name) -{ - GPtrArray *array = (GPtrArray *) ref; - - if (cols[0] && strlen (cols[0]) > 8) - g_ptr_array_add (array, (gchar *) (camel_pstring_strdup (cols[0]+8))); - - return 0; -} - -/** - * camel_db_get_vuids_from_vfolder: - * - * Since: 2.24 - **/ -GPtrArray * -camel_db_get_vuids_from_vfolder (CamelDB *db, - const gchar *folder_name, - gchar *filter, - GError **error) -{ - gchar *sel_query; - gchar *cond = NULL; - GPtrArray *array; - gchar *tmp = g_strdup_printf("%s%%", filter ? filter:""); - if (filter) - cond = sqlite3_mprintf(" WHERE vuid LIKE %Q", tmp); - g_free (tmp); - sel_query = sqlite3_mprintf("SELECT vuid FROM %Q%s", folder_name, filter ? cond : ""); - - if (cond) - sqlite3_free (cond); - /* FIXME[disk-summary] handle return values */ - /* FIXME[disk-summary] No The caller should parse the ex in case - * of NULL returns */ - array = g_ptr_array_new (); - camel_db_select (db, sel_query, read_vuids_callback, array, error); - sqlite3_free (sel_query); - /* We make sure to return NULL if we don't get anything. Be good to your caller */ - if (!array->len) { - g_ptr_array_free (array, TRUE); - array = NULL; - } - - return array; -} - -/** - * camel_db_add_to_vfolder: - * - * Since: 2.24 - **/ -gint -camel_db_add_to_vfolder (CamelDB *db, - gchar *folder_name, - gchar *vuid, - GError **error) -{ - gchar *ins_query; - gint ret; - - ins_query = sqlite3_mprintf ("INSERT INTO %Q VALUES (%Q)", folder_name, vuid); - - ret = camel_db_command (db, ins_query, error); - - sqlite3_free (ins_query); - CAMEL_DB_RELEASE_SQLITE_MEMORY; - return ret; -} - -/** - * camel_db_add_to_vfolder_transaction: - * - * Since: 2.24 - **/ -gint -camel_db_add_to_vfolder_transaction (CamelDB *db, - const gchar *folder_name, - const gchar *vuid, - GError **error) -{ - gchar *ins_query; - gint ret; - - ins_query = sqlite3_mprintf ("INSERT INTO %Q VALUES (%Q)", folder_name, vuid); - - ret = camel_db_add_to_transaction (db, ins_query, error); - - sqlite3_free (ins_query); - - return ret; -} - /** * camel_db_create_folders_table: * @@ -2123,21 +1929,6 @@ camel_db_delete_uids (CamelDB *cdb, } /** - * camel_db_delete_vuids: - * - * Since: 2.26 - **/ -gint -camel_db_delete_vuids (CamelDB *cdb, - const gchar *folder_name, - const gchar *hash, - GList *uids, - GError **error) -{ - return cdb_delete_ids (cdb, folder_name, uids, hash, "vuid", error); -} - -/** * camel_db_clear_folder_summary: * * Since: 2.24 diff --git a/camel/camel-db.h b/camel/camel-db.h index 33be066..deb9a11 100644 --- a/camel/camel-db.h +++ b/camel/camel-db.h @@ -273,7 +273,6 @@ gint camel_db_delete_folder (CamelDB *cdb, const gchar *folder, GError **error); gint camel_db_delete_uid (CamelDB *cdb, const gchar *folder, const gchar *uid, GError **error); /*int camel_db_delete_uids (CamelDB *cdb, GError **error, gint nargs, ... );*/ gint camel_db_delete_uids (CamelDB *cdb, const gchar * folder_name, GList *uids, GError **error); -gint camel_db_delete_vuids (CamelDB *cdb, const gchar * folder_name, const gchar *shash, GList *uids, GError **error); gint camel_db_create_folders_table (CamelDB *cdb, GError **error); gint camel_db_select (CamelDB *cdb, const gchar * stmt, CamelDBSelectCB callback, gpointer data, GError **error); @@ -300,14 +299,6 @@ gint camel_db_count_junk_not_deleted_message_info (CamelDB *cdb, const gchar *ta gint camel_db_count_message_info (CamelDB *cdb, const gchar *query, guint32 *count, GError **error); void camel_db_camel_mir_free (CamelMIRecord *record); -gint camel_db_create_vfolder (CamelDB *db, const gchar *folder_name, GError **error); -gint camel_db_recreate_vfolder (CamelDB *db, const gchar *folder_name, GError **error); -gint camel_db_delete_uid_from_vfolder (CamelDB *db, gchar *folder_name, gchar *vuid, GError **error); -gint camel_db_delete_uid_from_vfolder_transaction (CamelDB *db, const gchar *folder_name, const gchar *vuid, GError **error); -GPtrArray * camel_db_get_vuids_from_vfolder (CamelDB *db, const gchar *folder_name, gchar *filter, GError **error); -gint camel_db_add_to_vfolder (CamelDB *db, gchar *folder_name, gchar *vuid, GError **error); -gint camel_db_add_to_vfolder_transaction (CamelDB *db, const gchar *folder_name, const gchar *vuid, GError **error); - gint camel_db_get_folder_uids (CamelDB *db, const gchar *folder_name, const gchar *sort_by, const gchar *collate, GHashTable *hash, GError **error); GPtrArray * camel_db_get_folder_junk_uids (CamelDB *db, gchar *folder_name, GError **error); diff --git a/camel/camel-folder-search.c b/camel/camel-folder-search.c index c72acc8..ab96996 100644 --- a/camel/camel-folder-search.c +++ b/camel/camel-folder-search.c @@ -322,6 +322,40 @@ camel_folder_search_set_body_index (CamelFolderSearch *search, search->body_index = body_index; } +static gboolean +do_search_in_memory (CamelFolder *search_in_folder, + const gchar *expr) +{ + /* if the expression contains any of these tokens, then perform a memory search, instead of the SQL one */ + const gchar *in_memory_tokens[] = { + "body-contains", + "body-regex", + "match-threads", + "message-location", + "header-soundex", + "header-regex", + "header-full-regex", + "header-contains", + "header-has-words", + NULL }; + gint i; + + if (search_in_folder && + search_in_folder->summary && + (search_in_folder->summary->flags & CAMEL_FOLDER_SUMMARY_IN_MEMORY_ONLY) != 0) + return TRUE; + + if (!expr) + return FALSE; + + for (i = 0; in_memory_tokens[i]; i++) { + if (strstr (expr, in_memory_tokens[i])) + return TRUE; + } + + return FALSE; +} + /** * camel_folder_search_count: * @search: @@ -363,9 +397,11 @@ camel_folder_search_count (CamelFolderSearch *search, p->error = error; /* We route body-contains search and thread based search through memory and not via db. */ - if (strstr((const gchar *) expr, "body-contains") || strstr((const gchar *) expr, "match-threads")) { + if (do_search_in_memory (search->folder, expr)) { /* setup our search list only contains those we're interested in */ search->summary = camel_folder_get_summary (search->folder); + if (search->folder->summary) + camel_folder_summary_prepare_fetch_all (search->folder->summary, NULL); summary_set = search->summary; @@ -474,34 +510,6 @@ fail: return count; } -static gboolean -do_search_in_memory (const gchar *expr) -{ - /* if the expression contains any of these tokens, then perform a memory search, instead of the SQL one */ - const gchar *in_memory_tokens[] = { - "body-contains", - "body-regex", - "match-threads", - "message-location", - "header-soundex", - "header-regex", - "header-full-regex", - "header-contains", - "header-has-words", - NULL }; - gint i; - - if (!expr) - return FALSE; - - for (i = 0; in_memory_tokens[i]; i++) { - if (strstr (expr, in_memory_tokens[i])) - return TRUE; - } - - return FALSE; -} - /** * camel_folder_search_search: * @search: @@ -541,7 +549,7 @@ camel_folder_search_search (CamelFolderSearch *search, p->error = error; /* We route body-contains / thread based search and uid search through memory and not via db. */ - if (uids || do_search_in_memory (expr)) { + if (uids || do_search_in_memory (search->folder, expr)) { /* setup our search list only contains those we're interested in */ search->summary = camel_folder_get_summary (search->folder); @@ -556,6 +564,8 @@ camel_folder_search_search (CamelFolderSearch *search, g_ptr_array_add (search->summary_set, search->summary->pdata[i]); g_hash_table_destroy (uids_hash); } else { + if (search->folder->summary) + camel_folder_summary_prepare_fetch_all (search->folder->summary, NULL); summary_set = search->summary; } diff --git a/camel/camel-folder-summary.c b/camel/camel-folder-summary.c index 8dad22c..a80ca37 100644 --- a/camel/camel-folder-summary.c +++ b/camel/camel-folder-summary.c @@ -359,21 +359,32 @@ folder_summary_get_property (GObject *object, G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } -/** - * folder_summary_update_counts_by_flags: - * - * Since: 3.0 - **/ -static void +static gboolean +is_in_memory_summary (CamelFolderSummary *summary) +{ + g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE); + + return (summary->flags & CAMEL_FOLDER_SUMMARY_IN_MEMORY_ONLY) != 0; +} + +#define UPDATE_COUNTS_ADD (1) +#define UPDATE_COUNTS_SUB (2) +#define UPDATE_COUNTS_ADD_WITHOUT_TOTAL (3) +#define UPDATE_COUNTS_SUB_WITHOUT_TOTAL (4) + +static gboolean folder_summary_update_counts_by_flags (CamelFolderSummary *summary, guint32 flags, - gboolean subtract) + gint op_type) { gint unread = 0, deleted = 0, junk = 0; gboolean is_junk_folder = FALSE, is_trash_folder = FALSE; + gboolean subtract = op_type == UPDATE_COUNTS_SUB || op_type == UPDATE_COUNTS_SUB_WITHOUT_TOTAL; + gboolean without_total = op_type == UPDATE_COUNTS_ADD_WITHOUT_TOTAL || op_type == UPDATE_COUNTS_SUB_WITHOUT_TOTAL; + gboolean changed = FALSE; GObject *summary_object; - g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary)); + g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE); summary_object = G_OBJECT (summary); @@ -400,21 +411,25 @@ folder_summary_update_counts_by_flags (CamelFolderSummary *summary, if (deleted) { summary->priv->deleted_count += deleted; g_object_notify (summary_object, "deleted-count"); + changed = TRUE; } if (junk) { summary->priv->junk_count += junk; g_object_notify (summary_object, "junk-count"); + changed = TRUE; } if (junk && !deleted) { summary->priv->junk_not_deleted_count += junk; g_object_notify (summary_object, "junk-not-deleted-count"); + changed = TRUE; } if (!junk && !deleted) { summary->priv->visible_count += subtract ? -1 : 1; g_object_notify (summary_object, "visible-count"); + changed = TRUE; } if (junk && !is_junk_folder) @@ -425,16 +440,23 @@ folder_summary_update_counts_by_flags (CamelFolderSummary *summary, if (unread) { summary->priv->unread_count += unread; g_object_notify (summary_object, "unread-count"); + changed = TRUE; } - summary->priv->saved_count += subtract ? -1 : 1; - g_object_notify (summary_object, "saved-count"); + if (!without_total) { + summary->priv->saved_count += subtract ? -1 : 1; + g_object_notify (summary_object, "saved-count"); + changed = TRUE; + } - camel_folder_summary_touch (summary); + if (changed) + camel_folder_summary_touch (summary); g_object_thaw_notify (summary_object); dd(printf("%p: %d %d %d | %d %d %d\n", (gpointer) summary, unread, deleted, junk, summary->priv->unread_count, summary->priv->visible_count, summary->priv->saved_count)); + + return changed; } static gboolean @@ -499,19 +521,21 @@ summary_header_to_db (CamelFolderSummary *summary, record->nextuid = summary->priv->nextuid; record->time = summary->time; - /* FIXME: Ever heard of Constructors and initializing ? */ - if (camel_db_count_total_message_info (db, table_name, &(record->saved_count), NULL)) - record->saved_count = 0; - if (camel_db_count_junk_message_info (db, table_name, &(record->junk_count), NULL)) - record->junk_count = 0; - if (camel_db_count_deleted_message_info (db, table_name, &(record->deleted_count), NULL)) - record->deleted_count = 0; - if (camel_db_count_unread_message_info (db, table_name, &(record->unread_count), NULL)) - record->unread_count = 0; - if (camel_db_count_visible_message_info (db, table_name, &(record->visible_count), NULL)) - record->visible_count = 0; - if (camel_db_count_junk_not_deleted_message_info (db, table_name, &(record->jnd_count), NULL)) - record->jnd_count = 0; + if (!is_in_memory_summary (summary)) { + /* FIXME: Ever heard of Constructors and initializing ? */ + if (camel_db_count_total_message_info (db, table_name, &(record->saved_count), NULL)) + record->saved_count = 0; + if (camel_db_count_junk_message_info (db, table_name, &(record->junk_count), NULL)) + record->junk_count = 0; + if (camel_db_count_deleted_message_info (db, table_name, &(record->deleted_count), NULL)) + record->deleted_count = 0; + if (camel_db_count_unread_message_info (db, table_name, &(record->unread_count), NULL)) + record->unread_count = 0; + if (camel_db_count_visible_message_info (db, table_name, &(record->visible_count), NULL)) + record->visible_count = 0; + if (camel_db_count_junk_not_deleted_message_info (db, table_name, &(record->jnd_count), NULL)) + record->jnd_count = 0; + } summary->priv->unread_count = record->unread_count; summary->priv->deleted_count = record->deleted_count; @@ -798,14 +822,24 @@ content_info_to_db (CamelFolderSummary *summary, return TRUE; } -static gboolean -folder_summary_replace_flags (CamelFolderSummary *summary, - CamelMessageInfo *info) +/** + * camel_folder_summary_replace_flags: + * @summary: a #CamelFolderSummary + * @info: a #CamelMessageInfo + * + * Updates internal counts based on the flags in @info. + * + * Returns: Whether any count changed + * + * Since: 3.6 + **/ +gboolean +camel_folder_summary_replace_flags (CamelFolderSummary *summary, + CamelMessageInfo *info) { - guint32 used_flags; + guint32 old_flags, new_flags, added_flags, removed_flags; GObject *summary_object; - guint32 old_saved_count, old_unread_count, old_deleted_count, old_junk_count; - guint32 old_junk_not_deleted_count, old_visible_count; + gboolean changed = FALSE; g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE); g_return_val_if_fail (info != NULL, FALSE); @@ -819,38 +853,41 @@ folder_summary_replace_flags (CamelFolderSummary *summary, camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); g_object_freeze_notify (summary_object); - used_flags = GPOINTER_TO_UINT (g_hash_table_lookup (summary->priv->uids, camel_message_info_uid (info))); + old_flags = GPOINTER_TO_UINT (g_hash_table_lookup (summary->priv->uids, camel_message_info_uid (info))); + new_flags = camel_message_info_flags (info); + + if ((old_flags & ~CAMEL_MESSAGE_FOLDER_FLAGGED) == (new_flags & ~CAMEL_MESSAGE_FOLDER_FLAGGED)) { + g_object_thaw_notify (summary_object); + camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); + return FALSE; + } - old_saved_count = summary->priv->saved_count; - old_unread_count = summary->priv->unread_count; - old_deleted_count = summary->priv->deleted_count; - old_junk_count = summary->priv->junk_count; - old_junk_not_deleted_count = summary->priv->junk_not_deleted_count; - old_visible_count = summary->priv->visible_count; + added_flags = new_flags & (~(old_flags & new_flags)); + removed_flags = old_flags & (~(old_flags & new_flags)); - /* decrement counts with old flags */ - folder_summary_update_counts_by_flags (summary, used_flags, TRUE); + if ((old_flags & CAMEL_MESSAGE_SEEN) == (new_flags & CAMEL_MESSAGE_SEEN)) { + /* unread count is different from others, it asks for nonexistence + of the flag, thus if it wasn't changed, then simply set it + in added/removed, thus there are no false notifications + on unread counts */ + added_flags |= CAMEL_MESSAGE_SEEN; + removed_flags |= CAMEL_MESSAGE_SEEN; + } + + /* decrement counts with removed flags */ + changed = folder_summary_update_counts_by_flags (summary, removed_flags, UPDATE_COUNTS_SUB_WITHOUT_TOTAL) || changed; + /* increment counts with added flags */ + changed = folder_summary_update_counts_by_flags (summary, added_flags, UPDATE_COUNTS_ADD_WITHOUT_TOTAL) || changed; /* update current flags on the summary */ g_hash_table_insert (summary->priv->uids, (gpointer) camel_pstring_strdup (camel_message_info_uid (info)), - GUINT_TO_POINTER (camel_message_info_flags (info))); + GUINT_TO_POINTER (new_flags)); - /* increment counts with new flags */ - folder_summary_update_counts_by_flags (summary, camel_message_info_flags (info), FALSE); - - /* this approach generates false notifications at least for "saved-count", - * but is it an issue? I suppose not. - */ g_object_thaw_notify (summary_object); camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); - return old_saved_count != summary->priv->saved_count || - old_unread_count != summary->priv->unread_count || - old_deleted_count != summary->priv->deleted_count || - old_junk_count != summary->priv->junk_count || - old_junk_not_deleted_count != summary->priv->junk_not_deleted_count || - old_visible_count != summary->priv->visible_count; + return changed; } static CamelMessageInfo * @@ -1051,7 +1088,7 @@ info_set_flags (CamelMessageInfo *info, if (mi->summary) { camel_folder_summary_lock (mi->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); g_object_freeze_notify (G_OBJECT (mi->summary)); - counts_changed = folder_summary_replace_flags (mi->summary, info); + counts_changed = camel_folder_summary_replace_flags (mi->summary, info); } if (!counts_changed && ((old & ~CAMEL_MESSAGE_SYSTEM_MASK) == (mi->flags & ~CAMEL_MESSAGE_SYSTEM_MASK)) && !((set & CAMEL_MESSAGE_JUNK_LEARN) && !(set & CAMEL_MESSAGE_JUNK))) { @@ -1719,6 +1756,47 @@ camel_folder_summary_free_array (GPtrArray *array) g_ptr_array_free (array, TRUE); } +static void +cfs_copy_uids_cb (gpointer key, + gpointer value, + gpointer user_data) +{ + const gchar *uid = key; + GHashTable *copy_hash = user_data; + + g_hash_table_insert (copy_hash, (gpointer) camel_pstring_strdup (uid), GINT_TO_POINTER (1)); +} + +/** + * camel_folder_summary_get_hash: + * @summary: a #CamelFolderSummary object + * + * Returns hash of current stored 'uids' in summary, where key is 'uid' + * from the string pool, and value is 1. The returned pointer should + * be freed with g_hash_table_destroy(). + * + * Note: When searching for values always use uids from the string pool. + * + * Since: 3.6 + **/ +GHashTable * +camel_folder_summary_get_hash (CamelFolderSummary *summary) +{ + GHashTable *uids; + + g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL); + + camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); + + /* using direct hash because of strings being from the string pool */ + uids = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) camel_pstring_free, NULL); + g_hash_table_foreach (summary->priv->uids, cfs_copy_uids_cb, uids); + + camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); + + return uids; +} + /** * camel_folder_summary_peek_loaded: * @@ -1764,6 +1842,14 @@ message_info_from_uid (CamelFolderSummary *summary, struct _db_pass_data data; folder_name = camel_folder_get_full_name (summary->priv->folder); + + if (is_in_memory_summary (summary)) { + camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); + g_warning ("%s: Tried to load uid '%s' from DB on in-memory summary of '%s'", + G_STRFUNC, uid, folder_name); + return NULL; + } + parent_store = camel_folder_get_parent_store (summary->priv->folder); cdb = parent_store->cdb_r; @@ -1956,7 +2042,9 @@ cfs_try_release_memory (CamelFolderSummary *summary) CamelSession *session; /* If folder is freed or if the cache is nil then clean up */ - if (!summary->priv->folder || !g_hash_table_size (summary->priv->loaded_infos)) { + if (!summary->priv->folder || + !g_hash_table_size (summary->priv->loaded_infos) || + is_in_memory_summary (summary)) { summary->priv->cache_load_time = 0; summary->priv->timeout_handle = 0; return FALSE; @@ -1982,6 +2070,9 @@ cfs_schedule_info_release_timer (CamelFolderSummary *summary) { g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary)); + if (is_in_memory_summary (summary)) + return; + if (!summary->priv->timeout_handle) { static gboolean know_can_do = FALSE, can_do = TRUE; @@ -2027,8 +2118,10 @@ msg_update_preview (const gchar *uid, /* FIXME Pass a GCancellable */ msg = camel_folder_get_message_sync (folder, uid, NULL, NULL); if (msg != NULL) { - if (camel_mime_message_build_preview ((CamelMimePart *) msg, (CamelMessageInfo *) info) && info->preview) - camel_db_write_preview_record (parent_store->cdb_w, full_name, info->uid, info->preview, NULL); + if (camel_mime_message_build_preview ((CamelMimePart *) msg, (CamelMessageInfo *) info) && info->preview) { + if (!is_in_memory_summary (folder->summary)) + camel_db_write_preview_record (parent_store->cdb_w, full_name, info->uid, info->preview, NULL); + } } camel_message_info_free (info); } @@ -2077,6 +2170,7 @@ preview_update (CamelSession *session, GHashTable *preview_data, *uids_hash; CamelStore *parent_store; const gchar *full_name; + gboolean is_in_memory = is_in_memory_summary (folder->summary); gint i; uids_array = camel_folder_summary_get_array (folder->summary); @@ -2088,7 +2182,7 @@ preview_update (CamelSession *session, full_name = camel_folder_get_full_name (folder); parent_store = camel_folder_get_parent_store (folder); - preview_data = camel_db_get_folder_preview (parent_store->cdb_r, full_name, NULL); + preview_data = is_in_memory ? NULL : camel_db_get_folder_preview (parent_store->cdb_r, full_name, NULL); if (preview_data) { g_hash_table_foreach_remove (preview_data, (GHRFunc) fill_mi, folder); g_hash_table_destroy (preview_data); @@ -2103,9 +2197,11 @@ preview_update (CamelSession *session, } camel_folder_lock (folder, CAMEL_FOLDER_REC_LOCK); - camel_db_begin_transaction (parent_store->cdb_w, NULL); + if (!is_in_memory) + camel_db_begin_transaction (parent_store->cdb_w, NULL); g_hash_table_foreach (uids_hash, (GHFunc) msg_update_preview, folder); - camel_db_end_transaction (parent_store->cdb_w, NULL); + if (!is_in_memory) + camel_db_end_transaction (parent_store->cdb_w, NULL); camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK); camel_folder_free_uids (folder, uids_uncached); g_hash_table_destroy (uids_hash); @@ -2128,6 +2224,9 @@ cfs_reload_from_db (CamelFolderSummary *summary, * load better. */ d(printf ("\ncamel_folder_summary_reload_from_db called \n")); + if (is_in_memory_summary (summary)) + return 0; + folder_name = camel_folder_get_full_name (summary->priv->folder); parent_store = camel_folder_get_parent_store (summary->priv->folder); session = camel_service_get_session (CAMEL_SERVICE (parent_store)); @@ -2221,6 +2320,9 @@ camel_folder_summary_load_from_db (CamelFolderSummary *summary, g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE); + if (is_in_memory_summary (summary)) + return TRUE; + camel_folder_summary_save_to_db (summary, NULL); /* struct _db_pass_data data; */ @@ -2512,6 +2614,9 @@ save_message_infos_to_db (CamelFolderSummary *summary, const gchar *full_name; SaveToDBArgs args; + if (is_in_memory_summary (summary)) + return 0; + args.error = error; args.migration = fresh_mirs; args.progress = 0; @@ -2567,7 +2672,8 @@ camel_folder_summary_save_to_db (CamelFolderSummary *summary, g_return_val_if_fail (summary != NULL, FALSE); - if (!(summary->flags & CAMEL_SUMMARY_DIRTY)) + if (!(summary->flags & CAMEL_FOLDER_SUMMARY_DIRTY) || + is_in_memory_summary (summary)) return TRUE; parent_store = camel_folder_get_parent_store (summary->priv->folder); @@ -2585,7 +2691,7 @@ camel_folder_summary_save_to_db (CamelFolderSummary *summary, camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); } - summary->flags &= ~CAMEL_SUMMARY_DIRTY; + summary->flags &= ~CAMEL_FOLDER_SUMMARY_DIRTY; count = cfs_count_dirty (summary); if (!count) @@ -2594,7 +2700,7 @@ camel_folder_summary_save_to_db (CamelFolderSummary *summary, ret = save_message_infos_to_db (summary, FALSE, error); if (ret != 0) { /* Failed, so lets reset the flag */ - summary->flags |= CAMEL_SUMMARY_DIRTY; + summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY; return FALSE; } @@ -2614,14 +2720,14 @@ camel_folder_summary_save_to_db (CamelFolderSummary *summary, ret = save_message_infos_to_db (summary, FALSE, error); if (ret != 0) { - summary->flags |= CAMEL_SUMMARY_DIRTY; + summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY; return FALSE; } } record = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->summary_header_to_db (summary, error); if (!record) { - summary->flags |= CAMEL_SUMMARY_DIRTY; + summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY; return FALSE; } @@ -2633,7 +2739,7 @@ camel_folder_summary_save_to_db (CamelFolderSummary *summary, if (ret != 0) { camel_db_abort_transaction (cdb, NULL); - summary->flags |= CAMEL_SUMMARY_DIRTY; + summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY; return FALSE; } @@ -2656,6 +2762,9 @@ camel_folder_summary_header_save_to_db (CamelFolderSummary *summary, CamelDB *cdb; gint ret; + if (is_in_memory_summary (summary)) + return TRUE; + parent_store = camel_folder_get_parent_store (summary->priv->folder); cdb = parent_store->cdb_w; @@ -2699,6 +2808,9 @@ camel_folder_summary_header_load_from_db (CamelFolderSummary *summary, d(printf ("\ncamel_folder_summary_header_load_from_db called \n")); + if (is_in_memory_summary (summary)) + return TRUE; + camel_folder_summary_save_to_db (summary, NULL); cdb = store->cdb_r; @@ -2782,7 +2894,7 @@ camel_folder_summary_add (CamelFolderSummary *summary, } base_info = (CamelMessageInfoBase *) info; - folder_summary_update_counts_by_flags (summary, camel_message_info_flags (info), FALSE); + folder_summary_update_counts_by_flags (summary, camel_message_info_flags (info), UPDATE_COUNTS_ADD); base_info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED; base_info->dirty = TRUE; @@ -2818,7 +2930,7 @@ camel_folder_summary_insert (CamelFolderSummary *summary, if (!load) { CamelMessageInfoBase *base_info = (CamelMessageInfoBase *) info; - folder_summary_update_counts_by_flags (summary, camel_message_info_flags (info), FALSE); + folder_summary_update_counts_by_flags (summary, camel_message_info_flags (info), UPDATE_COUNTS_ADD); base_info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED; base_info->dirty = TRUE; @@ -3100,7 +3212,7 @@ void camel_folder_summary_touch (CamelFolderSummary *summary) { camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); - summary->flags |= CAMEL_SUMMARY_DIRTY; + summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY; camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); } @@ -3144,7 +3256,10 @@ camel_folder_summary_clear (CamelFolderSummary *summary, parent_store = camel_folder_get_parent_store (summary->priv->folder); cdb = parent_store->cdb_w; - res = camel_db_clear_folder_summary (cdb, folder_name, error) == 0; + if (!is_in_memory_summary (summary)) + res = camel_db_clear_folder_summary (cdb, folder_name, error) == 0; + else + res = TRUE; summary_object = G_OBJECT (summary); g_object_freeze_notify (summary_object); @@ -3213,16 +3328,18 @@ camel_folder_summary_remove_uid (CamelFolderSummary *summary, return FALSE; } - folder_summary_update_counts_by_flags (summary, GPOINTER_TO_UINT (ptr_flags), TRUE); + folder_summary_update_counts_by_flags (summary, GPOINTER_TO_UINT (ptr_flags), UPDATE_COUNTS_SUB); uid_copy = camel_pstring_strdup (uid); g_hash_table_remove (summary->priv->uids, uid_copy); g_hash_table_remove (summary->priv->loaded_infos, uid_copy); - full_name = camel_folder_get_full_name (summary->priv->folder); - parent_store = camel_folder_get_parent_store (summary->priv->folder); - if (camel_db_delete_uid (parent_store->cdb_w, full_name, uid_copy, NULL) != 0) - res = FALSE; + if (!is_in_memory_summary (summary)) { + full_name = camel_folder_get_full_name (summary->priv->folder); + parent_store = camel_folder_get_parent_store (summary->priv->folder); + if (camel_db_delete_uid (parent_store->cdb_w, full_name, uid_copy, NULL) != 0) + res = FALSE; + } camel_pstring_free (uid_copy); @@ -3255,26 +3372,30 @@ camel_folder_summary_remove_uids (CamelFolderSummary *summary, g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE); g_return_val_if_fail (uids != NULL, FALSE); + g_object_freeze_notify (G_OBJECT (summary)); camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); for (l = g_list_first(uids); l; l = g_list_next(l)) { gpointer ptr_uid = NULL, ptr_flags = NULL; if (g_hash_table_lookup_extended (summary->priv->uids, l->data, &ptr_uid, &ptr_flags)) { const gchar *uid_copy = camel_pstring_strdup (l->data); - folder_summary_update_counts_by_flags (summary, GPOINTER_TO_UINT (ptr_flags), TRUE); + folder_summary_update_counts_by_flags (summary, GPOINTER_TO_UINT (ptr_flags), UPDATE_COUNTS_SUB); g_hash_table_remove (summary->priv->uids, uid_copy); g_hash_table_remove (summary->priv->loaded_infos, uid_copy); camel_pstring_free (uid_copy); } } - full_name = camel_folder_get_full_name (summary->priv->folder); - parent_store = camel_folder_get_parent_store (summary->priv->folder); - if (camel_db_delete_uids (parent_store->cdb_w, full_name, uids, NULL) != 0) - res = FALSE; + if (is_in_memory_summary (summary)) { + full_name = camel_folder_get_full_name (summary->priv->folder); + parent_store = camel_folder_get_parent_store (summary->priv->folder); + if (camel_db_delete_uids (parent_store->cdb_w, full_name, uids, NULL) != 0) + res = FALSE; + } camel_folder_summary_touch (summary); camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); + g_object_thaw_notify (G_OBJECT (summary)); return res; } diff --git a/camel/camel-folder-summary.h b/camel/camel-folder-summary.h index dacacab..50f335e 100644 --- a/camel/camel-folder-summary.h +++ b/camel/camel-folder-summary.h @@ -214,8 +214,17 @@ struct _CamelMessageInfoBase { gchar *bodystructure; }; +/** + * CamelFolderSummaryFlags: + * @CAMEL_FOLDER_SUMMARY_DIRTY: + * There are changes in summary, which should be saved. + * @CAMEL_FOLDER_SUMMARY_IN_MEMORY_ONLY: + * Summary with this flag doesn't use DB for storing its content, + * it is always created on the fly. + **/ typedef enum { - CAMEL_SUMMARY_DIRTY = 1 << 0 + CAMEL_FOLDER_SUMMARY_DIRTY = 1 << 0, + CAMEL_FOLDER_SUMMARY_IN_MEMORY_ONLY = 1 << 1 } CamelFolderSummaryFlags; /** @@ -419,6 +428,12 @@ CamelMessageInfo * camel_folder_summary_get (CamelFolderSummary *summary, GPtrArray * camel_folder_summary_get_array (CamelFolderSummary *summary); void camel_folder_summary_free_array (GPtrArray *array); +GHashTable * camel_folder_summary_get_hash (CamelFolderSummary *summary); + +gboolean camel_folder_summary_replace_flags + (CamelFolderSummary *summary, + CamelMessageInfo *info); + /* Peek from mem only */ CamelMessageInfo * camel_folder_summary_peek_loaded (CamelFolderSummary *summary, diff --git a/camel/camel-folder.c b/camel/camel-folder.c index 633a41a..a1c5525 100644 --- a/camel/camel-folder.c +++ b/camel/camel-folder.c @@ -851,6 +851,8 @@ folder_freeze (CamelFolder *folder) camel_folder_lock (folder, CAMEL_FOLDER_CHANGE_LOCK); folder->priv->frozen++; + if (folder->summary) + g_object_freeze_notify (G_OBJECT (folder->summary)); d (printf ("freeze (%p '%s') = %d\n", folder, folder->full_name, folder->priv->frozen)); camel_folder_unlock (folder, CAMEL_FOLDER_CHANGE_LOCK); @@ -866,6 +868,8 @@ folder_thaw (CamelFolder *folder) camel_folder_lock (folder, CAMEL_FOLDER_CHANGE_LOCK); folder->priv->frozen--; + if (folder->summary) + g_object_thaw_notify (G_OBJECT (folder->summary)); d (printf ("thaw (%p '%s') = %d\n", folder, folder->full_name, folder->priv->frozen)); diff --git a/camel/camel-imapx-server.c b/camel/camel-imapx-server.c index 9f65e49..a52f521 100644 --- a/camel/camel-imapx-server.c +++ b/camel/camel-imapx-server.c @@ -5109,7 +5109,7 @@ imapx_command_sync_changes_done (CamelIMAPXServer *is, } if (job->commands == 0) { - if (job->folder->summary && (job->folder->summary->flags & CAMEL_SUMMARY_DIRTY) != 0) { + if (job->folder->summary && (job->folder->summary->flags & CAMEL_FOLDER_SUMMARY_DIRTY) != 0) { CamelStoreInfo *si; /* ... and store's summary when folder's summary is dirty */ diff --git a/camel/camel-store.c b/camel/camel-store.c index 8e55069..12fa79d 100644 --- a/camel/camel-store.c +++ b/camel/camel-store.c @@ -213,7 +213,7 @@ cs_delete_cached_folder (CamelStore *store, vfolder = camel_object_bag_get ( store->folders, CAMEL_VTRASH_NAME); if (vfolder != NULL) { - camel_vee_folder_remove_folder (vfolder, folder); + camel_vee_folder_remove_folder (vfolder, folder, NULL); g_object_unref (vfolder); } } @@ -222,7 +222,7 @@ cs_delete_cached_folder (CamelStore *store, vfolder = camel_object_bag_get ( store->folders, CAMEL_VJUNK_NAME); if (vfolder != NULL) { - camel_vee_folder_remove_folder (vfolder, folder); + camel_vee_folder_remove_folder (vfolder, folder, NULL); g_object_unref (vfolder); } } @@ -245,7 +245,7 @@ store_get_special (CamelStore *store, folders = camel_object_bag_list (store->folders); for (i = 0; i < folders->len; i++) { if (!CAMEL_IS_VTRASH_FOLDER (folders->pdata[i])) - camel_vee_folder_add_folder ((CamelVeeFolder *) folder, (CamelFolder *) folders->pdata[i]); + camel_vee_folder_add_folder ((CamelVeeFolder *) folder, (CamelFolder *) folders->pdata[i], NULL); g_object_unref (folders->pdata[i]); } g_ptr_array_free (folders, TRUE); @@ -402,8 +402,7 @@ store_synchronize_sync (CamelStore *store, camel_folder_synchronize_sync ( folder, expunge, cancellable, &local_error); ignore_no_such_table_exception (&local_error); - } else if (CAMEL_IS_VEE_FOLDER (folder)) - camel_vee_folder_sync_headers (folder, NULL); /* Literally don't care of vfolder exceptions */ + } g_object_unref (folder); } @@ -1924,7 +1923,7 @@ camel_store_get_folder_sync (CamelStore *store, * virtual Junk folder, let the virtual Junk folder * track this folder. */ if (vjunk != NULL) { - camel_vee_folder_add_folder (vjunk, folder); + camel_vee_folder_add_folder (vjunk, folder, NULL); g_object_unref (vjunk); } @@ -1932,7 +1931,7 @@ camel_store_get_folder_sync (CamelStore *store, * virtual Trash folder, let the virtual Trash folder * track this folder. */ if (vtrash != NULL) { - camel_vee_folder_add_folder (vtrash, folder); + camel_vee_folder_add_folder (vtrash, folder, NULL); g_object_unref (vtrash); } diff --git a/camel/camel-string-utils.c b/camel/camel-string-utils.c index 817c1d0..76971d9 100644 --- a/camel/camel-string-utils.c +++ b/camel/camel-string-utils.c @@ -161,8 +161,7 @@ camel_pstring_add (gchar *str, gboolean own) { gpointer pcount; - gchar *pstr; - gint count; + gpointer pstr; if (str == NULL) return NULL; @@ -175,16 +174,18 @@ camel_pstring_add (gchar *str, g_static_mutex_lock (&pstring_lock); if (pstring_table == NULL) - pstring_table = g_hash_table_new (g_str_hash, g_str_equal); + pstring_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); - if (g_hash_table_lookup_extended (pstring_table, str, (gpointer *) &pstr, &pcount)) { - count = GPOINTER_TO_INT (pcount) + 1; - g_hash_table_insert (pstring_table, pstr, GINT_TO_POINTER (count)); + if (g_hash_table_lookup_extended (pstring_table, str, &pstr, &pcount)) { + gint *count = pcount; + *count = (*count) + 1; if (own) g_free (str); } else { + gint *count = g_new0 (gint, 1); + *count = 1; pstr = own ? str : g_strdup (str); - g_hash_table_insert (pstring_table, pstr, GINT_TO_POINTER (1)); + g_hash_table_insert (pstring_table, pstr, count); } g_static_mutex_unlock (&pstring_lock); @@ -209,7 +210,7 @@ const gchar * camel_pstring_peek (const gchar *str) { gpointer pcount; - gchar *pstr; + gpointer pstr; if (str == NULL) return NULL; @@ -220,11 +221,13 @@ camel_pstring_peek (const gchar *str) g_static_mutex_lock (&pstring_lock); if (pstring_table == NULL) - pstring_table = g_hash_table_new (g_str_hash, g_str_equal); + pstring_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); - if (!g_hash_table_lookup_extended (pstring_table, str, (gpointer *) &pstr, &pcount)) { + if (!g_hash_table_lookup_extended (pstring_table, str, &pstr, &pcount)) { + gint *count = g_new0 (gint, 1); + *count = 1; pstr = g_strdup (str); - g_hash_table_insert (pstring_table, pstr, GINT_TO_POINTER (1)); + g_hash_table_insert (pstring_table, pstr, count); } g_static_mutex_unlock (&pstring_lock); @@ -262,9 +265,8 @@ camel_pstring_strdup (const gchar *s) void camel_pstring_free (const gchar *s) { - gchar *p; + gpointer pstr; gpointer pcount; - gint count; if (pstring_table == NULL) return; @@ -272,17 +274,16 @@ camel_pstring_free (const gchar *s) return; g_static_mutex_lock (&pstring_lock); - if (g_hash_table_lookup_extended (pstring_table, s, (gpointer *) &p, &pcount)) { - count = GPOINTER_TO_INT (pcount) - 1; - if (count == 0) { - g_hash_table_remove (pstring_table, p); - g_free (p); + if (g_hash_table_lookup_extended (pstring_table, s, &pstr, &pcount)) { + gint *count = pcount; + *count = (*count) - 1; + if ((*count) == 0) { + g_hash_table_remove (pstring_table, pstr); + g_free (pstr); if (g_getenv("CDS_DEBUG")) { - if (p != s) /* Only for debugging purposes */ + if (pstr != s) /* Only for debugging purposes */ g_assert (0); } - } else { - g_hash_table_insert (pstring_table, p, GINT_TO_POINTER (count)); } } else { if (g_getenv("CDS_DEBUG")) { @@ -293,3 +294,39 @@ camel_pstring_free (const gchar *s) } g_static_mutex_unlock (&pstring_lock); } + +static void +count_pstring_memory_cb (gpointer key, + gpointer value, + gpointer user_data) +{ + const gchar *str = key; + guint64 *pbytes = user_data; + + g_return_if_fail (str != NULL); + g_return_if_fail (pbytes != NULL); + + *pbytes += strlen (str); +} + +/** + * camel_pstring_dump_stat: + * + * Dumps to stdout memory statistic about the string pool. + **/ +void +camel_pstring_dump_stat (void) +{ + g_static_mutex_lock (&pstring_lock); + + if (!pstring_table) { + g_print (" String Pool Statistics: Not used yet\n"); + } else { + guint64 bytes = 0; + + g_hash_table_foreach (pstring_table, count_pstring_memory_cb, &bytes); + g_print (" String Pool Statistics: Holds %d strings of total %" G_GUINT64_FORMAT " bytes\n", g_hash_table_size (pstring_table), bytes); + } + + g_static_mutex_unlock (&pstring_lock); +} diff --git a/camel/camel-string-utils.h b/camel/camel-string-utils.h index c84ca38..d6b11e6 100644 --- a/camel/camel-string-utils.h +++ b/camel/camel-string-utils.h @@ -46,6 +46,7 @@ const gchar *camel_pstring_add (gchar *str, gboolean own); const gchar *camel_pstring_strdup (const gchar *s); void camel_pstring_free (const gchar *s); const gchar * camel_pstring_peek (const gchar *str); +void camel_pstring_dump_stat (void); G_END_DECLS diff --git a/camel/camel-subscribable.c b/camel/camel-subscribable.c index 0bb193b..ba1eed0 100644 --- a/camel/camel-subscribable.c +++ b/camel/camel-subscribable.c @@ -110,7 +110,7 @@ subscribable_delete_cached_folder (CamelStore *store, folder_name = CAMEL_VTRASH_NAME; vfolder = camel_object_bag_get (store->folders, folder_name); if (vfolder != NULL) { - camel_vee_folder_remove_folder (vfolder, folder); + camel_vee_folder_remove_folder (vfolder, folder, NULL); g_object_unref (vfolder); } } @@ -119,7 +119,7 @@ subscribable_delete_cached_folder (CamelStore *store, folder_name = CAMEL_VJUNK_NAME; vfolder = camel_object_bag_get (store->folders, folder_name); if (vfolder != NULL) { - camel_vee_folder_remove_folder (vfolder, folder); + camel_vee_folder_remove_folder (vfolder, folder, NULL); g_object_unref (vfolder); } } diff --git a/camel/camel-vee-data-cache.c b/camel/camel-vee-data-cache.c new file mode 100644 index 0000000..b0a1284 --- /dev/null +++ b/camel/camel-vee-data-cache.c @@ -0,0 +1,638 @@ +/* + * Copyright (C) 2012 Red Hat, Inc. (www.redhat.com) + * + * Authors: Milan Crha + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * 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 Lesser General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "camel-string-utils.h" +#include "camel-store.h" + +#include "camel-vee-data-cache.h" + +struct _CamelVeeSubfolderDataPrivate +{ + CamelFolder *folder; + const gchar *folder_id; /* stored in string pool */ +}; + +G_DEFINE_TYPE (CamelVeeSubfolderData, camel_vee_subfolder_data, G_TYPE_OBJECT) + +static void +camel_vee_subfolder_data_dispose (GObject *object) +{ + CamelVeeSubfolderData *data; + + data = CAMEL_VEE_SUBFOLDER_DATA (object); + if (data->priv) { + if (data->priv->folder) + g_object_unref (data->priv->folder); + data->priv->folder = NULL; + + if (data->priv->folder_id) + camel_pstring_free (data->priv->folder_id); + data->priv->folder_id = NULL; + } + + /* Chain up to parent's dispose () method. */ + G_OBJECT_CLASS (camel_vee_subfolder_data_parent_class)->dispose (object); +} + +static void +camel_vee_subfolder_data_class_init (CamelVeeSubfolderDataClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (CamelVeeSubfolderDataPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = camel_vee_subfolder_data_dispose; +} + +static void +camel_vee_subfolder_data_init (CamelVeeSubfolderData *data) +{ + data->priv = G_TYPE_INSTANCE_GET_PRIVATE (data, CAMEL_TYPE_VEE_SUBFOLDER_DATA, CamelVeeSubfolderDataPrivate); +} + +static void +vee_subfolder_data_hash_folder (CamelFolder *folder, + gchar buffer[8]) +{ + CamelStore *parent_store; + GChecksum *checksum; + guint8 *digest; + gsize length; + gint state = 0, save = 0; + gchar *ptr_string; + const gchar *uid; + gint i; + + length = g_checksum_type_get_length (G_CHECKSUM_MD5); + digest = g_alloca (length); + + checksum = g_checksum_new (G_CHECKSUM_MD5); + parent_store = camel_folder_get_parent_store (folder); + uid = camel_service_get_uid (CAMEL_SERVICE (parent_store)); + g_checksum_update (checksum, (guchar *) uid, -1); + + ptr_string = g_strdup_printf ("%p", folder); + g_checksum_update (checksum, (guchar *) ptr_string, -1); + g_free (ptr_string); + + g_checksum_get_digest (checksum, digest, &length); + g_checksum_free (checksum); + + g_base64_encode_step (digest, 6, FALSE, buffer, &state, &save); + g_base64_encode_close (FALSE, buffer, &state, &save); + + for (i = 0; i < 8; i++) { + if (buffer[i] == '+') + buffer[i] = '.'; + if (buffer[i] == '/') + buffer[i] = '_'; + } +} + +CamelVeeSubfolderData * +camel_vee_subfolder_data_new (CamelFolder *folder) +{ + CamelVeeSubfolderData *data; + gchar buffer[8], *folder_id; + + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); + + data = g_object_new (CAMEL_TYPE_VEE_SUBFOLDER_DATA, NULL); + data->priv->folder = g_object_ref (folder); + + vee_subfolder_data_hash_folder (folder, buffer); + folder_id = g_strndup (buffer, 8); + + data->priv->folder_id = camel_pstring_add (folder_id, TRUE); + + return data; +} + +CamelFolder * +camel_vee_subfolder_data_get_folder (CamelVeeSubfolderData *data) +{ + g_return_val_if_fail (CAMEL_IS_VEE_SUBFOLDER_DATA (data), NULL); + + return data->priv->folder; +} + +const gchar * +camel_vee_subfolder_data_get_folder_id (CamelVeeSubfolderData *data) +{ + g_return_val_if_fail (CAMEL_IS_VEE_SUBFOLDER_DATA (data), NULL); + + return data->priv->folder_id; +} + +/* ----------------------------------------------------------------------- */ + +struct _CamelVeeMessageInfoDataPrivate +{ + CamelVeeSubfolderData *subfolder_data; + const gchar *orig_message_uid; /* stored in string pool */ + const gchar *vee_message_uid; /* stored in string pool */ +}; + +G_DEFINE_TYPE (CamelVeeMessageInfoData, camel_vee_message_info_data, G_TYPE_OBJECT) + +static void +camel_vee_message_info_data_dispose (GObject *object) +{ + CamelVeeMessageInfoData *data; + + data = CAMEL_VEE_MESSAGE_INFO_DATA (object); + if (data->priv) { + if (data->priv->subfolder_data) + g_object_unref (data->priv->subfolder_data); + data->priv->subfolder_data = NULL; + + if (data->priv->orig_message_uid) + camel_pstring_free (data->priv->orig_message_uid); + data->priv->orig_message_uid = NULL; + + if (data->priv->vee_message_uid) + camel_pstring_free (data->priv->vee_message_uid); + data->priv->vee_message_uid = NULL; + } + + /* Chain up to parent's dispose () method. */ + G_OBJECT_CLASS (camel_vee_message_info_data_parent_class)->dispose (object); +} + +static void +camel_vee_message_info_data_class_init (CamelVeeMessageInfoDataClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (CamelVeeMessageInfoDataPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = camel_vee_message_info_data_dispose; +} + +static void +camel_vee_message_info_data_init (CamelVeeMessageInfoData *data) +{ + data->priv = G_TYPE_INSTANCE_GET_PRIVATE (data, CAMEL_TYPE_VEE_MESSAGE_INFO_DATA, CamelVeeMessageInfoDataPrivate); +} + +CamelVeeMessageInfoData * +camel_vee_message_info_data_new (CamelVeeSubfolderData *subfolder_data, + const gchar *orig_message_uid) +{ + CamelVeeMessageInfoData *data; + gchar *vee_message_uid; + + g_return_val_if_fail (CAMEL_IS_VEE_SUBFOLDER_DATA (subfolder_data), NULL); + g_return_val_if_fail (orig_message_uid != NULL, NULL); + + data = g_object_new (CAMEL_TYPE_VEE_MESSAGE_INFO_DATA, NULL); + data->priv->subfolder_data = g_object_ref (subfolder_data); + + vee_message_uid = g_strconcat (camel_vee_subfolder_data_get_folder_id (subfolder_data), orig_message_uid, NULL); + + data->priv->orig_message_uid = camel_pstring_strdup (orig_message_uid); + data->priv->vee_message_uid = camel_pstring_add (vee_message_uid, TRUE); + + return data; +} + +CamelVeeSubfolderData * +camel_vee_message_info_data_get_subfolder_data (CamelVeeMessageInfoData *data) +{ + g_return_val_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (data), NULL); + + return data->priv->subfolder_data; +} + +const gchar * +camel_vee_message_info_data_get_orig_message_uid (CamelVeeMessageInfoData *data) +{ + g_return_val_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (data), NULL); + + return data->priv->orig_message_uid; +} + +const gchar * +camel_vee_message_info_data_get_vee_message_uid (CamelVeeMessageInfoData *data) +{ + g_return_val_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (data), NULL); + + return data->priv->vee_message_uid; +} + +/* ----------------------------------------------------------------------- */ + +struct _CamelVeeDataCachePrivate +{ + GMutex *sf_mutex; /* guards subfolder_hash */ + GHashTable *subfolder_hash; /* CamelFolder * => CamelVeeSubfolderData * */ + + GMutex *mi_mutex; /* guards message_info_hash */ + GHashTable *orig_message_uid_hash; /* VeeData * => CamelVeeMessageInfoData * */ + GHashTable *vee_message_uid_hash; /* const gchar *vee_uid => CamelVeeMessageInfoData * */ +}; + +G_DEFINE_TYPE (CamelVeeDataCache, camel_vee_data_cache, G_TYPE_OBJECT) + +typedef struct _VeeData +{ + CamelFolder *folder; + const gchar *orig_message_uid; +} VeeData; + +static guint +vee_data_hash (gconstpointer ptr) +{ + const VeeData *vee_data = ptr; + + if (!vee_data) + return 0; + + return g_direct_hash (vee_data->folder) + + g_str_hash (vee_data->orig_message_uid); +} + +static gboolean +vee_data_equal (gconstpointer v1, + gconstpointer v2) +{ + const VeeData *vee_data1 = v1, *vee_data2 = v2; + + if (!v1 || !v2) + return v1 == v2; + + /* can contain ponters directly, strings are always from the string pool */ + return v1 == v2 || + (vee_data1->folder == vee_data2->folder && + vee_data1->orig_message_uid == vee_data2->orig_message_uid); +} + +static void +camel_vee_data_cache_dispose (GObject *object) +{ + CamelVeeDataCache *data_cache; + + data_cache = CAMEL_VEE_DATA_CACHE (object); + if (data_cache->priv) { + if (data_cache->priv->subfolder_hash) + g_hash_table_destroy (data_cache->priv->subfolder_hash); + data_cache->priv->subfolder_hash = NULL; + + if (data_cache->priv->orig_message_uid_hash) + g_hash_table_destroy (data_cache->priv->orig_message_uid_hash); + data_cache->priv->orig_message_uid_hash = NULL; + + if (data_cache->priv->vee_message_uid_hash) + g_hash_table_destroy (data_cache->priv->vee_message_uid_hash); + data_cache->priv->vee_message_uid_hash = NULL; + + if (data_cache->priv->sf_mutex) + g_mutex_free (data_cache->priv->sf_mutex); + data_cache->priv->sf_mutex = NULL; + + if (data_cache->priv->mi_mutex) + g_mutex_free (data_cache->priv->mi_mutex); + data_cache->priv->mi_mutex = NULL; + } + + /* Chain up to parent's dispose () method. */ + G_OBJECT_CLASS (camel_vee_data_cache_parent_class)->dispose (object); +} + +static void +camel_vee_data_cache_class_init (CamelVeeDataCacheClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (CamelVeeDataCachePrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = camel_vee_data_cache_dispose; +} + +static void +camel_vee_data_cache_init (CamelVeeDataCache *data_cache) +{ + data_cache->priv = G_TYPE_INSTANCE_GET_PRIVATE (data_cache, CAMEL_TYPE_VEE_DATA_CACHE, CamelVeeDataCachePrivate); + + data_cache->priv->sf_mutex = g_mutex_new (); + data_cache->priv->subfolder_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); + + data_cache->priv->mi_mutex = g_mutex_new (); + data_cache->priv->orig_message_uid_hash = g_hash_table_new_full (vee_data_hash, vee_data_equal, g_free, g_object_unref); + data_cache->priv->vee_message_uid_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); +} + +CamelVeeDataCache * +camel_vee_data_cache_new (void) +{ + return g_object_new (CAMEL_TYPE_VEE_DATA_CACHE, NULL); +} + +void +camel_vee_data_cache_add_subfolder (CamelVeeDataCache *data_cache, + CamelFolder *subfolder) +{ + CamelVeeSubfolderData *sf_data; + + g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache)); + g_return_if_fail (CAMEL_IS_FOLDER (subfolder)); + + g_mutex_lock (data_cache->priv->mi_mutex); + g_mutex_lock (data_cache->priv->sf_mutex); + + sf_data = g_hash_table_lookup (data_cache->priv->subfolder_hash, subfolder); + if (!sf_data) { + GPtrArray *uids; + gint ii; + + sf_data = camel_vee_subfolder_data_new (subfolder); + g_hash_table_insert (data_cache->priv->subfolder_hash, subfolder, sf_data); + + /* camel_vee_data_cache_get_message_info_data() caches uids on demand, + while here are cached all known uids in once - it is better when + the folder is used in Unmatched folder, where the uid/vuid will + be used in the vfolder or Unmatched folder anyway */ + uids = camel_folder_get_uids (subfolder); + if (uids) { + for (ii = 0; ii < uids->len; ii++) { + VeeData vdata; + CamelVeeMessageInfoData *mi_data; + + /* make sure the orig_message_uid comes from the string pool */ + vdata.folder = subfolder; + vdata.orig_message_uid = camel_pstring_strdup (uids->pdata[ii]); + + mi_data = g_hash_table_lookup (data_cache->priv->orig_message_uid_hash, &vdata); + if (!mi_data) { + VeeData *hash_data; + + mi_data = camel_vee_message_info_data_new (sf_data, vdata.orig_message_uid); + + hash_data = g_new0 (VeeData, 1); + hash_data->folder = subfolder; + hash_data->orig_message_uid = camel_vee_message_info_data_get_orig_message_uid (mi_data); + + g_hash_table_insert (data_cache->priv->orig_message_uid_hash, hash_data, mi_data); + g_hash_table_insert (data_cache->priv->vee_message_uid_hash, + (gpointer) camel_vee_message_info_data_get_vee_message_uid (mi_data), + mi_data); + } + + camel_pstring_free (vdata.orig_message_uid); + } + + camel_folder_free_uids (subfolder, uids); + } + } + + g_mutex_unlock (data_cache->priv->sf_mutex); + g_mutex_unlock (data_cache->priv->mi_mutex); +} + +static gboolean +remove_vee_by_folder_cb (gpointer key, + gpointer value, + gpointer user_data) +{ + CamelVeeMessageInfoData *mi_data = value; + CamelVeeSubfolderData *sf_data; + CamelFolder *folder = user_data; + + if (!mi_data) + return FALSE; + + sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data); + return sf_data && camel_vee_subfolder_data_get_folder (sf_data) == folder; +} + +static gboolean +remove_orig_by_folder_cb (gpointer key, + gpointer value, + gpointer user_data) +{ + VeeData *vee_data = key; + CamelFolder *folder = user_data; + + return vee_data && vee_data->folder == folder; +} + +void +camel_vee_data_cache_remove_subfolder (CamelVeeDataCache *data_cache, + CamelFolder *subfolder) +{ + g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache)); + g_return_if_fail (CAMEL_IS_FOLDER (subfolder)); + + g_mutex_lock (data_cache->priv->mi_mutex); + g_mutex_lock (data_cache->priv->sf_mutex); + + g_hash_table_foreach_remove (data_cache->priv->vee_message_uid_hash, remove_vee_by_folder_cb, subfolder); + g_hash_table_foreach_remove (data_cache->priv->orig_message_uid_hash, remove_orig_by_folder_cb, subfolder); + g_hash_table_remove (data_cache->priv->subfolder_hash, subfolder); + + g_mutex_unlock (data_cache->priv->sf_mutex); + g_mutex_unlock (data_cache->priv->mi_mutex); +} + +CamelVeeSubfolderData * +camel_vee_data_cache_get_subfolder_data (CamelVeeDataCache *data_cache, + CamelFolder *folder) +{ + CamelVeeSubfolderData *res; + + g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), NULL); + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); + + g_mutex_lock (data_cache->priv->sf_mutex); + + res = g_hash_table_lookup (data_cache->priv->subfolder_hash, folder); + if (!res) { + res = camel_vee_subfolder_data_new (folder); + g_hash_table_insert (data_cache->priv->subfolder_hash, folder, res); + } + + g_object_ref (res); + + g_mutex_unlock (data_cache->priv->sf_mutex); + + return res; +} + +CamelVeeMessageInfoData * +camel_vee_data_cache_get_message_info_data (CamelVeeDataCache *data_cache, + CamelFolder *folder, + const gchar *orig_message_uid) +{ + CamelVeeMessageInfoData *res; + VeeData vdata; + + g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), NULL); + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); + g_return_val_if_fail (orig_message_uid != NULL, NULL); + + g_mutex_lock (data_cache->priv->mi_mutex); + + /* make sure the orig_message_uid comes from the string pool */ + vdata.folder = folder; + vdata.orig_message_uid = camel_pstring_strdup (orig_message_uid); + + res = g_hash_table_lookup (data_cache->priv->orig_message_uid_hash, &vdata); + if (!res) { + VeeData *hash_data; + CamelVeeSubfolderData *sf_data; + + /* this locks also priv->sf_mutex */ + sf_data = camel_vee_data_cache_get_subfolder_data (data_cache, folder); + if (!sf_data) { + camel_pstring_free (vdata.orig_message_uid); + g_mutex_unlock (data_cache->priv->mi_mutex); + g_return_val_if_fail (sf_data != NULL, NULL); + } + + res = camel_vee_message_info_data_new (sf_data, orig_message_uid); + + /* res holds the reference now */ + g_object_unref (sf_data); + + hash_data = g_new0 (VeeData, 1); + hash_data->folder = folder; + hash_data->orig_message_uid = camel_vee_message_info_data_get_orig_message_uid (res); + + g_hash_table_insert (data_cache->priv->orig_message_uid_hash, hash_data, res); + g_hash_table_insert (data_cache->priv->vee_message_uid_hash, + (gpointer) camel_vee_message_info_data_get_vee_message_uid (res), + res); + } + + camel_pstring_free (vdata.orig_message_uid); + g_object_ref (res); + + g_mutex_unlock (data_cache->priv->mi_mutex); + + return res; +} + +CamelVeeMessageInfoData * +camel_vee_data_cache_get_message_info_data_by_vuid (CamelVeeDataCache *data_cache, + const gchar *vee_message_uid) +{ + CamelVeeMessageInfoData *res; + const gchar *vuid; + + g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), NULL); + g_return_val_if_fail (vee_message_uid != NULL, NULL); + + g_mutex_lock (data_cache->priv->mi_mutex); + + /* make sure vee_message_uid comes from the string pool */ + vuid = camel_pstring_strdup (vee_message_uid); + + res = g_hash_table_lookup (data_cache->priv->vee_message_uid_hash, vuid); + if (res) + g_object_ref (res); + + g_mutex_unlock (data_cache->priv->mi_mutex); + + camel_pstring_free (vuid); + + return res; +} + +struct ForeachMiData +{ + CamelFolder *fromfolder; + void (* func) (CamelVeeMessageInfoData *mi_data, + CamelFolder *subfolder, + gpointer user_data); + gpointer user_data; +}; + +static void +cvdc_foreach_mi_data_cb (gpointer key, + gpointer value, + gpointer user_data) +{ + VeeData *vdata = key; + CamelVeeMessageInfoData *mi_data = value; + struct ForeachMiData *fmd = user_data; + + g_return_if_fail (key != NULL); + g_return_if_fail (value != NULL); + g_return_if_fail (user_data != NULL); + + if (!fmd->fromfolder || fmd->fromfolder == vdata->folder) + fmd->func (mi_data, vdata->folder, fmd->user_data); +} + +void +camel_vee_data_cache_foreach_message_info_data (CamelVeeDataCache *data_cache, + CamelFolder *fromfolder, + void (* func) (CamelVeeMessageInfoData *mi_data, + CamelFolder *subfolder, + gpointer user_data), + gpointer user_data) +{ + struct ForeachMiData fmd; + + g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache)); + g_return_if_fail (func != NULL); + + g_mutex_lock (data_cache->priv->mi_mutex); + + fmd.fromfolder = fromfolder; + fmd.func = func; + fmd.user_data = user_data; + + g_hash_table_foreach (data_cache->priv->orig_message_uid_hash, cvdc_foreach_mi_data_cb, &fmd); + + g_mutex_unlock (data_cache->priv->mi_mutex); +} + +void +camel_vee_data_cache_remove_message_info_data (CamelVeeDataCache *data_cache, + CamelVeeMessageInfoData *mi_data) +{ + VeeData vdata; + CamelVeeSubfolderData *sf_data; + const gchar *vuid; + + g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache)); + g_return_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (mi_data)); + + g_mutex_lock (data_cache->priv->mi_mutex); + + sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data); + + vdata.folder = camel_vee_subfolder_data_get_folder (sf_data); + vdata.orig_message_uid = camel_vee_message_info_data_get_orig_message_uid (mi_data); + vuid = camel_vee_message_info_data_get_vee_message_uid (mi_data); + + g_hash_table_remove (data_cache->priv->vee_message_uid_hash, vuid); + g_hash_table_remove (data_cache->priv->orig_message_uid_hash, &vdata); + + g_mutex_unlock (data_cache->priv->mi_mutex); +} diff --git a/camel/camel-vee-data-cache.h b/camel/camel-vee-data-cache.h new file mode 100644 index 0000000..99249f5 --- /dev/null +++ b/camel/camel-vee-data-cache.h @@ -0,0 +1,177 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2012 Red Hat, Inc. (www.redhat.com) + * + * Authors: Milan Crha + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * 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 Lesser General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#if !defined (__CAMEL_H_INSIDE__) && !defined (CAMEL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef CAMEL_VEE_DATA_CACHE_H +#define CAMEL_VEE_DATA_CACHE_H + +#include + +/* Standard GObject macros */ +#define CAMEL_TYPE_VEE_SUBFOLDER_DATA \ + (camel_vee_subfolder_data_get_type ()) +#define CAMEL_VEE_SUBFOLDER_DATA(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), CAMEL_TYPE_VEE_SUBFOLDER_DATA, CamelVeeSubfolderData)) +#define CAMEL_VEE_SUBFOLDER_DATA_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), CAMEL_TYPE_VEE_SUBFOLDER_DATA, CamelVeeSubfolderDataClass)) +#define CAMEL_IS_VEE_SUBFOLDER_DATA(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), CAMEL_TYPE_VEE_SUBFOLDER_DATA)) +#define CAMEL_IS_VEE_SUBFOLDER_DATA_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), CAMEL_TYPE_VEE_SUBFOLDER_DATA)) +#define CAMEL_VEE_SUBFOLDER_DATA_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), CAMEL_TYPE_VEE_SUBFOLDER_DATA, CamelVeeSubfolderDataClass)) + +#define CAMEL_TYPE_VEE_MESSAGE_INFO_DATA \ + (camel_vee_message_info_data_get_type ()) +#define CAMEL_VEE_MESSAGE_INFO_DATA(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), CAMEL_TYPE_VEE_MESSAGE_INFO_DATA, CamelVeeMessageInfoData)) +#define CAMEL_VEE_MESSAGE_INFO_DATA_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), CAMEL_TYPE_VEE_MESSAGE_INFO_DATA, CamelVeeMessageInfoDataClass)) +#define CAMEL_IS_VEE_MESSAGE_INFO_DATA(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), CAMEL_TYPE_VEE_MESSAGE_INFO_DATA)) +#define CAMEL_IS_VEE_MESSAGE_INFO_DATA_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), CAMEL_TYPE_VEE_MESSAGE_INFO_DATA)) +#define CAMEL_VEE_MESSAGE_INFO_DATA_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), CAMEL_TYPE_VEE_MESSAGE_INFO_DATA, CamelVeeMessageInfoDataClass)) + +#define CAMEL_TYPE_VEE_DATA_CACHE \ + (camel_vee_data_cache_get_type ()) +#define CAMEL_VEE_DATA_CACHE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), CAMEL_TYPE_VEE_DATA_CACHE, CamelVeeDataCache)) +#define CAMEL_VEE_DATA_CACHE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), CAMEL_TYPE_VEE_DATA_CACHE, CamelVeeDataCacheClass)) +#define CAMEL_IS_VEE_DATA_CACHE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), CAMEL_TYPE_VEE_DATA_CACHE)) +#define CAMEL_IS_VEE_DATA_CACHE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), CAMEL_TYPE_VEE_DATA_CACHE)) +#define CAMEL_VEE_DATA_CACHE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), CAMEL_TYPE_VEE_DATA_CACHE, CamelVeeDataCacheClass)) + +G_BEGIN_DECLS + +typedef struct _CamelVeeSubfolderData CamelVeeSubfolderData; +typedef struct _CamelVeeSubfolderDataPrivate CamelVeeSubfolderDataPrivate; +typedef struct _CamelVeeSubfolderDataClass CamelVeeSubfolderDataClass; + +typedef struct _CamelVeeMessageInfoData CamelVeeMessageInfoData; +typedef struct _CamelVeeMessageInfoDataPrivate CamelVeeMessageInfoDataPrivate; +typedef struct _CamelVeeMessageInfoDataClass CamelVeeMessageInfoDataClass; + +typedef struct _CamelVeeDataCache CamelVeeDataCache; +typedef struct _CamelVeeDataCachePrivate CamelVeeDataCachePrivate; +typedef struct _CamelVeeDataCacheClass CamelVeeDataCacheClass; + +struct _CamelVeeSubfolderData { + GObject parent; + + CamelVeeSubfolderDataPrivate *priv; +}; + +struct _CamelVeeSubfolderDataClass { + GObjectClass parent_class; +}; + +GType camel_vee_subfolder_data_get_type (void); +CamelVeeSubfolderData * camel_vee_subfolder_data_new (CamelFolder *folder); +CamelFolder * camel_vee_subfolder_data_get_folder (CamelVeeSubfolderData *data); /* returned not reffed */ +const gchar * camel_vee_subfolder_data_get_folder_id (CamelVeeSubfolderData *data); + +/* ----------------------------------------------------------------------- */ + +struct _CamelVeeMessageInfoData { + GObject parent; + + CamelVeeMessageInfoDataPrivate *priv; +}; + +struct _CamelVeeMessageInfoDataClass { + GObjectClass parent_class; +}; + +GType camel_vee_message_info_data_get_type (void); +CamelVeeMessageInfoData * camel_vee_message_info_data_new (CamelVeeSubfolderData *subfolder_data, + const gchar *orig_message_uid); +CamelVeeSubfolderData * camel_vee_message_info_data_get_subfolder_data + (CamelVeeMessageInfoData *data); /* returned not reffed */ +const gchar * camel_vee_message_info_data_get_orig_message_uid + (CamelVeeMessageInfoData *data); +const gchar * camel_vee_message_info_data_get_vee_message_uid + (CamelVeeMessageInfoData *data); + +/* ----------------------------------------------------------------------- */ + +struct _CamelVeeDataCache { + GObject parent; + + CamelVeeDataCachePrivate *priv; +}; + +struct _CamelVeeDataCacheClass { + GObjectClass parent_class; +}; + +GType camel_vee_data_cache_get_type (void); +CamelVeeDataCache * camel_vee_data_cache_new (void); +void camel_vee_data_cache_add_subfolder (CamelVeeDataCache *data_cache, + CamelFolder *subfolder); +void camel_vee_data_cache_remove_subfolder (CamelVeeDataCache *data_cache, + CamelFolder *subfolder); +CamelVeeSubfolderData * camel_vee_data_cache_get_subfolder_data (CamelVeeDataCache *data_cache, /* returned is reffed */ + CamelFolder *folder); +CamelVeeMessageInfoData * camel_vee_data_cache_get_message_info_data /* returned is reffed */ + (CamelVeeDataCache *data_cache, + CamelFolder *folder, + const gchar *orig_message_uid); +CamelVeeMessageInfoData * camel_vee_data_cache_get_message_info_data_by_vuid /* returned is reffed */ + (CamelVeeDataCache *data_cache, + const gchar *vee_message_uid); +void camel_vee_data_cache_foreach_message_info_data + (CamelVeeDataCache *data_cache, + CamelFolder *fromfolder, + void (* func) (CamelVeeMessageInfoData *mi_data, + CamelFolder *subfolder, + gpointer user_data), + gpointer user_data); +void camel_vee_data_cache_remove_message_info_data + (CamelVeeDataCache *data_cache, + CamelVeeMessageInfoData *mi_data); + +G_END_DECLS + +#endif /* CAMEL_VEE_DATA_CACHE_H */ diff --git a/camel/camel-vee-folder.c b/camel/camel-vee-folder.c index c66b40a..82aa589 100644 --- a/camel/camel-vee-folder.c +++ b/camel/camel-vee-folder.c @@ -51,585 +51,417 @@ typedef struct _FolderChangedData FolderChangedData; struct _CamelVeeFolderPrivate { gboolean destroyed; - GList *folders; /* lock using subfolder_lock before changing/accessing */ - GList *folders_changed; /* for list of folders that have changed between updates */ + GList *subfolders; /* lock using subfolder_lock before changing/accessing */ GHashTable *ignore_changed; /* hash of subfolder pointers to ignore the next folder's 'changed' signal */ GHashTable *skipped_changes; /* CamelFolder -> CamelFolderChangeInfo accumulating ignored changes */ + GHashTable *unmatched_add_changed; /* CamelVeeMessageInfoData -> 1, for unmatched folder, postponed additions from camel_vee_folder_add_vuid() */ + GHashTable *unmatched_remove_changed; /* CamelVeeMessageInfoData -> 1, for unmatched folder, postponed removal from camel_vee_folder_remove_vuid() */ + gboolean auto_update; /* Processing queue for folder changes. */ GAsyncQueue *change_queue; gboolean change_queue_busy; - GMutex *summary_lock; /* for locking vfolder summary */ - GMutex *subfolder_lock; /* for locking the subfolder list */ - GMutex *changed_lock; /* for locking the folders-changed list */ + GStaticRecMutex summary_lock; /* for locking vfolder summary */ + GStaticRecMutex subfolder_lock; /* for locking the subfolder list */ + GStaticRecMutex changed_lock; /* for locking the folders-changed list */ + + gchar *expression; /* query expression */ + + /* only set-up if our parent is a vee-store, used also as a flag to + * say that this folder is part of the unmatched folder */ + CamelVeeStore *parent_vee_store; + + CamelVeeDataCache *vee_data_cache; }; -struct _update_data { - CamelFolder *source; - CamelVeeFolder *vee_folder; - gchar hash[8]; - CamelVeeFolder *folder_unmatched; - GHashTable *unmatched_uids; - gboolean rebuilt, correlating; - - /* used for uids that needs to be updated in db later by unmatched_check_uid and - * folder_added_uid */ - GQueue *message_uids; +/* The custom property ID is a CamelArg artifact. + * It still identifies the property in state files. */ +enum { + PROP_0, + PROP_AUTO_UPDATE = 0x2401 }; +G_DEFINE_TYPE (CamelVeeFolder, camel_vee_folder, CAMEL_TYPE_FOLDER) + struct _FolderChangedData { CamelFolderChangeInfo *changes; - CamelFolder *sub; + CamelFolder *subfolder; }; -G_DEFINE_TYPE (CamelVeeFolder, camel_vee_folder, CAMEL_TYPE_FOLDER) +static FolderChangedData * +vee_folder_changed_data_new (CamelFolder *subfolder, + CamelFolderChangeInfo *changes) +{ + FolderChangedData *data; + + data = g_slice_new0 (FolderChangedData); + data->changes = camel_folder_change_info_new (); + camel_folder_change_info_cat (data->changes, changes); + data->subfolder = g_object_ref (subfolder); + + return data; +} static void -folder_changed_data_free (FolderChangedData *data) +vee_folder_changed_data_free (FolderChangedData *data) { camel_folder_change_info_free (data->changes); - g_object_unref (data->sub); + g_object_unref (data->subfolder); g_slice_free (FolderChangedData, data); } -/* must be called with summary_lock held */ -static CamelVeeMessageInfo * -vee_folder_add_uid (CamelVeeFolder *vf, - CamelFolder *f, - const gchar *inuid, - const gchar hash[8]) +static CamelVeeDataCache * +vee_folder_get_data_cache (CamelVeeFolder *vfolder) { - CamelVeeMessageInfo *mi = NULL; + g_return_val_if_fail (CAMEL_IS_VEE_FOLDER (vfolder), NULL); + + if (vfolder->priv->parent_vee_store) + return camel_vee_store_get_vee_data_cache (vfolder->priv->parent_vee_store); - mi = camel_vee_summary_add ((CamelVeeSummary *)((CamelFolder *) vf)->summary, f->summary, (gchar *) inuid, hash); - return mi; + return vfolder->priv->vee_data_cache; } -/* same as vee_folder_add_uid, only returns whether uid was added or not */ static gboolean -vee_folder_add_uid_test (CamelVeeFolder *vf, - CamelFolder *f, - const gchar *inuid, - const gchar hash[8]) +vee_folder_is_unmatched (CamelVeeFolder *vfolder) { - CamelVeeMessageInfo *mi; - - mi = vee_folder_add_uid (vf, f, inuid, hash); + g_return_val_if_fail (vfolder != NULL, FALSE); - if (mi != NULL) - camel_message_info_free ((CamelMessageInfo *) mi); - - return mi != NULL; + return vfolder->priv->parent_vee_store && + vfolder == camel_vee_store_get_unmatched_folder (vfolder->priv->parent_vee_store); } -/* A "correlating" expression has the property that whether a message matches - * depends on the other messages being searched. folder_changed_change on a - * vfolder with a correlating expression may not make all the necessary updates, - * so the query is redone on the entire changed source folder the next time - * the vfolder is opened. - * - * The only current example of a correlating expression is one that uses - * "match-threads". */ -static gboolean -expression_is_correlating (const gchar *expr) +static void +vee_folder_note_added_uid (CamelVeeFolder *vfolder, + CamelVeeSummary *vsummary, + CamelVeeMessageInfoData *added_mi_data, + CamelFolderChangeInfo *changes, + gboolean included_as_changed) { - /* XXX: Actually parse the expression to avoid triggering on - * "match-threads" in the text the user is searching for! */ - return (strstr (expr, "match-threads") != NULL); + const gchar *vuid; + + vuid = camel_vee_message_info_data_get_vee_message_uid (added_mi_data); + if (!camel_folder_summary_check_uid (&vsummary->summary, vuid)) { + /* add it only if it wasn't in yet */ + CamelVeeMessageInfo *vmi; + + vmi = camel_vee_summary_add (vsummary, added_mi_data); + if (vmi) { + if (changes) + camel_folder_change_info_add_uid (changes, vuid); + camel_message_info_free (vmi); + + if (vfolder->priv->parent_vee_store) + camel_vee_store_note_vuid_used (vfolder->priv->parent_vee_store, added_mi_data, vfolder); + } + } else { + camel_vee_summary_replace_flags (vsummary, vuid); + if (included_as_changed && changes) + camel_folder_change_info_change_uid (changes, vuid); + } } -/* Hold all these with summary lock and unmatched summary lock held */ static void -folder_changed_add_uid (CamelFolder *sub, - const gchar *uid, - const gchar hash[8], - CamelVeeFolder *vf, - gboolean use_db, - GList **m_added_l, - GList **unm_added_l) +vee_folder_note_unmatch_uid (CamelVeeFolder *vfolder, + CamelVeeSummary *vsummary, + CamelFolder *subfolder, + CamelVeeMessageInfoData *unmatched_mi_data, + CamelFolderChangeInfo *changes) { - CamelVeeMessageInfo *vinfo; const gchar *vuid; - gchar *oldkey; - gpointer oldval; - gint n; - CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL; - GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL; - - vinfo = vee_folder_add_uid (vf, sub, uid, hash); - if (vinfo == NULL) - return; - vuid = camel_pstring_strdup (camel_message_info_uid (vinfo)); - camel_message_info_free ((CamelMessageInfo *) vinfo); + vuid = camel_vee_message_info_data_get_vee_message_uid (unmatched_mi_data); + if (camel_folder_summary_check_uid (&vsummary->summary, vuid)) { + g_object_ref (unmatched_mi_data); - if (use_db) - *m_added_l = g_list_prepend (*m_added_l, (gpointer) camel_pstring_strdup (vuid)); + /* this one doesn't belong to us anymore */ + if (changes) + camel_folder_change_info_remove_uid (changes, vuid); + camel_vee_summary_remove (vsummary, vuid, subfolder); - camel_folder_change_info_add_uid (vf->changes, vuid); - if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER (sub) && folder_unmatched != NULL) { - if (g_hash_table_lookup_extended (unmatched_uids, vuid, (gpointer *) &oldkey, &oldval)) { - n = GPOINTER_TO_INT (oldval); - g_hash_table_insert (unmatched_uids, oldkey, GINT_TO_POINTER (n + 1)); - } else { - g_hash_table_insert (unmatched_uids, g_strdup (vuid), GINT_TO_POINTER (1)); - } - vinfo = (CamelVeeMessageInfo *) camel_folder_get_message_info ((CamelFolder *) folder_unmatched, vuid); - if (vinfo) { - camel_folder_change_info_remove_uid ( - folder_unmatched->changes, vuid); - - *unm_added_l = g_list_prepend (*unm_added_l, (gpointer) camel_pstring_strdup (vuid)); - - camel_folder_summary_remove_uid ( - CAMEL_FOLDER (folder_unmatched)->summary, vuid); - camel_folder_free_message_info ( - CAMEL_FOLDER (folder_unmatched), - (CamelMessageInfo *) vinfo); - } - } + if (vfolder->priv->parent_vee_store) + camel_vee_store_note_vuid_unused (vfolder->priv->parent_vee_store, unmatched_mi_data, vfolder); - camel_pstring_free (vuid); + g_object_unref (unmatched_mi_data); + } } static void -folder_changed_remove_uid (CamelFolder *sub, - const gchar *uid, - const gchar hash[8], - gint keep, - CamelVeeFolder *vf, - gboolean use_db, - GList **m_removed_l, - GList **unm_removed_l) +vee_folder_remove_unmatched (CamelVeeFolder *vfolder, + CamelVeeSummary *vsummary, + CamelVeeDataCache *data_cache, + CamelFolderChangeInfo *changes, + CamelFolder *subfolder, + const gchar *orig_message_uid, + gboolean is_orig_message_uid) /* if not, then it's 'vee_message_uid' */ { - CamelFolder *folder = (CamelFolder *) vf; - gchar *vuid, *oldkey; - gpointer oldval; - gint n; - CamelVeeMessageInfo *vinfo; - CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL; - GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL; - - vuid = alloca (strlen (uid) + 9); - memcpy (vuid, hash, 8); - strcpy (vuid + 8, uid); - - camel_folder_change_info_remove_uid (vf->changes, vuid); - if (use_db) - *m_removed_l = g_list_prepend (*m_removed_l, (gpointer) camel_pstring_strdup (vuid)); - - camel_folder_summary_remove_uid (folder->summary, vuid); - - if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER (sub) && folder_unmatched != NULL) { - if (keep) { - if (g_hash_table_lookup_extended (unmatched_uids, vuid, (gpointer *) &oldkey, &oldval)) { - n = GPOINTER_TO_INT (oldval); - if (n == 1) { - g_hash_table_remove (unmatched_uids, oldkey); - if (vee_folder_add_uid_test (folder_unmatched, sub, uid, hash)) - camel_folder_change_info_add_uid (folder_unmatched->changes, oldkey); - g_free (oldkey); - } else { - g_hash_table_insert (unmatched_uids, oldkey, GINT_TO_POINTER (n - 1)); - } - } else { - if (vee_folder_add_uid_test (folder_unmatched, sub, uid, hash)) - camel_folder_change_info_add_uid (folder_unmatched->changes, vuid); - } - } else { - if (g_hash_table_lookup_extended (unmatched_uids, vuid, (gpointer *) &oldkey, &oldval)) { - g_hash_table_remove (unmatched_uids, oldkey); - g_free (oldkey); - } + CamelVeeMessageInfoData *mi_data; - vinfo = (CamelVeeMessageInfo *) camel_folder_get_message_info ((CamelFolder *) folder_unmatched, vuid); - if (vinfo) { - camel_folder_change_info_remove_uid ( - folder_unmatched->changes, vuid); + if (is_orig_message_uid) + mi_data = camel_vee_data_cache_get_message_info_data (data_cache, subfolder, orig_message_uid); + else + mi_data = camel_vee_data_cache_get_message_info_data_by_vuid (data_cache, orig_message_uid); - *unm_removed_l = g_list_prepend (*unm_removed_l, (gpointer) camel_pstring_strdup (vuid)); + if (!mi_data) + return; - camel_folder_summary_remove_uid ( - CAMEL_FOLDER (folder_unmatched)->summary, vuid); - camel_folder_free_message_info ( - CAMEL_FOLDER (folder_unmatched), - (CamelMessageInfo *) vinfo); - } - } - } + vee_folder_note_unmatch_uid (vfolder, vsummary, subfolder, mi_data, changes); + + g_object_unref (mi_data); } +struct RemoveUnmatchedData +{ + CamelVeeFolder *vfolder; + CamelVeeSummary *vsummary; + CamelFolder *subfolder; + CamelVeeDataCache *data_cache; + CamelFolderChangeInfo *changes; + gboolean is_orig_message_uid; +}; + static void -folder_changed_change_uid (CamelFolder *sub, - const gchar *uid, - const gchar hash[8], - CamelVeeFolder *vf, - gboolean use_db, - GList **m_removed_l, - GList **unm_removed_l) +vee_folder_remove_unmatched_cb (gpointer key, + gpointer value, + gpointer user_data) { - gchar *vuid; - CamelVeeMessageInfo *vinfo, *uinfo = NULL; - CamelMessageInfo *info; - CamelFolder *folder = (CamelFolder *) vf; - CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL; - - vuid = alloca (strlen (uid) + 9); - memcpy (vuid, hash, 8); - strcpy (vuid + 8, uid); - - vinfo = (CamelVeeMessageInfo *) camel_folder_summary_get (folder->summary, vuid); - if (folder_unmatched != NULL) - uinfo = (CamelVeeMessageInfo *) camel_folder_summary_get (((CamelFolder *) folder_unmatched)->summary, vuid); - if (vinfo || uinfo) { - info = camel_folder_get_message_info (sub, uid); - if (info) { - if (vinfo) { - camel_folder_change_info_change_uid (vf->changes, vuid); - vinfo->old_flags = camel_message_info_flags ((CamelMessageInfo *) vinfo); - vinfo->info.flags |= (vinfo->old_flags & ~CAMEL_MESSAGE_FOLDER_FLAGGED); - camel_message_info_free ((CamelMessageInfo *) vinfo); - } + struct RemoveUnmatchedData *rud = user_data; + const gchar *uid = key; - if (uinfo) { - camel_folder_change_info_change_uid (folder_unmatched->changes, vuid); - uinfo->old_flags = camel_message_info_flags ((CamelMessageInfo *) uinfo); - uinfo->info.flags |= (uinfo->old_flags & ~CAMEL_MESSAGE_FOLDER_FLAGGED); - camel_message_info_free ((CamelMessageInfo *) uinfo); - } + g_return_if_fail (rud != NULL); - camel_folder_free_message_info (sub, info); - } else { - if (vinfo) { - folder_changed_remove_uid (sub, uid, hash, FALSE, vf, use_db, m_removed_l, unm_removed_l); - camel_message_info_free ((CamelMessageInfo *) vinfo); - } - if (uinfo) - camel_message_info_free ((CamelMessageInfo *) uinfo); - } - } + vee_folder_remove_unmatched (rud->vfolder, rud->vsummary, rud->data_cache, rud->changes, rud->subfolder, uid, rud->is_orig_message_uid); } static void -vfolder_add_remove_transaction (CamelStore *parent_store, - const gchar *full_name, - GList **uids, - gboolean add, - GError **error) +vee_folder_merge_matching (CamelVeeFolder *vfolder, + CamelFolder *subfolder, + GHashTable *all_uids, + GPtrArray *match, + CamelFolderChangeInfo *changes, + gboolean included_as_changed) { - GList *l; + CamelVeeDataCache *data_cache; + CamelVeeMessageInfoData *mi_data; + CamelFolder *folder; + CamelVeeSummary *vsummary; + struct RemoveUnmatchedData rud; + gint ii; - for (l = *uids; l != NULL; l = g_list_next (l)) { - if (add) - camel_db_add_to_vfolder_transaction (parent_store->cdb_w, full_name, - (const gchar *) l->data, error); - else - camel_db_delete_uid_from_vfolder_transaction - (parent_store->cdb_w, full_name, - (const gchar *) l->data, error); + g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder)); + g_return_if_fail (CAMEL_IS_FOLDER (subfolder)); + g_return_if_fail (all_uids != NULL); + g_return_if_fail (match != NULL); + + folder = CAMEL_FOLDER (vfolder); + g_return_if_fail (folder != NULL); + + vsummary = CAMEL_VEE_SUMMARY (folder->summary); + g_return_if_fail (vsummary != NULL); + + data_cache = vee_folder_get_data_cache (vfolder); + for (ii = 0; ii < match->len; ii++) { + const gchar *uid = match->pdata[ii]; + + mi_data = camel_vee_data_cache_get_message_info_data (data_cache, subfolder, uid); + if (!mi_data) + continue; + + g_hash_table_remove (all_uids, uid); + + vee_folder_note_added_uid (vfolder, vsummary, mi_data, changes, included_as_changed); + + g_object_unref (mi_data); } - g_list_foreach (*uids, (GFunc) camel_pstring_free, NULL); - g_list_free (*uids); - *uids = NULL; + rud.vfolder = vfolder; + rud.vsummary = vsummary; + rud.subfolder = subfolder; + rud.data_cache = data_cache; + rud.changes = changes; + rud.is_orig_message_uid = TRUE; + + /* in 'all_uids' left only those which are not part of the folder anymore */ + g_hash_table_foreach (all_uids, vee_folder_remove_unmatched_cb, &rud); } static void -folder_changed_change (CamelVeeFolder *vf, - GCancellable *cancellable, - FolderChangedData *data, - GError **error) +vee_folder_rebuild_folder_with_changes (CamelVeeFolder *vfolder, + CamelFolder *subfolder, + CamelFolderChangeInfo *changes, + GCancellable *cancellable) { - CamelFolder *sub = data->sub; - CamelFolder *folder = CAMEL_FOLDER (vf); - CamelFolderChangeInfo *changes = data->changes; - gchar *vuid = NULL, hash[8]; - const gchar *uid; - CamelVeeMessageInfo *vinfo; - gint i, vuidlen = 0; - CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL; - GPtrArray *matches_added = NULL, /* newly added, that match */ - *matches_changed = NULL, /* newly changed, that now match */ - *newchanged = NULL, - *changed; - GPtrArray *always_changed = NULL; - GHashTable *matches_hash; - CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL; - GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL; - GPtrArray *present = NULL; - GList *m_added_l = NULL, *m_removed_l = NULL, *unm_added_l = NULL, *unm_removed_l = NULL; - - /* See vee_folder_rebuild_folder. */ - gboolean correlating = expression_is_correlating (vf->expression); - - /* Check the folder hasn't beem removed while we weren't watching */ - camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - if (g_list_find (CAMEL_VEE_FOLDER_GET_PRIVATE (vf)->folders, sub) == NULL) { - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - return; - } + GPtrArray *match = NULL; - camel_vee_folder_hash_folder (sub, hash); + g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder)); + g_return_if_fail (CAMEL_IS_FOLDER (subfolder)); - /* Lookup anything before we lock anything, to avoid deadlock with build_folder */ + /* Unmatched folder cannot be rebuilt */ + if (vee_folder_is_unmatched (vfolder)) + return; - /* Find newly added that match */ - if (changes->uid_added->len > 0) { - dd (printf (" Searching for added matches '%s'\n", vf->expression)); - matches_added = camel_folder_search_by_uids (sub, vf->expression, changes->uid_added, cancellable, NULL); + /* if we have no expression, or its been cleared, then act as if no matches */ + if (vfolder->priv->expression == NULL) { + match = g_ptr_array_new (); + } else { + match = camel_folder_search_by_expression (subfolder, vfolder->priv->expression, cancellable, NULL); + if (!match) + return; } - /* TODO: - * In this code around here, we can work out if the search will affect the changes - * we had, and only re-search against them if they might have */ - - /* Search for changed items that newly match, but only if we dont have them */ - changed = changes->uid_changed; - if (changed->len > 0) { - dd (printf (" Searching for changed matches '%s'\n", vf->expression)); - - if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0) { - newchanged = g_ptr_array_new (); - always_changed = g_ptr_array_new (); - for (i = 0; i < changed->len; i++) { - uid = changed->pdata[i]; - if (!vuid || strlen (uid) + 9 > vuidlen) { - vuidlen = strlen (uid) + 64; - vuid = g_realloc (vuid, vuidlen); - } - memcpy (vuid, hash, 8); - strcpy (vuid + 8, uid); - vinfo = (CamelVeeMessageInfo *) camel_folder_summary_get (folder->summary, vuid); - if (vinfo == NULL) { - g_ptr_array_add (newchanged, (gchar *) uid); - } else { - g_ptr_array_add (always_changed, (gchar *) uid); - camel_message_info_free ((CamelMessageInfo *) vinfo); - } - } - changed = newchanged; - } + if (!g_cancellable_is_cancelled (cancellable)) { + GHashTable *all_uids; - if (changed->len) - matches_changed = camel_folder_search_by_uids (sub, vf->expression, changed, cancellable, NULL); - if (always_changed && always_changed->len) - present = camel_folder_search_by_uids (sub, vf->expression, always_changed, cancellable, NULL); + all_uids = camel_folder_summary_get_hash (subfolder->summary); + vee_folder_merge_matching (vfolder, subfolder, all_uids, match, changes, FALSE); + g_hash_table_destroy (all_uids); } - camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_SUMMARY_LOCK); + camel_folder_search_free (subfolder, match); +} - if (folder_unmatched != NULL) - camel_vee_folder_lock (folder_unmatched, CAMEL_VEE_FOLDER_SUMMARY_LOCK); +static void +vee_folder_rebuild_all (CamelVeeFolder *vfolder, + GCancellable *cancellable) +{ + CamelFolderChangeInfo *changes; + GList *iter; - /* Always remove removed uid's, in any case */ - for (i = 0; i < changes->uid_removed->len; i++) { - dd (printf (" removing uid '%s'\n", (gchar *)changes->uid_removed->pdata[i])); - folder_changed_remove_uid (sub, changes->uid_removed->pdata[i], hash, FALSE, vf, !correlating, &m_removed_l, &unm_removed_l); - } + g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder)); - /* Add any newly matched or to unmatched folder if they dont */ - if (matches_added) { - matches_hash = g_hash_table_new (g_str_hash, g_str_equal); - for (i = 0; i < matches_added->len; i++) { - dd (printf (" %s", (gchar *)matches_added->pdata[i])); - g_hash_table_insert (matches_hash, matches_added->pdata[i], matches_added->pdata[i]); - } - for (i = 0; i < changes->uid_added->len; i++) { - uid = changes->uid_added->pdata[i]; - if (g_hash_table_lookup (matches_hash, uid)) { - dd (printf (" adding uid '%s' [newly matched]\n", (gchar *)uid)); - folder_changed_add_uid (sub, uid, hash, vf, !correlating, &m_added_l, &unm_added_l); - } else if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) { - if (strlen (uid) + 9 > vuidlen) { - vuidlen = strlen (uid) + 64; - vuid = g_realloc (vuid, vuidlen); - } - memcpy (vuid, hash, 8); - strcpy (vuid + 8, uid); - - if (!CAMEL_IS_VEE_FOLDER (sub) && folder_unmatched != NULL && g_hash_table_lookup (unmatched_uids, vuid) == NULL) { - dd (printf (" adding uid '%s' to Unmatched [newly unmatched]\n", (gchar *)uid)); - vinfo = (CamelVeeMessageInfo *) camel_folder_get_message_info ((CamelFolder *) folder_unmatched, vuid); - if (vinfo == NULL) { - if (vee_folder_add_uid_test (folder_unmatched, sub, uid, hash)) - camel_folder_change_info_add_uid (folder_unmatched->changes, vuid); - } else { - camel_folder_free_message_info ((CamelFolder *) folder_unmatched, (CamelMessageInfo *) vinfo); - } - } - } - } - g_hash_table_destroy (matches_hash); - } + /* Unmatched folder cannot be rebuilt */ + if (vee_folder_is_unmatched (vfolder)) + return; - /* Change any newly changed */ - if (always_changed) { - if (correlating) { - /* Messages may be pulled in by the correlation even if - * they do not match the expression individually, so it - * would be wrong to preemptively remove anything here. - * vee_folder_rebuild_folder will make any necessary removals - * when it re-queries the entire source folder. */ - for (i = 0; i < always_changed->len; i++) - folder_changed_change_uid (sub, always_changed->pdata[i], hash, vf, !correlating, &m_removed_l, &unm_removed_l); - } else { - GHashTable *ht_present = g_hash_table_new (g_str_hash, g_str_equal); + changes = camel_folder_change_info_new (); - for (i = 0; present && i < present->len; i++) { - folder_changed_change_uid (sub, present->pdata[i], hash, vf, !correlating, &m_removed_l, &unm_removed_l); - g_hash_table_insert (ht_present, present->pdata[i], present->pdata[i]); - } + camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - for (i = 0; i < always_changed->len; i++) { - if (!present || !g_hash_table_lookup (ht_present, always_changed->pdata[i])) - /* XXX: IIUC, these messages haven't been deleted from the - * source folder, so shouldn't "keep" be set to TRUE? */ - folder_changed_remove_uid (sub, always_changed->pdata[i], hash, TRUE, vf, !correlating, &m_removed_l, &unm_removed_l); - } + for (iter = vfolder->priv->subfolders; + iter && !g_cancellable_is_cancelled (cancellable); + iter = iter->next) { + CamelFolder *subfolder = iter->data; - g_hash_table_destroy (ht_present); - } - g_ptr_array_free (always_changed, TRUE); + vee_folder_rebuild_folder_with_changes (vfolder, subfolder, changes, cancellable); } - /* Change/add/remove any changed */ - if (changes->uid_changed->len) { - /* If we are auto-updating, then re-check changed uids still match */ - dd (printf (" Vfolder %supdate\nuids match:", (vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO)?"auto-":"")); - matches_hash = g_hash_table_new (g_str_hash, g_str_equal); - for (i = 0; matches_changed && i < matches_changed->len; i++) { - dd (printf (" %s", (gchar *)matches_changed->pdata[i])); - g_hash_table_insert (matches_hash, matches_changed->pdata[i], matches_changed->pdata[i]); - } - dd (printf ("\n")); + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - for (i = 0; i < changed->len; i++) { - uid = changed->pdata[i]; - if (strlen (uid) + 9 > vuidlen) { - vuidlen = strlen (uid) + 64; - vuid = g_realloc (vuid, vuidlen); - } - memcpy (vuid, hash, 8); - strcpy (vuid + 8, uid); - vinfo = (CamelVeeMessageInfo *) camel_folder_summary_get (folder->summary, vuid); - if (vinfo == NULL) { - if (g_hash_table_lookup (matches_hash, uid)) { - /* A uid we dont have, but now it matches, add it */ - dd (printf (" adding uid '%s' [newly matched]\n", uid)); - folder_changed_add_uid (sub, uid, hash, vf, !correlating, &m_added_l, &unm_added_l); - } else { - /* A uid we still don't have, just change it (for unmatched) */ - folder_changed_change_uid (sub, uid, hash, vf, !correlating, &m_removed_l, &unm_removed_l); - } - } else { - if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0 - || g_hash_table_lookup (matches_hash, uid)) { - /* still match, or we're not auto-updating, change event, (if it changed) */ - dd (printf (" changing uid '%s' [still matches]\n", uid)); - folder_changed_change_uid (sub, uid, hash, vf, !correlating, &m_removed_l, &unm_removed_l); - } else { - /* No longer matches, remove it, but keep it in unmatched (potentially) */ - dd (printf (" removing uid '%s' [did match]\n", uid)); - folder_changed_remove_uid (sub, uid, hash, TRUE, vf, !correlating, &m_removed_l, &unm_removed_l); - } - camel_message_info_free ((CamelMessageInfo *) vinfo); - } - } - g_hash_table_destroy (matches_hash); - } else { - /* stuff didn't match but it changed - check unmatched folder for changes */ - for (i = 0; i < changed->len; i++) - folder_changed_change_uid (sub, changed->pdata[i], hash, vf, !correlating, &m_removed_l, &unm_removed_l); - } + if (camel_folder_change_info_changed (changes)) + camel_folder_changed (CAMEL_FOLDER (vfolder), changes); + camel_folder_change_info_free (changes); +} - if (folder_unmatched != NULL) { - if (camel_folder_change_info_changed (folder_unmatched->changes)) { - unmatched_changes = folder_unmatched->changes; - folder_unmatched->changes = camel_folder_change_info_new (); - } +static void +vee_folder_subfolder_changed (CamelVeeFolder *vfolder, + CamelFolder *subfolder, + CamelFolderChangeInfo *subfolder_changes, + GCancellable *cancellable, + GError **error) +{ + CamelVeeDataCache *data_cache; + CamelFolderChangeInfo *changes; + CamelFolder *v_folder; + CamelVeeSummary *vsummary; + gint ii; - camel_vee_folder_unlock (folder_unmatched, CAMEL_VEE_FOLDER_SUMMARY_LOCK); - } + g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder)); + g_return_if_fail (CAMEL_IS_FOLDER (subfolder)); + g_return_if_fail (subfolder_changes != NULL); - if (camel_folder_change_info_changed (vf->changes)) { - vf_changes = vf->changes; - vf->changes = camel_folder_change_info_new (); + camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + if (!g_list_find (vfolder->priv->subfolders, subfolder)){ + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + return; } + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + + changes = camel_folder_change_info_new (); + data_cache = vee_folder_get_data_cache (vfolder); + v_folder = CAMEL_FOLDER (vfolder); + vsummary = CAMEL_VEE_SUMMARY (v_folder->summary); - if (matches_changed || matches_added || changes->uid_removed->len || present) { - const gchar *full_name; - const gchar *unm_full_name = NULL; - CamelStore *parent_store; + camel_folder_freeze (v_folder); - parent_store = camel_folder_get_parent_store (folder); - full_name = camel_folder_get_full_name (folder); + for (ii = 0; ii < subfolder_changes->uid_removed->len; ii++) { + const gchar *orig_message_uid = subfolder_changes->uid_removed->pdata[ii]; - if (folder_unmatched) - unm_full_name = camel_folder_get_full_name (CAMEL_FOLDER (folder_unmatched)); + vee_folder_remove_unmatched (vfolder, vsummary, data_cache, changes, subfolder, orig_message_uid, TRUE); + } - camel_db_begin_transaction (parent_store->cdb_w, NULL); + if (subfolder_changes->uid_added->len + subfolder_changes->uid_changed->len > 0) { + GPtrArray *test_uids, *match; + gboolean my_match = FALSE; - if (m_added_l) - vfolder_add_remove_transaction (parent_store, full_name, &m_added_l, TRUE, NULL); - if (m_removed_l) - vfolder_add_remove_transaction (parent_store, full_name, &m_removed_l, FALSE, NULL); - if (unm_added_l) - vfolder_add_remove_transaction (parent_store, unm_full_name, &unm_added_l, TRUE, NULL); - if (unm_removed_l) - vfolder_add_remove_transaction (parent_store, unm_full_name, &unm_removed_l, FALSE, NULL); + test_uids = g_ptr_array_sized_new (subfolder_changes->uid_added->len + subfolder_changes->uid_changed->len); - camel_db_end_transaction (parent_store->cdb_w, NULL); - } + for (ii = 0; ii < subfolder_changes->uid_added->len; ii++) { + g_ptr_array_add (test_uids, subfolder_changes->uid_added->pdata[ii]); + } - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_SUMMARY_LOCK); + for (ii = 0; ii < subfolder_changes->uid_changed->len; ii++) { + g_ptr_array_add (test_uids, subfolder_changes->uid_changed->pdata[ii]); + } - /* Cleanup stuff on our folder */ - if (matches_added) - camel_folder_search_free (sub, matches_added); - if (present) - camel_folder_search_free (sub, present); + if (!vfolder->priv->expression) { + my_match = TRUE; + match = g_ptr_array_new (); + + if (vee_folder_is_unmatched (vfolder)) { + CamelVeeMessageInfoData *mi_data; + const gchar *vuid; + + /* all common from test_uids and stored uids + in the unmatched folder should be updated */ + for (ii = 0; ii < test_uids->len; ii++) { + mi_data = camel_vee_data_cache_get_message_info_data (data_cache, subfolder, test_uids->pdata[ii]); + if (!mi_data) + continue; + + vuid = camel_vee_message_info_data_get_vee_message_uid (mi_data); + if (camel_folder_summary_check_uid (v_folder->summary, vuid)) + g_ptr_array_add (match, (gpointer) camel_pstring_strdup (test_uids->pdata[ii])); + g_object_unref (mi_data); + } + } + } else { + /* sadly, if there are threads involved, then searching by uids doesn't work, + because just changed uids can be brought in by the thread condition */ + if (strstr (vfolder->priv->expression, "match-threads") != NULL) + match = camel_folder_search_by_expression (subfolder, vfolder->priv->expression, cancellable, NULL); + else + match = camel_folder_search_by_uids (subfolder, vfolder->priv->expression, test_uids, cancellable, NULL); + } - if (matches_changed) - camel_folder_search_free (sub, matches_changed); + if (match) { + GHashTable *with_uids; - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + /* uids are taken from the string pool, thus use direct hashes */ + with_uids = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) camel_pstring_free, NULL); + for (ii = 0; ii < test_uids->len; ii++) { + g_hash_table_insert (with_uids, (gpointer) camel_pstring_strdup (test_uids->pdata[ii]), GINT_TO_POINTER (1)); + } - /* cleanup the rest */ - if (newchanged) - g_ptr_array_free (newchanged, TRUE); + vee_folder_merge_matching (vfolder, subfolder, with_uids, match, changes, TRUE); - g_free (vuid); + g_hash_table_destroy (with_uids); + if (my_match) { + g_ptr_array_foreach (match, (GFunc) camel_pstring_free, NULL); + g_ptr_array_free (match, TRUE); + } else { + camel_folder_search_free (subfolder, match); + } + } - if (unmatched_changes) { - camel_folder_changed ( - CAMEL_FOLDER (folder_unmatched), unmatched_changes); - camel_folder_change_info_free (unmatched_changes); + g_ptr_array_free (test_uids, TRUE); } - /* Add to folders_changed if we need to call vee_folder_rebuild_folder, which - * could be the case for two reasons: - * - We changed the vfolder and it is not auto-updating. Need to re-sync. - * - Vfolder is correlating. Changes to non-matching source messages - * won't be processed here and won't show up in vf_changes but may - * still affect the vfolder contents (e.g., non-matching messages - * added to a matching thread), so we re-run the query on the whole - * source folder. (For match-threads, it may be enough to do this if - * changes->uid_added->len > 0, but I'm not completely sure and I'd - * rather be safe than sorry.) - */ - if ((vf_changes && (vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0) || correlating) { - camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_CHANGED_LOCK); - if (g_list_find (vf->priv->folders_changed, sub) == NULL) - vf->priv->folders_changed = g_list_prepend (vf->priv->folders_changed, sub); - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_CHANGED_LOCK); - } + camel_folder_thaw (v_folder); - if (vf_changes) { - camel_folder_changed (CAMEL_FOLDER (vf), vf_changes); - camel_folder_change_info_free (vf_changes); - } + if (camel_folder_change_info_changed (changes)) + camel_folder_changed (v_folder, changes); + camel_folder_change_info_free (changes); } static void @@ -652,8 +484,8 @@ vee_folder_process_changes (CamelSession *session, cancellable, _("Updating %s folder"), display_name); while ((data = g_async_queue_try_pop (change_queue)) != NULL) { - folder_changed_change (vee_folder, cancellable, data, error); - folder_changed_data_free (data); + vee_folder_subfolder_changed (vee_folder, data->subfolder, data->changes, cancellable, error); + vee_folder_changed_data_free (data); if (g_cancellable_is_cancelled (cancellable)) break; @@ -665,295 +497,41 @@ vee_folder_process_changes (CamelSession *session, } static void -subfolder_renamed_update (CamelVeeFolder *vf, - CamelFolder *sub, - gchar hash[8]) -{ - gint i; - CamelFolderChangeInfo *changes = NULL; - CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL; - GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL; - CamelFolderSummary *ssummary = sub->summary; - GPtrArray *known_uids; - - camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_SUMMARY_LOCK); - - camel_folder_summary_prepare_fetch_all (((CamelFolder *) vf)->summary, NULL); - - known_uids = camel_folder_summary_get_array (((CamelFolder *) vf)->summary); - for (i = 0; known_uids && i < known_uids->len; i++) { - CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *) camel_folder_summary_get (((CamelFolder *) vf)->summary, g_ptr_array_index (known_uids, i)); - CamelVeeMessageInfo *vinfo; - - if (mi == NULL) - continue; - - if (mi->orig_summary == ssummary) { - gchar *uid = (gchar *) camel_message_info_uid (mi); - gchar *oldkey; - gpointer oldval; - - camel_folder_change_info_remove_uid (vf->changes, uid); - camel_folder_summary_remove (((CamelFolder *) vf)->summary, (CamelMessageInfo *) mi); - - vinfo = vee_folder_add_uid (vf, sub, uid + 8, hash); - if (vinfo) { - camel_folder_change_info_add_uid (vf->changes, camel_message_info_uid (vinfo)); - - /* check unmatched uid's table for any matches */ - if (vf == folder_unmatched - && g_hash_table_lookup_extended (unmatched_uids, uid, (gpointer *) &oldkey, &oldval)) { - g_hash_table_remove (unmatched_uids, oldkey); - g_hash_table_insert (unmatched_uids, g_strdup (camel_message_info_uid (vinfo)), oldval); - g_free (oldkey); - } - - camel_message_info_free ((CamelMessageInfo *) vinfo); - } - } - - camel_message_info_free ((CamelMessageInfo *) mi); - } - - camel_folder_summary_free_array (known_uids); - - if (camel_folder_change_info_changed (vf->changes)) { - changes = vf->changes; - vf->changes = camel_folder_change_info_new (); - } - - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_SUMMARY_LOCK); - - if (changes) { - camel_folder_changed (CAMEL_FOLDER (vf), changes); - camel_folder_change_info_free (changes); - } -} - -static gint -vee_folder_rebuild_folder (CamelVeeFolder *vee_folder, - CamelFolder *source, - GError **error); - -static void -unmatched_check_uid (gchar *uidin, - gpointer value, - struct _update_data *u) -{ - gchar *uid; - gint n; - - uid = alloca (strlen (uidin) + 9); - memcpy (uid, u->hash, 8); - strcpy (uid + 8, uidin); - n = GPOINTER_TO_INT (g_hash_table_lookup (u->unmatched_uids, uid)); - if (n == 0) { - if (vee_folder_add_uid_test (u->folder_unmatched, u->source, uidin, u->hash)) - camel_folder_change_info_add_uid (u->folder_unmatched->changes, uid); - } else { - CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *) camel_folder_summary_get (((CamelFolder *) u->folder_unmatched)->summary, uid); - if (mi) { - if (u->message_uids != NULL) - g_queue_push_tail (u->message_uids, g_strdup (uid)); - - camel_folder_summary_remove_uid ( - ((CamelFolder *) u->folder_unmatched)->summary, uid); - camel_folder_change_info_remove_uid ( - u->folder_unmatched->changes, uid); - camel_message_info_free ((CamelMessageInfo *) mi); - } - } -} - -static void -folder_added_uid (gchar *uidin, - gpointer value, - struct _update_data *u) -{ - CamelVeeMessageInfo *mi; - gchar *oldkey; - gpointer oldval; - const gchar *uid; - gint n; - - mi = vee_folder_add_uid (u->vee_folder, u->source, uidin, u->hash); - if (mi == NULL) - return; - - uid = camel_message_info_uid (mi); - - if (u->message_uids != NULL) - g_queue_push_tail (u->message_uids, g_strdup (uid)); - - camel_folder_change_info_add_uid (u->vee_folder->changes, uid); - - if (!CAMEL_IS_VEE_FOLDER (u->source) && u->unmatched_uids != NULL) { - gboolean found_uid; - - found_uid = g_hash_table_lookup_extended ( - u->unmatched_uids, uid, - (gpointer *) &oldkey, &oldval); - - if (found_uid) { - n = GPOINTER_TO_INT (oldval); - g_hash_table_insert ( - u->unmatched_uids, - oldkey, GINT_TO_POINTER (n + 1)); - } else { - g_hash_table_insert ( - u->unmatched_uids, - g_strdup (uid), GINT_TO_POINTER (1)); - } - } - - camel_message_info_free ((CamelMessageInfo *) mi); -} - -static CamelFIRecord * -summary_header_to_db (CamelFolderSummary *s, - GError **error) +subfolder_changed (CamelFolder *subfolder, + CamelFolderChangeInfo *changes, + CamelVeeFolder *vfolder) { - CamelFIRecord * record = g_new0 (CamelFIRecord, 1); - const gchar *full_name; - - /* We do this during write, so lets use write handle, though we gonna read */ - full_name = camel_folder_get_full_name (camel_folder_summary_get_folder (s)); + g_return_if_fail (vfolder != NULL); + g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder)); - record->folder_name = g_strdup (full_name); + camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_CHANGED_LOCK); + if (g_hash_table_lookup (vfolder->priv->ignore_changed, subfolder) || + !camel_vee_folder_get_auto_update (vfolder)) { + CamelFolderChangeInfo *my_changes; - /* we always write out the current version */ - record->version = 13; /* FIXME: CAMEL_FOLDER_SUMMARY_VERSION; */ - record->flags = s->flags; - record->nextuid = camel_folder_summary_get_next_uid (s); - record->time = s->time; + g_hash_table_remove (vfolder->priv->ignore_changed, subfolder); - record->saved_count = camel_folder_summary_count (s); - record->junk_count = camel_folder_summary_get_junk_count (s); - record->deleted_count = camel_folder_summary_get_deleted_count (s); - record->unread_count = camel_folder_summary_get_unread_count (s); - record->visible_count = camel_folder_summary_get_visible_count (s); - record->jnd_count = camel_folder_summary_get_junk_not_deleted_count (s); + my_changes = g_hash_table_lookup (vfolder->priv->skipped_changes, subfolder); + if (!my_changes) + my_changes = camel_folder_change_info_new (); + camel_folder_change_info_cat (my_changes, changes); + g_hash_table_insert (vfolder->priv->skipped_changes, subfolder, my_changes); - return record; -} - -static void -folder_changed (CamelFolder *sub, - CamelFolderChangeInfo *changes, - CamelVeeFolder *vee_folder) -{ - CamelVeeFolderClass *class; + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_CHANGED_LOCK); - g_return_if_fail (vee_folder != NULL); - g_return_if_fail (CAMEL_IS_VEE_FOLDER (vee_folder)); - - camel_vee_folder_lock (vee_folder, CAMEL_VEE_FOLDER_CHANGED_LOCK); - if (g_hash_table_lookup (vee_folder->priv->ignore_changed, sub)) { - CamelFolderChangeInfo *old_changes; - g_hash_table_remove (vee_folder->priv->ignore_changed, sub); - - old_changes = g_hash_table_lookup (vee_folder->priv->skipped_changes, sub); - if (!old_changes) - old_changes = camel_folder_change_info_new (); - camel_folder_change_info_cat (old_changes, changes); - g_hash_table_insert (vee_folder->priv->skipped_changes, sub, old_changes); - camel_vee_folder_unlock (vee_folder, CAMEL_VEE_FOLDER_CHANGED_LOCK); return; } - camel_vee_folder_unlock (vee_folder, CAMEL_VEE_FOLDER_CHANGED_LOCK); + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_CHANGED_LOCK); - class = CAMEL_VEE_FOLDER_GET_CLASS (vee_folder); - class->folder_changed (vee_folder, sub, changes); + CAMEL_VEE_FOLDER_GET_CLASS (vfolder)->folder_changed (vfolder, subfolder, changes); } /* track vanishing folders */ static void -subfolder_deleted (CamelFolder *folder, - CamelVeeFolder *vee_folder) +subfolder_deleted (CamelFolder *subfolder, + CamelVeeFolder *vfolder) { - camel_vee_folder_remove_folder (vee_folder, folder); -} - -static void -folder_renamed (CamelFolder *sub, - const gchar *old, - CamelVeeFolder *vee_folder) -{ - CamelVeeFolderClass *class; - - class = CAMEL_VEE_FOLDER_GET_CLASS (vee_folder); - class->folder_renamed (vee_folder, sub, old); -} - -static void -vee_folder_stop_folder (CamelVeeFolder *vf, - CamelFolder *sub, - GCancellable *cancellable) -{ - CamelVeeFolderPrivate *p = CAMEL_VEE_FOLDER_GET_PRIVATE (vf); - gint i; - CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL; - - camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - - camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_CHANGED_LOCK); - p->folders_changed = g_list_remove (p->folders_changed, sub); - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_CHANGED_LOCK); - - if (g_list_find (p->folders, sub) == NULL) { - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - return; - } - - g_signal_handlers_disconnect_by_func (sub, folder_changed, vf); - g_signal_handlers_disconnect_by_func (sub, subfolder_deleted, vf); - g_signal_handlers_disconnect_by_func (sub, folder_renamed, vf); - - p->folders = g_list_remove (p->folders, sub); - - /* undo the freeze state that we have imposed on this source folder */ - camel_folder_lock (CAMEL_FOLDER (vf), CAMEL_FOLDER_CHANGE_LOCK); - for (i = 0; i < camel_folder_get_frozen_count ((CamelFolder *) vf); i++) - camel_folder_thaw (sub); - camel_folder_unlock (CAMEL_FOLDER (vf), CAMEL_FOLDER_CHANGE_LOCK); - - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - - if (folder_unmatched != NULL) { - CamelVeeFolderPrivate *up = CAMEL_VEE_FOLDER_GET_PRIVATE (folder_unmatched); - - camel_vee_folder_lock (folder_unmatched, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - /* if folder deleted, then blow it away from unmatched always, and remove all refs to it */ - if (sub->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED) { - while (g_list_find (up->folders, sub)) { - up->folders = g_list_remove (up->folders, sub); - g_object_unref (sub); - - /* undo the freeze state that Unmatched has imposed on this source folder */ - camel_folder_lock (CAMEL_FOLDER (folder_unmatched), CAMEL_FOLDER_CHANGE_LOCK); - for (i = 0; i < camel_folder_get_frozen_count ((CamelFolder *) folder_unmatched); i++) - camel_folder_thaw (sub); - camel_folder_unlock (CAMEL_FOLDER (folder_unmatched), CAMEL_FOLDER_CHANGE_LOCK); - } - } else if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) { - if (g_list_find (up->folders, sub) != NULL) { - up->folders = g_list_remove (up->folders, sub); - g_object_unref (sub); - - /* undo the freeze state that Unmatched has imposed on this source folder */ - camel_folder_lock (CAMEL_FOLDER (folder_unmatched), CAMEL_FOLDER_CHANGE_LOCK); - for (i = 0; i < camel_folder_get_frozen_count ((CamelFolder *) folder_unmatched); i++) - camel_folder_thaw (sub); - camel_folder_unlock (CAMEL_FOLDER (folder_unmatched), CAMEL_FOLDER_CHANGE_LOCK); - } - } - camel_vee_folder_unlock (folder_unmatched, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - } - - if (CAMEL_IS_VEE_FOLDER (sub)) - return; - - g_object_unref (sub); + camel_vee_folder_remove_folder (vfolder, subfolder, NULL); } static void @@ -965,46 +543,17 @@ vee_folder_dispose (GObject *object) /* parent's class frees summary on dispose, thus depend on it */ if (folder->summary) { - CamelVeeFolder *vf; - CamelVeeFolder *folder_unmatched; - GList *node; - CamelFIRecord * record; - - vf = CAMEL_VEE_FOLDER (object); - vf->priv->destroyed = TRUE; + CamelVeeFolder *vfolder; - folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL; + vfolder = CAMEL_VEE_FOLDER (object); + vfolder->priv->destroyed = TRUE; - /* Save the counts to DB */ - if (!vf->deleted) { - CamelFolder *folder; - CamelStore *parent_store; - - folder = CAMEL_FOLDER (vf); - parent_store = camel_folder_get_parent_store (folder); - record = summary_header_to_db (folder->summary, NULL); - - camel_db_begin_transaction (parent_store->cdb_w, NULL); - camel_db_write_folder_info_record (parent_store->cdb_w, record, NULL); - camel_db_end_transaction (parent_store->cdb_w, NULL); - - g_free (record->folder_name); - g_free (record); - } - - /* This may invoke sub-classes with partially destroyed state, they must deal with this */ - if (vf == folder_unmatched) { - for (node = vf->priv->folders; node; node = g_list_next (node)) - g_object_unref (node->data); - } else { - /* FIXME[disk-summary] See if it is really reqd */ - camel_folder_freeze ((CamelFolder *) vf); - while (vf->priv->folders) { - CamelFolder *f = vf->priv->folders->data; - vee_folder_stop_folder (vf, f, NULL); - } - camel_folder_thaw ((CamelFolder *) vf); + camel_folder_freeze ((CamelFolder *) vfolder); + while (vfolder->priv->subfolders) { + CamelFolder *subfolder = vfolder->priv->subfolders->data; + camel_vee_folder_remove_folder (vfolder, subfolder, NULL); } + camel_folder_thaw ((CamelFolder *) vfolder); } /* Chain up to parent's dispose () method. */ @@ -1026,33 +575,69 @@ vee_folder_finalize (GObject *object) vf = CAMEL_VEE_FOLDER (object); - g_free (vf->expression); + g_free (vf->priv->expression); - g_list_free (vf->priv->folders); - g_list_free (vf->priv->folders_changed); - - camel_folder_change_info_free (vf->changes); - g_object_unref (vf->search); + g_list_free (vf->priv->subfolders); g_hash_table_foreach (vf->priv->skipped_changes, free_change_info_cb, NULL); - g_mutex_free (vf->priv->summary_lock); - g_mutex_free (vf->priv->subfolder_lock); - g_mutex_free (vf->priv->changed_lock); - g_hash_table_destroy (vf->hashes); + g_static_rec_mutex_free (&vf->priv->summary_lock); + g_static_rec_mutex_free (&vf->priv->subfolder_lock); + g_static_rec_mutex_free (&vf->priv->changed_lock); g_hash_table_destroy (vf->priv->ignore_changed); g_hash_table_destroy (vf->priv->skipped_changes); + g_hash_table_destroy (vf->priv->unmatched_add_changed); + g_hash_table_destroy (vf->priv->unmatched_remove_changed); g_async_queue_unref (vf->priv->change_queue); + if (vf->priv->vee_data_cache) + g_object_unref (vf->priv->vee_data_cache); + vf->priv->vee_data_cache = NULL; + /* Chain up to parent's finalize () method. */ G_OBJECT_CLASS (camel_vee_folder_parent_class)->finalize (object); } static void +vee_folder_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_AUTO_UPDATE: + g_value_set_boolean ( + value, camel_vee_folder_get_auto_update ( + CAMEL_VEE_FOLDER (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +vee_folder_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_AUTO_UPDATE: + camel_vee_folder_set_auto_update ( + CAMEL_VEE_FOLDER (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void vee_folder_propagate_skipped_changes (CamelVeeFolder *vf) { CamelVeeFolderClass *class; + CamelFolderChangeInfo *changes = NULL; GHashTableIter iter; gpointer psub, pchanges; @@ -1062,13 +647,47 @@ vee_folder_propagate_skipped_changes (CamelVeeFolder *vf) camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_CHANGED_LOCK); + /* this is for Unmatched folder only, other folders have unmatched_remove_changed always empty */ + if (g_hash_table_size (vf->priv->unmatched_add_changed) + + g_hash_table_size (vf->priv->unmatched_remove_changed) > 0) { + gpointer pkey, pvalue; + CamelVeeSummary *vsummary; + CamelFolder *v_folder; + + changes = camel_folder_change_info_new (); + v_folder = CAMEL_FOLDER (vf); + vsummary = CAMEL_VEE_SUMMARY (v_folder->summary); + + /* first remove ... */ + g_hash_table_iter_init (&iter, vf->priv->unmatched_remove_changed); + while (g_hash_table_iter_next (&iter, &pkey, &pvalue)) { + CamelVeeMessageInfoData *mi_data = pkey; + CamelVeeSubfolderData *sf_data; + CamelFolder *subfolder; + + sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data); + subfolder = camel_vee_subfolder_data_get_folder (sf_data); + vee_folder_note_unmatch_uid (vf, vsummary, subfolder, mi_data, changes); + } + g_hash_table_remove_all (vf->priv->unmatched_remove_changed); + + /* ... then add */ + g_hash_table_iter_init (&iter, vf->priv->unmatched_add_changed); + while (g_hash_table_iter_next (&iter, &pkey, &pvalue)) { + CamelVeeMessageInfoData *mi_data = pkey; + + vee_folder_note_added_uid (vf, vsummary, mi_data, changes, FALSE); + } + g_hash_table_remove_all (vf->priv->unmatched_add_changed); + } + g_hash_table_iter_init (&iter, vf->priv->skipped_changes); while (g_hash_table_iter_next (&iter, &psub, &pchanges)) { g_warn_if_fail (pchanges != NULL); if (!pchanges) continue; - if (g_list_find (vf->priv->folders, psub) != NULL) + if (g_list_find (vf->priv->subfolders, psub) != NULL) class->folder_changed (vf, psub, pchanges); camel_folder_change_info_free (pchanges); @@ -1077,6 +696,12 @@ vee_folder_propagate_skipped_changes (CamelVeeFolder *vf) g_hash_table_remove_all (vf->priv->skipped_changes); camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_CHANGED_LOCK); + + if (changes) { + if (camel_folder_change_info_changed (changes)) + camel_folder_changed (CAMEL_FOLDER (vf), changes); + camel_folder_change_info_free (changes); + } } static GPtrArray * @@ -1085,59 +710,15 @@ vee_folder_search_by_expression (CamelFolder *folder, GCancellable *cancellable, GError **error) { - GList *node; - GPtrArray *matches, *result = g_ptr_array_new (); - gchar *expr; - CamelVeeFolder *vf = (CamelVeeFolder *) folder; - CamelVeeFolderPrivate *p = vf->priv; - GHashTable *searched = g_hash_table_new (NULL, NULL); - CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL; - gboolean is_folder_unmatched = vf == folder_unmatched && folder_unmatched; - CamelFolderSummary *folder_unmatched_summary = NULL; - - vee_folder_propagate_skipped_changes (vf); + CamelFolderSearch *search; + GPtrArray *matches; - if (is_folder_unmatched) { - expr = g_strdup (expression); - folder_unmatched_summary = ((CamelFolder *) folder_unmatched)->summary; - } else { - expr = g_strdup_printf ("(and %s %s)", vf->expression ? vf->expression : "", expression); - } + search = camel_folder_search_new (); + camel_folder_search_set_folder (search, folder); + matches = camel_folder_search_search (search, expression, NULL, cancellable, error); + g_object_unref (search); - node = p->folders; - while (node && !g_cancellable_is_cancelled (cancellable)) { - CamelFolder *f = node->data; - gint i; - gchar hash[8]; - - /* make sure we only search each folder once - for unmatched folder to work right */ - if (g_hash_table_lookup (searched, f) == NULL) { - camel_vee_folder_hash_folder (f, hash); - matches = camel_folder_search_by_expression (f, expr, cancellable, NULL); - if (matches) { - for (i = 0; i < matches->len; i++) { - gchar *uid = matches->pdata[i], *vuid; - - vuid = g_malloc (strlen (uid) + 9); - memcpy (vuid, hash, 8); - strcpy (vuid + 8, uid); - - if (!is_folder_unmatched || camel_folder_summary_check_uid (folder_unmatched_summary, vuid)) - g_ptr_array_add (result, (gpointer) camel_pstring_strdup (vuid)); - g_free (vuid); - } - camel_folder_search_free (f, matches); - } - g_hash_table_insert (searched, f, f); - } - node = g_list_next (node); - } - - g_free (expr); - - g_hash_table_destroy (searched); - d (printf ("returning %d\n", result->len)); - return result; + return matches; } static GPtrArray * @@ -1147,64 +728,18 @@ vee_folder_search_by_uids (CamelFolder *folder, GCancellable *cancellable, GError **error) { - GList *node; - GPtrArray *matches, *result = g_ptr_array_new (); - GPtrArray *folder_uids = g_ptr_array_new (); - gchar *expr; - CamelVeeFolder *vf = (CamelVeeFolder *) folder; - CamelVeeFolderPrivate *p = vf->priv; - GHashTable *searched = g_hash_table_new (NULL, NULL); - - vee_folder_propagate_skipped_changes (vf); - - camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + CamelFolderSearch *search; + GPtrArray *matches; - expr = g_strdup_printf ("(and %s %s)", vf->expression ? vf->expression : "", expression); - node = p->folders; - while (node && !g_cancellable_is_cancelled (cancellable)) { - CamelFolder *f = node->data; - gint i; - gchar hash[8]; + if (!uids || uids->len == 0) + return g_ptr_array_new (); - /* make sure we only search each folder once - for unmatched folder to work right */ - if (g_hash_table_lookup (searched, f) == NULL) { - camel_vee_folder_hash_folder (f, hash); + search = camel_folder_search_new (); + camel_folder_search_set_folder (search, folder); + matches = camel_folder_search_search (search, expression, uids, cancellable, error); + g_object_unref (search); - /* map the vfolder uid's to the source folder uid's first */ - g_ptr_array_set_size (folder_uids, 0); - for (i = 0; i < uids->len; i++) { - gchar *uid = uids->pdata[i]; - - if (strlen (uid) >= 8 && strncmp (uid, hash, 8) == 0) - g_ptr_array_add (folder_uids, uid + 8); - } - if (folder_uids->len > 0) { - matches = camel_folder_search_by_uids (f, expr, folder_uids, cancellable, error); - if (matches) { - for (i = 0; i < matches->len; i++) { - gchar *uid = matches->pdata[i], *vuid; - - vuid = g_malloc (strlen (uid) + 9); - memcpy (vuid, hash, 8); - strcpy (vuid + 8, uid); - g_ptr_array_add (result, (gpointer) camel_pstring_strdup (vuid)); - g_free (vuid); - } - camel_folder_search_free (f, matches); - } - } - g_hash_table_insert (searched, f, f); - } - node = g_list_next (node); - } - - g_free (expr); - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - - g_hash_table_destroy (searched); - g_ptr_array_free (folder_uids, TRUE); - - return result; + return matches; } static guint32 @@ -1213,82 +748,64 @@ vee_folder_count_by_expression (CamelFolder *folder, GCancellable *cancellable, GError **error) { - GList *node; - gchar *expr; - guint32 count = 0; - CamelVeeFolder *vf = (CamelVeeFolder *) folder; - CamelVeeFolderPrivate *p = vf->priv; - GHashTable *searched = g_hash_table_new (NULL, NULL); - CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL; - - vee_folder_propagate_skipped_changes (vf); - - if (vf != folder_unmatched) - expr = g_strdup_printf ("(and %s %s)", vf->expression ? vf->expression : "", expression); - else - expr = g_strdup (expression); + CamelFolderSearch *search; + guint32 count; - node = p->folders; - while (node && !g_cancellable_is_cancelled (cancellable)) { - CamelFolder *f = node->data; + search = camel_folder_search_new (); + camel_folder_search_set_folder (search, folder); + count = camel_folder_search_count (search, expression, cancellable, error); + g_object_unref (search); - /* make sure we only search each folder once - for unmatched folder to work right */ - if (g_hash_table_lookup (searched, f) == NULL) { - count += camel_folder_count_by_expression (f, expr, cancellable, NULL); - g_hash_table_insert (searched, f, f); - } - node = g_list_next (node); - } - - g_free (expr); - - g_hash_table_destroy (searched); return count; } static void +vee_folder_search_free (CamelFolder *folder, + GPtrArray *result) +{ + camel_folder_search_free_result (NULL, result); +} + +static void vee_folder_delete (CamelFolder *folder) { - CamelVeeFolderPrivate *p = CAMEL_VEE_FOLDER_GET_PRIVATE (folder); + CamelVeeFolder *vfolder; + + g_return_if_fail (CAMEL_IS_VEE_FOLDER (folder)); - /* NB: this is never called on UNMTACHED */ + vfolder = CAMEL_VEE_FOLDER (folder); - camel_vee_folder_lock (CAMEL_VEE_FOLDER (folder), CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - while (p->folders) { - CamelFolder *f = p->folders->data; + camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + while (vfolder->priv->subfolders) { + CamelFolder *subfolder = vfolder->priv->subfolders->data; + + g_object_ref (subfolder); + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - g_object_ref (f); - camel_vee_folder_unlock (CAMEL_VEE_FOLDER (folder), CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + camel_vee_folder_remove_folder (vfolder, subfolder, NULL); + g_object_unref (subfolder); - camel_vee_folder_remove_folder ((CamelVeeFolder *) folder, f); - g_object_unref (f); - camel_vee_folder_lock (CAMEL_VEE_FOLDER (folder), CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); } - camel_vee_folder_unlock (CAMEL_VEE_FOLDER (folder), CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); ((CamelFolderClass *) camel_vee_folder_parent_class)->delete (folder); - ((CamelVeeFolder *) folder)->deleted = TRUE; } static void vee_folder_freeze (CamelFolder *folder) { - CamelVeeFolder *vfolder = (CamelVeeFolder *) folder; - CamelVeeFolderPrivate *p = vfolder->priv; - GList *node; + CamelVeeFolder *vfolder = CAMEL_VEE_FOLDER (folder); - camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + if (vfolder->priv->parent_vee_store && + !vee_folder_is_unmatched (vfolder)) { + CamelVeeFolder *unmatched_folder; - node = p->folders; - while (node) { - CamelFolder *f = node->data; - - camel_folder_freeze (f); - node = node->next; + unmatched_folder = camel_vee_store_get_unmatched_folder (vfolder->priv->parent_vee_store); + if (unmatched_folder) + camel_folder_freeze (CAMEL_FOLDER (unmatched_folder)); } - camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - /* call parent implementation */ CAMEL_FOLDER_CLASS (camel_vee_folder_parent_class)->freeze (folder); } @@ -1296,21 +813,16 @@ vee_folder_freeze (CamelFolder *folder) static void vee_folder_thaw (CamelFolder *folder) { - CamelVeeFolder *vfolder = (CamelVeeFolder *) folder; - CamelVeeFolderPrivate *p = vfolder->priv; - GList *node; + CamelVeeFolder *vfolder = CAMEL_VEE_FOLDER (folder); - camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + if (vfolder->priv->parent_vee_store && + !vee_folder_is_unmatched (vfolder)) { + CamelVeeFolder *unmatched_folder; - node = p->folders; - while (node) { - CamelFolder *f = node->data; - - camel_folder_thaw (f); - node = node->next; - } - - camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + unmatched_folder = camel_vee_store_get_unmatched_folder (vfolder->priv->parent_vee_store); + if (unmatched_folder) + camel_folder_thaw (CAMEL_FOLDER (unmatched_folder)); + } /* call parent implementation */ CAMEL_FOLDER_CLASS (camel_vee_folder_parent_class)->thaw (folder); @@ -1372,32 +884,11 @@ vee_folder_refresh_info_sync (CamelFolder *folder, GError **error) { CamelVeeFolder *vf = (CamelVeeFolder *) folder; - CamelVeeFolderPrivate *p = vf->priv; - GList *node, *list; - gboolean success = TRUE; vee_folder_propagate_skipped_changes (vf); + vee_folder_rebuild_all (vf, cancellable); - camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_CHANGED_LOCK); - list = p->folders_changed; - p->folders_changed = NULL; - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_CHANGED_LOCK); - - node = list; - while (node) { - CamelFolder *f = node->data; - - if (camel_vee_folder_rebuild_folder (vf, f, error) == -1) { - success = FALSE; - break; - } - - node = node->next; - } - - g_list_free (list); - - return success; + return TRUE; } static gboolean @@ -1406,70 +897,44 @@ vee_folder_synchronize_sync (CamelFolder *folder, GCancellable *cancellable, GError **error) { - CamelVeeFolder *vf = (CamelVeeFolder *) folder; - CamelVeeFolderPrivate *p = vf->priv; - GList *node; + CamelVeeFolder *vfolder = (CamelVeeFolder *) folder; + gboolean res = TRUE; + GList *iter; - vee_folder_propagate_skipped_changes (vf); + g_return_val_if_fail (CAMEL_IS_VEE_FOLDER (folder), FALSE); - camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + vee_folder_propagate_skipped_changes (vfolder); - node = p->folders; - while (node) { + /* basically no-op here, especially do not call synchronize on subfolders + if not expunging, they are responsible for themselfs */ + if (!expunge || + vee_folder_is_unmatched (vfolder)) + return TRUE; + + camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + + for (iter = vfolder->priv->subfolders; iter && !g_cancellable_is_cancelled (cancellable); iter = iter->next) { GError *local_error = NULL; - CamelFolder *f = node->data; + CamelFolder *subfolder = iter->data; - if (!camel_folder_synchronize_sync (f, expunge, cancellable, &local_error)) { + if (!camel_folder_synchronize_sync (subfolder, expunge, cancellable, &local_error)) { if (local_error && strncmp (local_error->message, "no such table", 13) != 0 && error && !*error) { const gchar *desc; - desc = camel_folder_get_description (f); - g_warning ("%s", local_error->message); + desc = camel_folder_get_description (subfolder); g_propagate_prefixed_error ( error, local_error, _("Error storing '%s': "), desc); + + res = FALSE; } else g_clear_error (&local_error); } - - /* auto update vfolders shouldn't need a rebuild */ -/* if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0 */ -/* && camel_vee_folder_rebuild_folder (vf, f, ex) == -1) */ -/* break; */ - - node = node->next; } - if (!CAMEL_IS_VTRASH_FOLDER (vf)) { - /* Cleanup Junk/Trash uids */ - CamelStore *parent_store; - const gchar *full_name; - GList *del = NULL; - gint i; - GPtrArray *known_uids; - - camel_folder_summary_prepare_fetch_all (folder->summary, NULL); - known_uids = camel_folder_summary_get_array (folder->summary); - for (i = 0; known_uids && i < known_uids->len; i++) { - CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *) camel_folder_summary_get (folder->summary, g_ptr_array_index (known_uids, i)); - if (mi->old_flags & CAMEL_MESSAGE_DELETED) { - del = g_list_prepend (del, (gpointer) camel_pstring_strdup (((CamelMessageInfo *) mi)->uid)); - camel_folder_summary_remove_uid (folder->summary, ((CamelMessageInfo *) mi)->uid); - - } - camel_message_info_free (mi); - } - camel_folder_summary_free_array (known_uids); - - full_name = camel_folder_get_full_name (folder); - parent_store = camel_folder_get_parent_store (folder); - camel_db_delete_vuids (parent_store->cdb_w, full_name, "", del, NULL); - g_list_foreach (del, (GFunc) camel_pstring_free, NULL); - g_list_free (del); - } - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - return TRUE; + return res; } static gboolean @@ -1492,489 +957,124 @@ static void vee_folder_set_expression (CamelVeeFolder *vee_folder, const gchar *query) { - CamelVeeFolderPrivate *p = CAMEL_VEE_FOLDER_GET_PRIVATE (vee_folder); - GList *node; - camel_vee_folder_lock (vee_folder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); /* no change, do nothing */ - if ((vee_folder->expression && query && strcmp (vee_folder->expression, query) == 0) - || (vee_folder->expression == NULL && query == NULL)) { + if ((vee_folder->priv->expression && query && strcmp (vee_folder->priv->expression, query) == 0) + || (vee_folder->priv->expression == NULL && query == NULL)) { camel_vee_folder_unlock (vee_folder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); return; } - /* Recreate the table when the query changes, only if we are not setting it first */ - if (vee_folder->expression) { - CamelFolderSummary *summary; - CamelStore *parent_store; - CamelFolder *folder; - const gchar *full_name; - - folder = CAMEL_FOLDER (vee_folder); - full_name = camel_folder_get_full_name (folder); - parent_store = camel_folder_get_parent_store (folder); - summary = folder->summary; - - camel_folder_summary_clear (summary, NULL); - camel_db_recreate_vfolder (parent_store->cdb_w, full_name, NULL); - } - - g_free (vee_folder->expression); + g_free (vee_folder->priv->expression); if (query) - vee_folder->expression = g_strdup (query); - - node = p->folders; - while (node) { - CamelFolder *f = node->data; - - if (camel_vee_folder_rebuild_folder (vee_folder, f, NULL) == -1) - break; - - node = node->next; - } + vee_folder->priv->expression = g_strdup (query); - camel_vee_folder_lock (vee_folder, CAMEL_VEE_FOLDER_CHANGED_LOCK); - g_list_free (p->folders_changed); - p->folders_changed = NULL; - camel_vee_folder_unlock (vee_folder, CAMEL_VEE_FOLDER_CHANGED_LOCK); + vee_folder_rebuild_all (vee_folder, NULL); camel_vee_folder_unlock (vee_folder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); } static void -vee_folder_add_folder (CamelVeeFolder *vee_folder, - CamelFolder *sub) +vee_folder_rebuild_folder (CamelVeeFolder *vfolder, + CamelFolder *subfolder, + GCancellable *cancellable) { - vee_folder_rebuild_folder (vee_folder, sub, NULL); -} - -static void -vee_folder_remove_folder_helper (CamelVeeFolder *vf, - CamelFolder *source) -{ - gint i, n, still = FALSE; - gchar *oldkey; - CamelFolder *folder = (CamelFolder *) vf; - gchar hash[8]; - CamelFolderChangeInfo *vf_changes = NULL, *unmatched_changes = NULL; - gpointer oldval; - CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL; - GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL; - CamelFolderSummary *ssummary = source->summary; - gint killun = FALSE; - GPtrArray *known_uids; - - if (vf == folder_unmatched) - return; - - if ((source->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED)) - killun = TRUE; - - camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_SUMMARY_LOCK); - - if (folder_unmatched != NULL) { - /* check if this folder is still to be part of unmatched */ - if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !killun) { - camel_vee_folder_lock (folder_unmatched, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - still = g_list_find (CAMEL_VEE_FOLDER_GET_PRIVATE (folder_unmatched)->folders, source) != NULL; - camel_vee_folder_unlock (folder_unmatched, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - camel_vee_folder_hash_folder (source, hash); - } - - camel_vee_folder_lock (folder_unmatched, CAMEL_VEE_FOLDER_SUMMARY_LOCK); - - /* See if we just blow all uid's from this folder away from unmatched, regardless */ - if (killun) { - camel_folder_summary_prepare_fetch_all (((CamelFolder *) folder_unmatched)->summary, NULL); - known_uids = camel_folder_summary_get_array (((CamelFolder *) folder_unmatched)->summary); - for (i = 0; known_uids && i < known_uids->len; i++) { - CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *) - camel_folder_summary_get (((CamelFolder *) folder_unmatched)->summary, g_ptr_array_index (known_uids, i)); - - if (mi) { - if (mi->orig_summary == ssummary) { - camel_folder_change_info_remove_uid (folder_unmatched->changes, camel_message_info_uid (mi)); - camel_folder_summary_remove_uid (((CamelFolder *) folder_unmatched)->summary, camel_message_info_uid (mi)); - } - camel_message_info_free ((CamelMessageInfo *) mi); - } - } - camel_folder_summary_free_array (known_uids); - } - } - - /*FIXME: This can be optimized a lot like, searching for UID in the summary uids */ - camel_folder_summary_prepare_fetch_all (folder->summary, NULL); - known_uids = camel_folder_summary_get_array (folder->summary); - for (i = 0; known_uids && i < known_uids->len; i++) { - CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *) camel_folder_summary_get (folder->summary, g_ptr_array_index (known_uids, i)); - if (mi) { - if (mi->orig_summary == ssummary) { - const gchar *uid = camel_message_info_uid (mi); - - camel_folder_change_info_remove_uid (vf->changes, uid); - camel_folder_summary_remove_uid (folder->summary, uid); - - if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && folder_unmatched != NULL) { - if (still) { - if (g_hash_table_lookup_extended (unmatched_uids, uid, (gpointer *) &oldkey, &oldval)) { - n = GPOINTER_TO_INT (oldval); - if (n == 1) { - g_hash_table_remove (unmatched_uids, oldkey); - if (vee_folder_add_uid_test (folder_unmatched, source, oldkey + 8, hash)) { - camel_folder_change_info_add_uid (folder_unmatched->changes, oldkey); - } - g_free (oldkey); - } else { - g_hash_table_insert (unmatched_uids, oldkey, GINT_TO_POINTER (n - 1)); - } - } - } else { - if (g_hash_table_lookup_extended (unmatched_uids, camel_message_info_uid (mi), (gpointer *) &oldkey, &oldval)) { - g_hash_table_remove (unmatched_uids, oldkey); - g_free (oldkey); - } - } - } - } - camel_message_info_free ((CamelMessageInfo *) mi); - } - } - camel_folder_summary_free_array (known_uids); - - if (folder_unmatched) { - if (camel_folder_change_info_changed (folder_unmatched->changes)) { - unmatched_changes = folder_unmatched->changes; - folder_unmatched->changes = camel_folder_change_info_new (); - } - - camel_vee_folder_unlock (folder_unmatched, CAMEL_VEE_FOLDER_SUMMARY_LOCK); - } - - if (camel_folder_change_info_changed (vf->changes)) { - vf_changes = vf->changes; - vf->changes = camel_folder_change_info_new (); - } + CamelFolderChangeInfo *changes; + CamelFolder *v_folder; - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_SUMMARY_LOCK); + v_folder = CAMEL_FOLDER (vfolder); + changes = camel_folder_change_info_new (); - if (unmatched_changes) { - camel_folder_changed ( - CAMEL_FOLDER (folder_unmatched), unmatched_changes); - camel_folder_change_info_free (unmatched_changes); - } + camel_folder_freeze (v_folder); + vee_folder_rebuild_folder_with_changes (vfolder, subfolder, changes, cancellable); + camel_folder_thaw (v_folder); - if (vf_changes) { - camel_folder_changed (CAMEL_FOLDER (vf), vf_changes); - camel_folder_change_info_free (vf_changes); - } + if (camel_folder_change_info_changed (changes)) + camel_folder_changed (CAMEL_FOLDER (vfolder), changes); + camel_folder_change_info_free (changes); } static void -vee_folder_remove_folder (CamelVeeFolder *vee_folder, - CamelFolder *sub) +vee_folder_add_folder (CamelVeeFolder *vfolder, + CamelFolder *subfolder, + GCancellable *cancellable) { - gchar *shash, hash[8]; - CamelVeeFolder *folder_unmatched = vee_folder->parent_vee_store ? vee_folder->parent_vee_store->folder_unmatched : NULL; - - camel_vee_folder_hash_folder (sub, hash); - vee_folder_remove_folder_helper (vee_folder, sub); - shash = g_strdup_printf ( - "%c%c%c%c%c%c%c%c", - hash[0], hash[1], hash[2], hash[3], - hash[4], hash[5], hash[6], hash[7]); - if (g_hash_table_lookup (vee_folder->hashes, shash)) { - g_hash_table_remove (vee_folder->hashes, shash); - } - - if (folder_unmatched && g_hash_table_lookup (folder_unmatched->hashes, shash)) { - g_hash_table_remove (folder_unmatched->hashes, shash); - } - - g_free (shash); + if (vfolder->priv->parent_vee_store) + camel_vee_store_note_subfolder_used (vfolder->priv->parent_vee_store, subfolder, vfolder); + vee_folder_rebuild_folder (vfolder, subfolder, cancellable); } -static gint -vee_folder_rebuild_folder (CamelVeeFolder *vee_folder, - CamelFolder *source, - GError **error) +static gboolean +vee_folder_remove_from_unmatched_changed_cb (gpointer key, + gpointer value, + gpointer user_data) { - GPtrArray *match = NULL, *all; - GHashTable *allhash, *matchhash, *fullhash; - GList *del_list = NULL; - CamelFolder *folder = (CamelFolder *) vee_folder; - gint i, n, count; - struct _update_data u; - CamelFolderChangeInfo *vee_folder_changes = NULL, *unmatched_changes = NULL; - CamelVeeFolder *folder_unmatched = vee_folder->parent_vee_store ? vee_folder->parent_vee_store->folder_unmatched : NULL; - GHashTable *unmatched_uids = vee_folder->parent_vee_store ? vee_folder->parent_vee_store->unmatched_uids : NULL; - CamelFolderSummary *ssummary = source->summary; - gboolean rebuilded = FALSE; - gchar *shash; - GPtrArray *known_uids; - - /* Since the source of a correlating vfolder has to be requeried in - * full every time it changes, caching the results in the db is not - * worth the effort. Thus, DB use is conditioned on !correlating. */ - gboolean correlating = vee_folder->expression && expression_is_correlating (vee_folder->expression); - - if (vee_folder == folder_unmatched) - return 0; - - camel_vee_folder_hash_folder (source, u.hash); - shash = g_strdup_printf ("%c%c%c%c%c%c%c%c", u.hash[0], u.hash[1], u.hash[2], u.hash[3], u.hash[4], u.hash[5], u.hash[6], u.hash[7]); - if (!g_hash_table_lookup (vee_folder->hashes, shash)) { - g_hash_table_insert (vee_folder->hashes, g_strdup (shash), source->summary); - } - if (folder_unmatched && !g_hash_table_lookup (folder_unmatched->hashes, shash)) { - g_hash_table_insert (folder_unmatched->hashes, g_strdup (shash), source->summary); - } - - /* if we have no expression, or its been cleared, then act as if no matches */ - if (vee_folder->expression == NULL) { - match = g_ptr_array_new (); - } else { - if (!correlating) { - /* Load the folder results from the DB. */ - match = camel_vee_summary_get_ids ((CamelVeeSummary *) folder->summary, u.hash); - } - if (correlating || - /* We take this to mean the results have not been cached. - * XXX: It will also trigger if the result set is empty. */ - match == NULL) { - match = camel_folder_search_by_expression (source, vee_folder->expression, NULL, error); - if (match == NULL) /* Search failed */ - return 0; - rebuilded = TRUE; - } - - } - - u.source = source; - u.vee_folder = vee_folder; - u.folder_unmatched = folder_unmatched; - u.unmatched_uids = unmatched_uids; - u.rebuilt = rebuilded; - u.correlating = correlating; - - camel_vee_folder_lock (vee_folder, CAMEL_VEE_FOLDER_SUMMARY_LOCK); - - /* we build 2 hash tables, one for all uid's not matched, the - * other for all matched uid's, we just ref the real memory */ - matchhash = g_hash_table_new (g_str_hash, g_str_equal); - for (i = 0; i < match->len; i++) - g_hash_table_insert (matchhash, match->pdata[i], GINT_TO_POINTER (1)); - - allhash = g_hash_table_new (g_str_hash, g_str_equal); - fullhash = g_hash_table_new (g_str_hash, g_str_equal); - all = camel_folder_summary_get_array (source->summary); - for (i = 0; i < all->len; i++) { - if (g_hash_table_lookup (matchhash, all->pdata[i]) == NULL) - g_hash_table_insert (allhash, all->pdata[i], GINT_TO_POINTER (1)); - g_hash_table_insert (fullhash, all->pdata[i], GINT_TO_POINTER (1)); - - } - /* remove uids that can't be found in the source folder */ - count = match->len; - for (i = 0; i < count; i++) { - if (!g_hash_table_lookup (fullhash, match->pdata[i])) { - g_hash_table_remove (matchhash, match->pdata[i]); - del_list = g_list_prepend (del_list, match->pdata[i]); /* Free the original */ - g_ptr_array_remove_index_fast (match, i); - i--; - count--; - continue; - } - } - - if (folder_unmatched != NULL) - camel_vee_folder_lock (folder_unmatched, CAMEL_VEE_FOLDER_SUMMARY_LOCK); - - /* scan, looking for "old" uid's to be removed. "old" uid's - * are those that are from previous added sources (not in - * current source) */ - camel_folder_summary_prepare_fetch_all (folder->summary, NULL); - known_uids = camel_folder_summary_get_array (folder->summary); - for (i = 0; known_uids && i < known_uids->len; i++) { - CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *) camel_folder_summary_get (folder->summary, g_ptr_array_index (known_uids, i)); - - if (mi) { - if (mi->orig_summary == ssummary) { - gchar *uid = (gchar *) camel_message_info_uid (mi), *oldkey; - gpointer oldval; - - if (g_hash_table_lookup (matchhash, uid + 8) == NULL) { - camel_folder_change_info_remove_uid (vee_folder->changes, camel_message_info_uid (mi)); - camel_folder_summary_remove_uid (folder->summary, uid); - - if (!CAMEL_IS_VEE_FOLDER (source) - && unmatched_uids != NULL - && g_hash_table_lookup_extended (unmatched_uids, uid, (gpointer *) &oldkey, &oldval)) { - n = GPOINTER_TO_INT (oldval); - if (n == 1) { - g_hash_table_remove (unmatched_uids, oldkey); - g_free (oldkey); - } else { - g_hash_table_insert (unmatched_uids, oldkey, GINT_TO_POINTER (n - 1)); - } - } - } else { - g_hash_table_remove (matchhash, uid + 8); - } - } - camel_message_info_free ((CamelMessageInfo *) mi); - } - } - camel_folder_summary_free_array (known_uids); + CamelVeeMessageInfoData *mi_data = key; + CamelFolder *subfolder = user_data; + CamelVeeSubfolderData *sf_data; - if (rebuilded && !correlating) - u.message_uids = g_queue_new (); - else - u.message_uids = NULL; - - /* now matchhash contains any new uid's, add them, etc */ - g_hash_table_foreach (matchhash, (GHFunc) folder_added_uid, &u); - - if (u.message_uids != NULL) { - CamelStore *parent_store; - const gchar *full_name; - gchar *uid; - - full_name = camel_folder_get_full_name (folder); - parent_store = camel_folder_get_parent_store (folder); - - camel_db_begin_transaction (parent_store->cdb_w, NULL); - - while ((uid = g_queue_pop_head (u.message_uids)) != NULL) { - camel_db_add_to_vfolder_transaction ( - parent_store->cdb_w, full_name, uid, NULL); - g_free (uid); - } - - camel_db_end_transaction (parent_store->cdb_w, NULL); - - g_queue_free (u.message_uids); - u.message_uids = NULL; - } - - if (folder_unmatched != NULL) { - /* scan unmatched, remove any that have vanished, etc */ - GPtrArray *known_uids; - - known_uids = camel_folder_summary_get_array (((CamelFolder *) folder_unmatched)->summary); - if (known_uids != NULL) { - for (i = 0; i < known_uids->len; i++) { - const gchar *uid = g_ptr_array_index (known_uids, i); - - if (uid) { - if (strncmp (uid, u.hash, 8) == 0) { - if (g_hash_table_lookup (allhash, uid + 8) == NULL) { - /* no longer exists at all, just remove it entirely */ - camel_folder_summary_remove_uid (((CamelFolder *) folder_unmatched)->summary, uid); - camel_folder_change_info_remove_uid (folder_unmatched->changes, uid); - } else { - g_hash_table_remove (allhash, uid + 8); - } - } - } - } - camel_folder_summary_free_array (known_uids); - } - - /* now allhash contains all potentially new uid's for the unmatched folder, process */ - if (!CAMEL_IS_VEE_FOLDER (source)) { - - u.message_uids = g_queue_new (); - g_hash_table_foreach (allhash, (GHFunc) unmatched_check_uid, &u); + g_return_val_if_fail (mi_data != NULL, TRUE); - if (!g_queue_is_empty (u.message_uids)) { - CamelStore *parent_store; - const gchar *full_name; - gchar *uid; + sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data); - full_name = camel_folder_get_full_name (CAMEL_FOLDER (u.folder_unmatched)); - parent_store = camel_folder_get_parent_store (CAMEL_FOLDER (u.folder_unmatched)); - - camel_db_begin_transaction (parent_store->cdb_w, NULL); - - while ((uid = g_queue_pop_head (u.message_uids)) != NULL) { - camel_db_add_to_vfolder_transaction ( - parent_store->cdb_w, full_name, uid, NULL); - g_free (uid); - } - - camel_db_end_transaction (parent_store->cdb_w, NULL); - } - - g_queue_free (u.message_uids); - u.message_uids = NULL; - } + return subfolder == camel_vee_subfolder_data_get_folder (sf_data); +} - /* copy any changes so we can raise them outside the lock */ - if (camel_folder_change_info_changed (folder_unmatched->changes)) { - unmatched_changes = folder_unmatched->changes; - folder_unmatched->changes = camel_folder_change_info_new (); +static void +vee_folder_remove_folder (CamelVeeFolder *vfolder, + CamelFolder *subfolder, + GCancellable *cancellable) +{ + CamelFolderChangeInfo *changes; + CamelFolder *v_folder; + GHashTable *uids; + + v_folder = CAMEL_FOLDER (vfolder); + changes = camel_folder_change_info_new (); + + camel_folder_freeze (v_folder); + + uids = camel_vee_summary_get_uids_for_subfolder (CAMEL_VEE_SUMMARY (v_folder->summary), subfolder); + if (uids) { + struct RemoveUnmatchedData rud; + + rud.vfolder = vfolder; + rud.vsummary = CAMEL_VEE_SUMMARY (v_folder->summary); + rud.subfolder = subfolder; + rud.data_cache = vee_folder_get_data_cache (vfolder); + rud.changes = changes; + rud.is_orig_message_uid = FALSE; + + g_hash_table_foreach (uids, vee_folder_remove_unmatched_cb, &rud); + + if (vee_folder_is_unmatched (vfolder) && + !camel_vee_folder_get_auto_update (vfolder) && + g_hash_table_size (vfolder->priv->unmatched_add_changed) + + g_hash_table_size (vfolder->priv->unmatched_remove_changed) > 0) { + /* forget about these in cached updates */ + g_hash_table_foreach_remove (vfolder->priv->unmatched_add_changed, + vee_folder_remove_from_unmatched_changed_cb, subfolder); + g_hash_table_foreach_remove (vfolder->priv->unmatched_remove_changed, + vee_folder_remove_from_unmatched_changed_cb, subfolder); } - camel_vee_folder_unlock (folder_unmatched, CAMEL_VEE_FOLDER_SUMMARY_LOCK); - } - - if (camel_folder_change_info_changed (vee_folder->changes)) { - vee_folder_changes = vee_folder->changes; - vee_folder->changes = camel_folder_change_info_new (); + g_hash_table_destroy (uids); } - camel_vee_folder_unlock (vee_folder, CAMEL_VEE_FOLDER_SUMMARY_LOCK); - - /* Del the unwanted things from the summary, we don't hold any locks now. */ - if (del_list) { - if (!correlating) { - CamelStore *parent_store; - const gchar *full_name; - - full_name = camel_folder_get_full_name (folder); - parent_store = camel_folder_get_parent_store (folder); - camel_db_delete_vuids ( - parent_store->cdb_w, - full_name, shash, del_list, NULL); - } - - g_list_foreach (del_list, (GFunc) camel_pstring_free, NULL); - g_list_free (del_list); - }; - - g_hash_table_destroy (matchhash); - g_hash_table_destroy (allhash); - g_hash_table_destroy (fullhash); - - g_free (shash); - /* if expression not set, we only had a null list */ - if (vee_folder->expression == NULL || !rebuilded) { - g_ptr_array_foreach (match, (GFunc) camel_pstring_free, NULL); - g_ptr_array_free (match, TRUE); - } else - camel_folder_search_free (source, match); - camel_folder_summary_free_array (all); - - if (unmatched_changes) { - camel_folder_changed ( - CAMEL_FOLDER (folder_unmatched), unmatched_changes); - camel_folder_change_info_free (unmatched_changes); - } + if (vfolder->priv->parent_vee_store) + camel_vee_store_note_subfolder_unused (vfolder->priv->parent_vee_store, subfolder, vfolder); - if (vee_folder_changes) { - camel_folder_changed ( - CAMEL_FOLDER (vee_folder), vee_folder_changes); - camel_folder_change_info_free (vee_folder_changes); - } + camel_folder_thaw (v_folder); - return 0; + if (camel_folder_change_info_changed (changes)) + camel_folder_changed (CAMEL_FOLDER (vfolder), changes); + camel_folder_change_info_free (changes); } static void vee_folder_folder_changed (CamelVeeFolder *vee_folder, - CamelFolder *sub, + CamelFolder *subfolder, CamelFolderChangeInfo *changes) { CamelVeeFolderPrivate *p = vee_folder->priv; @@ -1992,10 +1092,7 @@ vee_folder_folder_changed (CamelVeeFolder *vee_folder, g_async_queue_lock (vee_folder->priv->change_queue); - data = g_slice_new0 (FolderChangedData); - data->changes = camel_folder_change_info_new (); - camel_folder_change_info_cat (data->changes, changes); - data->sub = g_object_ref (sub); + data = vee_folder_changed_data_new (subfolder, changes); g_async_queue_push_unlocked (vee_folder->priv->change_queue, data); @@ -2012,26 +1109,6 @@ vee_folder_folder_changed (CamelVeeFolder *vee_folder, } static void -vee_folder_folder_renamed (CamelVeeFolder *vee_folder, - CamelFolder *f, - const gchar *old) -{ - gchar hash[8]; - CamelVeeFolder *folder_unmatched = vee_folder->parent_vee_store ? vee_folder->parent_vee_store->folder_unmatched : NULL; - - /* TODO: This could probably be done in another thread, tho it is pretty quick/memory bound */ - - /* Life just got that little bit harder, if the folder is renamed, it means it breaks all of our uid's. - * We need to remove the old uid's, fix them up, then release the new uid's, for the uid's that match this folder */ - - camel_vee_folder_hash_folder (f, hash); - - subfolder_renamed_update (vee_folder, f, hash); - if (folder_unmatched != NULL) - subfolder_renamed_update (folder_unmatched, f, hash); -} - -static void camel_vee_folder_class_init (CamelVeeFolderClass *class) { GObjectClass *object_class; @@ -2042,11 +1119,14 @@ camel_vee_folder_class_init (CamelVeeFolderClass *class) object_class = G_OBJECT_CLASS (class); object_class->dispose = vee_folder_dispose; object_class->finalize = vee_folder_finalize; + object_class->get_property = vee_folder_get_property; + object_class->set_property = vee_folder_set_property; folder_class = CAMEL_FOLDER_CLASS (class); folder_class->search_by_expression = vee_folder_search_by_expression; folder_class->search_by_uids = vee_folder_search_by_uids; folder_class->count_by_expression = vee_folder_count_by_expression; + folder_class->search_free = vee_folder_search_free; folder_class->delete = vee_folder_delete; folder_class->freeze = vee_folder_freeze; folder_class->thaw = vee_folder_thaw; @@ -2062,7 +1142,17 @@ camel_vee_folder_class_init (CamelVeeFolderClass *class) class->remove_folder = vee_folder_remove_folder; class->rebuild_folder = vee_folder_rebuild_folder; class->folder_changed = vee_folder_folder_changed; - class->folder_renamed = vee_folder_folder_renamed; + + g_object_class_install_property ( + object_class, + PROP_AUTO_UPDATE, + g_param_spec_boolean ( + "auto-update", + "Auto Update", + _("Automatically _update on change in source folders"), + TRUE, + G_PARAM_READWRITE | + CAMEL_PARAM_PERSISTENT)); } static void @@ -2081,22 +1171,20 @@ camel_vee_folder_init (CamelVeeFolder *vee_folder) CAMEL_MESSAGE_FLAGGED | CAMEL_MESSAGE_SEEN; - vee_folder->changes = camel_folder_change_info_new (); - vee_folder->search = camel_folder_search_new (); - vee_folder->hashes = g_hash_table_new_full ( - g_str_hash, g_str_equal, g_free, NULL); - - /* Loaded is no longer used.*/ - vee_folder->loaded = NULL; - vee_folder->deleted = FALSE; - vee_folder->priv->summary_lock = g_mutex_new (); - vee_folder->priv->subfolder_lock = g_mutex_new (); - vee_folder->priv->changed_lock = g_mutex_new (); + g_static_rec_mutex_init (&vee_folder->priv->summary_lock); + g_static_rec_mutex_init (&vee_folder->priv->subfolder_lock); + g_static_rec_mutex_init (&vee_folder->priv->changed_lock); + + vee_folder->priv->auto_update = TRUE; vee_folder->priv->ignore_changed = g_hash_table_new (g_direct_hash, g_direct_equal); vee_folder->priv->skipped_changes = g_hash_table_new (g_direct_hash, g_direct_equal); + vee_folder->priv->unmatched_add_changed = + g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); + vee_folder->priv->unmatched_remove_changed = + g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); vee_folder->priv->change_queue = g_async_queue_new_full ( - (GDestroyNotify) folder_changed_data_free); + (GDestroyNotify) vee_folder_changed_data_free); } void @@ -2108,12 +1196,34 @@ camel_vee_folder_construct (CamelVeeFolder *vf, vf->flags = flags; + parent_store = camel_folder_get_parent_store (CAMEL_FOLDER (vf)); + if (CAMEL_IS_VEE_STORE (parent_store)) + vf->priv->parent_vee_store = CAMEL_VEE_STORE (parent_store); + else + vf->priv->vee_data_cache = camel_vee_data_cache_new (); + folder->summary = camel_vee_summary_new (folder); - parent_store = camel_folder_get_parent_store (CAMEL_FOLDER (vf)); + /* only for subfolders of vee-store */ + if (vf->priv->parent_vee_store) { + const gchar *user_data_dir; + gchar *state_file, *folder_name, *filename; - if (CAMEL_IS_VEE_STORE (parent_store)) - vf->parent_vee_store = CAMEL_VEE_STORE (parent_store); + user_data_dir = camel_service_get_user_data_dir (CAMEL_SERVICE (parent_store)); + + folder_name = g_uri_escape_string (camel_folder_get_full_name (folder), NULL, TRUE); + filename = g_strconcat (folder_name, ".cmeta", NULL); + state_file = g_build_filename (user_data_dir, filename, NULL); + + camel_object_set_state_filename (CAMEL_OBJECT (vf), state_file); + + g_free (state_file); + g_free (filename); + g_free (folder_name); + + /* set/load persistent state */ + camel_object_state_read (CAMEL_OBJECT (vf)); + } } /** @@ -2137,8 +1247,9 @@ camel_vee_folder_new (CamelStore *parent_store, g_return_val_if_fail (full != NULL, NULL); if (CAMEL_IS_VEE_STORE (parent_store) && strcmp (full, CAMEL_UNMATCHED_NAME) == 0) { - vf = ((CamelVeeStore *) parent_store)->folder_unmatched; - g_object_ref (vf); + vf = camel_vee_store_get_unmatched_folder (CAMEL_VEE_STORE (parent_store)); + if (vf) + g_object_ref (vf); } else { const gchar *name = strrchr (full, '/'); @@ -2159,173 +1270,111 @@ camel_vee_folder_new (CamelStore *parent_store, } void -camel_vee_folder_set_expression (CamelVeeFolder *vf, - const gchar *query) +camel_vee_folder_set_expression (CamelVeeFolder *vfolder, + const gchar *expr) +{ + CAMEL_VEE_FOLDER_GET_CLASS (vfolder)->set_expression (vfolder, expr); +} + +const gchar * +camel_vee_folder_get_expression (CamelVeeFolder *vfolder) { - CAMEL_VEE_FOLDER_GET_CLASS (vf)->set_expression (vf, query); + g_return_val_if_fail (CAMEL_IS_VEE_FOLDER (vfolder), NULL); + + return vfolder->priv->expression; } /** * camel_vee_folder_add_folder: - * @vf: Virtual Folder object - * @sub: source CamelFolder to add to @vf + * @vfolder: Virtual Folder object + * @subfolder: source CamelFolder to add to @vfolder * - * Adds @sub as a source folder to @vf. + * Adds @subfolder as a source folder to @vfolder. **/ void -camel_vee_folder_add_folder (CamelVeeFolder *vf, - CamelFolder *sub) +camel_vee_folder_add_folder (CamelVeeFolder *vfolder, + CamelFolder *subfolder, + GCancellable *cancellable) { - CamelVeeFolderPrivate *p = CAMEL_VEE_FOLDER_GET_PRIVATE (vf); - gint i; - CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL; + g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder)); - if (vf == (CamelVeeFolder *) sub) { + if (vfolder == (CamelVeeFolder *) subfolder) { g_warning ("Adding a virtual folder to itself as source, ignored"); return; } - camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - - /* for normal vfolders we want only unique ones, for unmatched we want them all recorded */ - if (g_list_find (p->folders, sub) == NULL) { - p->folders = g_list_append ( - p->folders, g_object_ref (sub)); - - camel_folder_lock (CAMEL_FOLDER (vf), CAMEL_FOLDER_CHANGE_LOCK); - - /* update the freeze state of 'sub' to match our freeze state */ - for (i = 0; i < camel_folder_get_frozen_count ((CamelFolder *) vf); i++) - camel_folder_freeze (sub); - - camel_folder_unlock (CAMEL_FOLDER (vf), CAMEL_FOLDER_CHANGE_LOCK); - } - if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER (sub) && folder_unmatched != NULL) { - CamelVeeFolderPrivate *up = CAMEL_VEE_FOLDER_GET_PRIVATE (folder_unmatched); - up->folders = g_list_append ( - up->folders, g_object_ref (sub)); - - camel_folder_lock (CAMEL_FOLDER (folder_unmatched), CAMEL_FOLDER_CHANGE_LOCK); - - /* update the freeze state of 'sub' to match Unmatched's freeze state */ - for (i = 0; i < camel_folder_get_frozen_count ((CamelFolder *) folder_unmatched); i++) - camel_folder_freeze (sub); + camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - camel_folder_unlock (CAMEL_FOLDER (folder_unmatched), CAMEL_FOLDER_CHANGE_LOCK); + if (g_list_find (vfolder->priv->subfolders, subfolder) == NULL) { + vfolder->priv->subfolders = g_list_append (vfolder->priv->subfolders, g_object_ref (subfolder)); + } else { + /* nothing to do, it's already there */ + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + return; } - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - - g_signal_connect ( - sub, "changed", - G_CALLBACK (folder_changed), vf); + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); g_signal_connect ( - sub, "deleted", - G_CALLBACK (subfolder_deleted), vf); + subfolder, "changed", + G_CALLBACK (subfolder_changed), vfolder); g_signal_connect ( - sub, "renamed", - G_CALLBACK (folder_renamed), vf); + subfolder, "deleted", + G_CALLBACK (subfolder_deleted), vfolder); - CAMEL_VEE_FOLDER_GET_CLASS (vf)->add_folder (vf, sub); + CAMEL_VEE_FOLDER_GET_CLASS (vfolder)->add_folder (vfolder, subfolder, cancellable); } /** * camel_vee_folder_remove_folder: - * @vf: Virtual Folder object - * @sub: source CamelFolder to remove from @vf + * @vfolder: Virtual Folder object + * @subfolder: source CamelFolder to remove from @vfolder * - * Removed the source folder, @sub, from the virtual folder, @vf. + * Removed the source folder, @subfolder, from the virtual folder, @vfolder. **/ void -camel_vee_folder_remove_folder (CamelVeeFolder *vf, - CamelFolder *sub) +camel_vee_folder_remove_folder (CamelVeeFolder *vfolder, + CamelFolder *subfolder, + GCancellable *cancellable) { - CamelVeeFolderPrivate *p = CAMEL_VEE_FOLDER_GET_PRIVATE (vf); - gint i; - CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL; + g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder)); - camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - - camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_CHANGED_LOCK); - p->folders_changed = g_list_remove (p->folders_changed, sub); - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_CHANGED_LOCK); + camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - if (g_list_find (p->folders, sub) == NULL) { - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + if (g_list_find (vfolder->priv->subfolders, subfolder) == NULL) { + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); return; } - g_signal_handlers_disconnect_by_func (sub, folder_changed, vf); - g_signal_handlers_disconnect_by_func (sub, subfolder_deleted, vf); - g_signal_handlers_disconnect_by_func (sub, folder_renamed, vf); - - p->folders = g_list_remove (p->folders, sub); - - /* undo the freeze state that we have imposed on this source folder */ - camel_folder_lock (CAMEL_FOLDER (vf), CAMEL_FOLDER_CHANGE_LOCK); - for (i = 0; i < camel_folder_get_frozen_count ((CamelFolder *) vf); i++) - camel_folder_thaw (sub); - camel_folder_unlock (CAMEL_FOLDER (vf), CAMEL_FOLDER_CHANGE_LOCK); - - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + g_signal_handlers_disconnect_by_func (subfolder, subfolder_changed, vfolder); + g_signal_handlers_disconnect_by_func (subfolder, subfolder_deleted, vfolder); - if (folder_unmatched != NULL) { - CamelVeeFolderPrivate *up = CAMEL_VEE_FOLDER_GET_PRIVATE (folder_unmatched); - - camel_vee_folder_lock (folder_unmatched, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - /* if folder deleted, then blow it away from unmatched always, and remove all refs to it */ - if (sub->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED) { - while (g_list_find (up->folders, sub)) { - up->folders = g_list_remove (up->folders, sub); - g_object_unref (sub); - - /* undo the freeze state that Unmatched has imposed on this source folder */ - camel_folder_lock (CAMEL_FOLDER (folder_unmatched), CAMEL_FOLDER_CHANGE_LOCK); - for (i = 0; i < camel_folder_get_frozen_count ((CamelFolder *) folder_unmatched); i++) - camel_folder_thaw (sub); - camel_folder_unlock (CAMEL_FOLDER (folder_unmatched), CAMEL_FOLDER_CHANGE_LOCK); - } - } else if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) { - if (g_list_find (up->folders, sub) != NULL) { - up->folders = g_list_remove (up->folders, sub); - g_object_unref (sub); - - /* undo the freeze state that Unmatched has imposed on this source folder */ - camel_folder_lock (CAMEL_FOLDER (folder_unmatched), CAMEL_FOLDER_CHANGE_LOCK); - for (i = 0; i < camel_folder_get_frozen_count ((CamelFolder *) folder_unmatched); i++) - camel_folder_thaw (sub); - camel_folder_unlock (CAMEL_FOLDER (folder_unmatched), CAMEL_FOLDER_CHANGE_LOCK); - } - } - camel_vee_folder_unlock (folder_unmatched, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - } + vfolder->priv->subfolders = g_list_remove (vfolder->priv->subfolders, subfolder); - CAMEL_VEE_FOLDER_GET_CLASS (vf)->remove_folder (vf, sub); + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - if (CAMEL_IS_VEE_FOLDER (sub)) - return; + CAMEL_VEE_FOLDER_GET_CLASS (vfolder)->remove_folder (vfolder, subfolder, cancellable); - g_object_unref (sub); + g_object_unref (subfolder); } /** * camel_vee_folder_rebuild_folder: - * @vf: Virtual Folder object - * @sub: source CamelFolder to add to @vf - * @error: return location for a #GError, or %NULL + * @vfolder: Virtual Folder object + * @subfolder: source CamelFolder to add to @vfolder + * @cancellable: * - * Rebuild the folder @sub, if it should be. + * Rebuild the folder @subfolder, if it should be. **/ -gint -camel_vee_folder_rebuild_folder (CamelVeeFolder *vf, - CamelFolder *sub, - GError **error) +void +camel_vee_folder_rebuild_folder (CamelVeeFolder *vfolder, + CamelFolder *subfolder, + GCancellable *cancellable) { - vee_folder_propagate_skipped_changes (vf); + vee_folder_propagate_skipped_changes (vfolder); - return CAMEL_VEE_FOLDER_GET_CLASS (vf)->rebuild_folder (vf, sub, error); + CAMEL_VEE_FOLDER_GET_CLASS (vfolder)->rebuild_folder (vfolder, subfolder, cancellable); } static void @@ -2333,7 +1382,7 @@ remove_folders (CamelFolder *folder, CamelFolder *foldercopy, CamelVeeFolder *vf) { - camel_vee_folder_remove_folder (vf, folder); + camel_vee_folder_remove_folder (vf, folder, NULL); g_object_unref (folder); } @@ -2346,16 +1395,17 @@ remove_folders (CamelFolder *folder, **/ void camel_vee_folder_set_folders (CamelVeeFolder *vf, - GList *folders) + GList *folders, + GCancellable *cancellable) { CamelVeeFolderPrivate *p = CAMEL_VEE_FOLDER_GET_PRIVATE (vf); GHashTable *remove = g_hash_table_new (NULL, NULL); - GList *l; + GList *l, *to_add = NULL; CamelFolder *folder; /* setup a table of all folders we have currently */ camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - l = p->folders; + l = p->subfolders; while (l) { g_hash_table_insert (remove, l->data, l->data); g_object_ref (l->data); @@ -2363,66 +1413,117 @@ camel_vee_folder_set_folders (CamelVeeFolder *vf, } camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); - /* if we already have the folder, ignore it, otherwise add it */ + camel_folder_freeze (CAMEL_FOLDER (vf)); + + /* if we already have the folder, ignore it, otherwise mark to add it */ l = folders; while (l) { if ((folder = g_hash_table_lookup (remove, l->data))) { g_hash_table_remove (remove, folder); g_object_unref (folder); } else { - camel_vee_folder_add_folder (vf, l->data); + to_add = g_list_prepend (to_add, g_object_ref (l->data)); } l = l->next; } - /* then remove any we still have */ + /* first remove any we still have */ g_hash_table_foreach (remove, (GHFunc) remove_folders, vf); g_hash_table_destroy (remove); + + /* then add those new */ + for (l = to_add; l; l = l->next) { + camel_vee_folder_add_folder (vf, l->data, cancellable); + } + g_list_free_full (to_add, g_object_unref); + + camel_folder_thaw (CAMEL_FOLDER (vf)); } -/** - * camel_vee_folder_hash_folder: - * @folder: - * @: - * - * Create a hash string representing the folder name, which should be - * unique, and remain static for a given folder. - **/ void -camel_vee_folder_hash_folder (CamelFolder *folder, - gchar buffer[8]) +camel_vee_folder_add_vuid (CamelVeeFolder *vfolder, + CamelVeeMessageInfoData *mi_data, + CamelFolderChangeInfo *changes) { - CamelStore *parent_store; - GChecksum *checksum; - guint8 *digest; - gsize length; - gint state = 0, save = 0; - const gchar *full_name; - const gchar *uid; - gint i; - - length = g_checksum_type_get_length (G_CHECKSUM_MD5); - digest = g_alloca (length); - - checksum = g_checksum_new (G_CHECKSUM_MD5); - parent_store = camel_folder_get_parent_store (folder); - uid = camel_service_get_uid (CAMEL_SERVICE (parent_store)); - g_checksum_update (checksum, (guchar *) uid, -1); - - full_name = camel_folder_get_full_name (folder); - g_checksum_update (checksum, (guchar *) full_name, -1); - g_checksum_get_digest (checksum, digest, &length); - g_checksum_free (checksum); - - g_base64_encode_step (digest, 6, FALSE, buffer, &state, &save); - g_base64_encode_close (FALSE, buffer, &state, &save); - - for (i = 0; i < 8; i++) { - if (buffer[i] == '+') - buffer[i] = '.'; - if (buffer[i] == '/') - buffer[i] = '_'; + CamelVeeSummary *vsummary; + CamelVeeSubfolderData *sf_data; + CamelFolder *subfolder; + + g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder)); + g_return_if_fail (mi_data != NULL); + g_return_if_fail (vee_folder_is_unmatched (vfolder)); + + sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data); + subfolder = camel_vee_subfolder_data_get_folder (sf_data); + + camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_CHANGED_LOCK); + if (!camel_vee_folder_get_auto_update (vfolder) || + g_hash_table_lookup (vfolder->priv->ignore_changed, subfolder) || + g_hash_table_lookup (vfolder->priv->skipped_changes, subfolder)) { + g_hash_table_remove (vfolder->priv->unmatched_remove_changed, mi_data); + + camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + + if (g_list_find (vfolder->priv->subfolders, subfolder)) { + /* postpone addition to the Unmatched folder, if the change was done + in the Unmatched folder itself or auto-update is disabled */ + g_hash_table_insert (vfolder->priv->unmatched_add_changed, + g_object_ref (mi_data), GINT_TO_POINTER (1)); + } + + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_CHANGED_LOCK); + + return; + } + + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_CHANGED_LOCK); + + vsummary = CAMEL_VEE_SUMMARY (CAMEL_FOLDER (vfolder)->summary); + vee_folder_note_added_uid (vfolder, vsummary, mi_data, changes, FALSE); +} + +void +camel_vee_folder_remove_vuid (CamelVeeFolder *vfolder, + CamelVeeMessageInfoData *mi_data, + CamelFolderChangeInfo *changes) +{ + CamelVeeSummary *vsummary; + CamelVeeSubfolderData *sf_data; + CamelFolder *subfolder; + + g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder)); + g_return_if_fail (mi_data != NULL); + g_return_if_fail (vee_folder_is_unmatched (vfolder)); + + sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data); + subfolder = camel_vee_subfolder_data_get_folder (sf_data); + + camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_CHANGED_LOCK); + if (!camel_vee_folder_get_auto_update (vfolder) || + g_hash_table_lookup (vfolder->priv->ignore_changed, subfolder) || + g_hash_table_lookup (vfolder->priv->skipped_changes, subfolder)) { + g_hash_table_remove (vfolder->priv->unmatched_add_changed, mi_data); + + camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + + if (g_list_find (vfolder->priv->subfolders, subfolder)) { + /* postpone removal from the Unmatched folder, if the change was done + in the Unmatched folder itself or auto-update is disabled */ + g_hash_table_insert (vfolder->priv->unmatched_remove_changed, + g_object_ref (mi_data), GINT_TO_POINTER (1)); + } + + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_SUBFOLDER_LOCK); + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_CHANGED_LOCK); + + return; } + + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_CHANGED_LOCK); + + vsummary = CAMEL_VEE_SUMMARY (CAMEL_FOLDER (vfolder)->summary); + vee_folder_note_unmatch_uid (vfolder, vsummary, subfolder, mi_data, changes); } /** @@ -2443,6 +1544,9 @@ camel_vee_folder_get_location (CamelVeeFolder *vf, { CamelFolder *folder; + g_return_val_if_fail (CAMEL_IS_VEE_FOLDER (vf), NULL); + g_return_val_if_fail (vinfo != NULL, NULL); + folder = camel_folder_summary_get_folder (vinfo->orig_summary); /* locking? yes? no? although the vfolderinfo is valid when obtained @@ -2463,12 +1567,61 @@ camel_vee_folder_get_location (CamelVeeFolder *vf, } } +CamelFolder * +camel_vee_folder_get_vee_uid_folder (CamelVeeFolder *vf, + const gchar *vee_message_uid) +{ + CamelFolder *res; + CamelVeeDataCache *data_cache; + CamelVeeMessageInfoData *mi_data; + CamelVeeSubfolderData *sf_data; + + g_return_val_if_fail (CAMEL_IS_VEE_FOLDER (vf), NULL); + g_return_val_if_fail (vee_message_uid, NULL); + + res = NULL; + + data_cache = vee_folder_get_data_cache (vf); + g_return_val_if_fail (data_cache != NULL, NULL); + + mi_data = camel_vee_data_cache_get_message_info_data_by_vuid (data_cache, vee_message_uid); + if (mi_data) { + sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data); + res = camel_vee_subfolder_data_get_folder (sf_data); + g_object_unref (mi_data); + } + + return res; +} + +void +camel_vee_folder_set_auto_update (CamelVeeFolder *vfolder, + gboolean auto_update) +{ + g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder)); + + if ((vfolder->priv->auto_update ? 1 : 0) == (auto_update ? 1 : 0)) + return; + + vfolder->priv->auto_update = auto_update; + + g_object_notify (G_OBJECT (vfolder), "auto-update"); +} + +gboolean +camel_vee_folder_get_auto_update (CamelVeeFolder *vfolder) +{ + g_return_val_if_fail (CAMEL_IS_VEE_FOLDER (vfolder), FALSE); + + return vfolder->priv->auto_update; +} + /** * camel_vee_folder_ignore_next_changed_event: - * @vf: a #CamelVeeFolder - * @sub: a #CamelFolder folder + * @vfolder: a #CamelVeeFolder + * @subfolder: a #CamelFolder folder * - * The next @sub folder's 'changed' event will be silently ignored. This + * The next @subfolder-'s 'changed' event will be silently ignored. This * is usually used in virtual folders when the change was done in them, * but it is neither vTrash nor vJunk folder. Doing this avoids unnecessary * removals of messages which don't satisfy search criteria anymore, @@ -2478,39 +1631,15 @@ camel_vee_folder_get_location (CamelVeeFolder *vf, * Since: 3.2 **/ void -camel_vee_folder_ignore_next_changed_event (CamelVeeFolder *vf, - CamelFolder *sub) -{ - g_return_if_fail (vf != NULL); - g_return_if_fail (CAMEL_IS_VEE_FOLDER (vf)); - g_return_if_fail (sub != NULL); - - camel_vee_folder_lock (vf, CAMEL_VEE_FOLDER_CHANGED_LOCK); - g_hash_table_insert (vf->priv->ignore_changed, sub, GINT_TO_POINTER (1)); - camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_CHANGED_LOCK); -} - -/** - * camel_vee_folder_sync_headers: - * - * Since: 2.24 - **/ -void -camel_vee_folder_sync_headers (CamelFolder *vf, - GError **error) +camel_vee_folder_ignore_next_changed_event (CamelVeeFolder *vfolder, + CamelFolder *subfolder) { - CamelFIRecord * record; - CamelStore *parent_store; - - /* Save the counts to DB */ - record = summary_header_to_db (vf->summary, error); - parent_store = camel_folder_get_parent_store (vf); - camel_db_begin_transaction (parent_store->cdb_w, NULL); - camel_db_write_folder_info_record (parent_store->cdb_w, record, error); - camel_db_end_transaction (parent_store->cdb_w, NULL); + g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder)); + g_return_if_fail (subfolder != NULL); - g_free (record->folder_name); - g_free (record); + camel_vee_folder_lock (vfolder, CAMEL_VEE_FOLDER_CHANGED_LOCK); + g_hash_table_insert (vfolder->priv->ignore_changed, subfolder, GINT_TO_POINTER (1)); + camel_vee_folder_unlock (vfolder, CAMEL_VEE_FOLDER_CHANGED_LOCK); } /** @@ -2530,13 +1659,13 @@ camel_vee_folder_lock (CamelVeeFolder *folder, switch (lock) { case CAMEL_VEE_FOLDER_SUMMARY_LOCK: - g_mutex_lock (folder->priv->summary_lock); + g_static_rec_mutex_lock (&folder->priv->summary_lock); break; case CAMEL_VEE_FOLDER_SUBFOLDER_LOCK: - g_mutex_lock (folder->priv->subfolder_lock); + g_static_rec_mutex_lock (&folder->priv->subfolder_lock); break; case CAMEL_VEE_FOLDER_CHANGED_LOCK: - g_mutex_lock (folder->priv->changed_lock); + g_static_rec_mutex_lock (&folder->priv->changed_lock); break; default: g_return_if_reached (); @@ -2560,13 +1689,13 @@ camel_vee_folder_unlock (CamelVeeFolder *folder, switch (lock) { case CAMEL_VEE_FOLDER_SUMMARY_LOCK: - g_mutex_unlock (folder->priv->summary_lock); + g_static_rec_mutex_unlock (&folder->priv->summary_lock); break; case CAMEL_VEE_FOLDER_SUBFOLDER_LOCK: - g_mutex_unlock (folder->priv->subfolder_lock); + g_static_rec_mutex_unlock (&folder->priv->subfolder_lock); break; case CAMEL_VEE_FOLDER_CHANGED_LOCK: - g_mutex_unlock (folder->priv->changed_lock); + g_static_rec_mutex_unlock (&folder->priv->changed_lock); break; default: g_return_if_reached (); diff --git a/camel/camel-vee-folder.h b/camel/camel-vee-folder.h index 8999c1a..ea452ef 100644 --- a/camel/camel-vee-folder.h +++ b/camel/camel-vee-folder.h @@ -71,19 +71,7 @@ struct _CamelVeeFolder { CamelFolder parent; CamelVeeFolderPrivate *priv; - gchar *expression; /* query expression */ - guint32 flags; /* folder open flags */ - - CamelFolderChangeInfo *changes; - CamelFolderSearch *search; - - /* only set-up if our parent is a vee-store, used also as a flag to - * say that this folder is part of the unmatched folder */ - struct _CamelVeeStore *parent_vee_store; - GHashTable *hashes; - GHashTable *loaded; - gboolean deleted; }; struct _CamelVeeFolderClass { @@ -92,12 +80,14 @@ struct _CamelVeeFolderClass { /* TODO: Some of this may need some additional work/thinking through, it works for now*/ void (*add_folder) (CamelVeeFolder *vee_folder, - CamelFolder *folder); + CamelFolder *folder, + GCancellable *cancellable); void (*remove_folder) (CamelVeeFolder *vee_folder, - CamelFolder *folder); - gint (*rebuild_folder) (CamelVeeFolder *vee_folder, CamelFolder *folder, - GError **error); + GCancellable *cancellable); + void (*rebuild_folder) (CamelVeeFolder *vee_folder, + CamelFolder *folder, + GCancellable *cancellable); void (*set_expression) (CamelVeeFolder *vee_folder, const gchar *expression); @@ -106,34 +96,56 @@ struct _CamelVeeFolderClass { void (*folder_changed) (CamelVeeFolder *vee_folder, CamelFolder *subfolder, CamelFolderChangeInfo *changes); - /* Called for a folder-renamed event on a source folder */ - void (*folder_renamed) (CamelVeeFolder *vee_folder, - CamelFolder *subfolder, - const gchar *old); }; #define CAMEL_UNMATCHED_NAME "UNMATCHED" -GType camel_vee_folder_get_type (void); -CamelFolder *camel_vee_folder_new (CamelStore *parent_store, const gchar *full, guint32 flags); -void camel_vee_folder_construct (CamelVeeFolder *vf, guint32 flags); - -CamelFolder *camel_vee_folder_get_location (CamelVeeFolder *vf, const struct _CamelVeeMessageInfo *vinfo, gchar **realuid); - -void camel_vee_folder_add_folder (CamelVeeFolder *vf, CamelFolder *sub); -void camel_vee_folder_remove_folder (CamelVeeFolder *vf, - CamelFolder *sub); -void camel_vee_folder_set_folders (CamelVeeFolder *vf, GList *folders); -gint camel_vee_folder_rebuild_folder (CamelVeeFolder *vf, CamelFolder *sub, GError **error); -void camel_vee_folder_set_expression (CamelVeeFolder *vf, const gchar *expr); - -void camel_vee_folder_ignore_next_changed_event (CamelVeeFolder *vf, CamelFolder *sub); - -void camel_vee_folder_hash_folder (CamelFolder *folder, gchar buffer[8]); -void camel_vee_folder_sync_headers (CamelFolder *vf, GError **error); - -void camel_vee_folder_lock (CamelVeeFolder *folder, CamelVeeFolderLock lock); -void camel_vee_folder_unlock (CamelVeeFolder *folder, CamelVeeFolderLock lock); +GType camel_vee_folder_get_type (void); +CamelFolder * camel_vee_folder_new (CamelStore *parent_store, + const gchar *full, + guint32 flags); +void camel_vee_folder_construct (CamelVeeFolder *vf, + guint32 flags); + +CamelFolder * camel_vee_folder_get_location (CamelVeeFolder *vf, + const struct _CamelVeeMessageInfo *vinfo, + gchar **realuid); +CamelFolder * camel_vee_folder_get_vee_uid_folder (CamelVeeFolder *vf, + const gchar *vee_message_uid); +void camel_vee_folder_set_auto_update (CamelVeeFolder *vfolder, + gboolean auto_update); +gboolean camel_vee_folder_get_auto_update (CamelVeeFolder *vfolder); +void camel_vee_folder_add_folder (CamelVeeFolder *vfolder, + CamelFolder *subfolder, + GCancellable *cancellable); +void camel_vee_folder_remove_folder (CamelVeeFolder *vfolder, + CamelFolder *subfolder, + GCancellable *cancellable); +void camel_vee_folder_set_folders (CamelVeeFolder *vf, + GList *folders, + GCancellable *cancellable); +void camel_vee_folder_add_vuid (CamelVeeFolder *vfolder, + struct _CamelVeeMessageInfoData *mi_data, + CamelFolderChangeInfo *changes); +void camel_vee_folder_remove_vuid (CamelVeeFolder *vfolder, + struct _CamelVeeMessageInfoData *mi_data, + CamelFolderChangeInfo *changes); + +void camel_vee_folder_rebuild_folder (CamelVeeFolder *vfolder, + CamelFolder *subfolder, + GCancellable *cancellable); +void camel_vee_folder_set_expression (CamelVeeFolder *vfolder, + const gchar *expr); +const gchar * camel_vee_folder_get_expression (CamelVeeFolder *vfolder); + +void camel_vee_folder_ignore_next_changed_event + (CamelVeeFolder *vfolder, + CamelFolder *subfolder); + +void camel_vee_folder_lock (CamelVeeFolder *folder, + CamelVeeFolderLock lock); +void camel_vee_folder_unlock (CamelVeeFolder *folder, + CamelVeeFolderLock lock); G_END_DECLS diff --git a/camel/camel-vee-store.c b/camel/camel-vee-store.c index 7e30e99..bd8f481 100644 --- a/camel/camel-vee-store.c +++ b/camel/camel-vee-store.c @@ -29,6 +29,7 @@ #include "camel-db.h" #include "camel-session.h" +#include "camel-string-utils.h" #include "camel-vee-folder.h" #include "camel-vee-store.h" @@ -46,8 +47,28 @@ #define CHANGE_DELETE (1) #define CHANGE_NOSELECT (2) +/* The custom property ID is a CamelArg artifact. + * It still identifies the property in state files. */ +enum { + PROP_0, + PROP_UNMATCHED_ENABLED = 0x2400 +}; + G_DEFINE_TYPE (CamelVeeStore, camel_vee_store, CAMEL_TYPE_STORE) +struct _CamelVeeStorePrivate +{ + CamelVeeDataCache *vee_data_cache; + CamelVeeFolder *unmatched_folder; + gboolean unmatched_enabled; + + GMutex *sf_counts_mutex; + GHashTable *subfolder_usage_counts; /* CamelFolder * (subfolder) => gint of usages, for unmatched_folder */ + + GMutex *vu_counts_mutex; + GHashTable *vuid_usage_counts; /* gchar * (vuid) => gint of usages, those with 0 comes to unmatched_folder */ +}; + static gint vee_folder_cmp (gconstpointer ap, gconstpointer bp) @@ -92,21 +113,16 @@ change_folder (CamelStore *store, } static void -cvs_free_unmatched (gpointer key, - gpointer value, - gpointer data) -{ - g_free (key); -} - -static void vee_store_finalize (GObject *object) { CamelVeeStore *vee_store = CAMEL_VEE_STORE (object); - g_hash_table_foreach (vee_store->unmatched_uids, cvs_free_unmatched, NULL); - g_hash_table_destroy (vee_store->unmatched_uids); - g_object_unref (vee_store->folder_unmatched); + g_object_unref (vee_store->priv->unmatched_folder); + g_object_unref (vee_store->priv->vee_data_cache); + g_hash_table_destroy (vee_store->priv->subfolder_usage_counts); + g_hash_table_destroy (vee_store->priv->vuid_usage_counts); + g_mutex_free (vee_store->priv->sf_counts_mutex); + g_mutex_free (vee_store->priv->vu_counts_mutex); /* Chain up to parent's finalize () method. */ G_OBJECT_CLASS (camel_vee_store_parent_class)->finalize (object); @@ -122,20 +138,52 @@ vee_store_constructed (GObject *object) /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (camel_vee_store_parent_class)->constructed (object); -#ifndef VEE_UNMATCHED_ENABLE /* Set up unmatched folder */ - vee_store->unmatched_uids = g_hash_table_new (g_str_hash, g_str_equal); - vee_store->folder_unmatched = g_object_new ( + vee_store->priv->unmatched_folder = g_object_new ( CAMEL_TYPE_VEE_FOLDER, "full-name", CAMEL_UNMATCHED_NAME, "display-name", PRETTY_UNMATCHED_FOLDER_NAME, "parent-store", vee_store, NULL); camel_vee_folder_construct ( - vee_store->folder_unmatched, CAMEL_STORE_FOLDER_PRIVATE); - camel_db_create_vfolder ( - CAMEL_STORE (vee_store)->cdb_r, PRETTY_UNMATCHED_FOLDER_NAME, NULL); -#endif + vee_store->priv->unmatched_folder, CAMEL_STORE_FOLDER_PRIVATE); + vee_store->priv->subfolder_usage_counts = g_hash_table_new (g_direct_hash, g_direct_equal); + vee_store->priv->vuid_usage_counts = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) camel_pstring_free, NULL); + vee_store->priv->sf_counts_mutex = g_mutex_new (); + vee_store->priv->vu_counts_mutex = g_mutex_new (); +} + +static void +vee_store_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_UNMATCHED_ENABLED: + g_value_set_boolean ( + value, camel_vee_store_get_unmatched_enabled ( + CAMEL_VEE_STORE (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +vee_store_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_UNMATCHED_ENABLED: + camel_vee_store_set_unmatched_enabled ( + CAMEL_VEE_STORE (object), + g_value_get_boolean (value)); + return; + } + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static gchar * @@ -189,6 +237,24 @@ vee_store_get_folder_sync (CamelStore *store, } static CamelFolderInfo * +vee_store_create_unmatched_fi (void) +{ + CamelFolderInfo *info; + + info = camel_folder_info_new (); + info->full_name = g_strdup (CAMEL_UNMATCHED_NAME); + info->display_name = g_strdup (PRETTY_UNMATCHED_FOLDER_NAME); + info->unread = -1; + info->flags = + CAMEL_FOLDER_NOCHILDREN | + CAMEL_FOLDER_NOINFERIORS | + CAMEL_FOLDER_SYSTEM | + CAMEL_FOLDER_VIRTUAL; + + return info; +} + +static CamelFolderInfo * vee_store_get_folder_info_sync (CamelStore *store, const gchar *top, CamelStoreGetFolderInfoFlags flags, @@ -295,18 +361,10 @@ vee_store_get_folder_info_sync (CamelStore *store, g_ptr_array_free (folders, TRUE); g_hash_table_destroy (infos_hash); - /* and always add UNMATCHED, if scanning from top/etc */ - /* FIXME[disk-summary] comment it out well */ - if ((top == NULL || top[0] == 0 || strncmp (top, CAMEL_UNMATCHED_NAME, strlen (CAMEL_UNMATCHED_NAME)) == 0)) { - info = camel_folder_info_new (); - info->full_name = g_strdup (CAMEL_UNMATCHED_NAME); - info->display_name = g_strdup (PRETTY_UNMATCHED_FOLDER_NAME); - info->unread = -1; - info->flags = - CAMEL_FOLDER_NOCHILDREN | - CAMEL_FOLDER_NOINFERIORS | - CAMEL_FOLDER_SYSTEM | - CAMEL_FOLDER_VIRTUAL; + /* and add UNMATCHED, if scanning from top/etc and it's enabled */ + if (camel_vee_store_get_unmatched_enabled (CAMEL_VEE_STORE (store)) && + (top == NULL || top[0] == 0 || strncmp (top, CAMEL_UNMATCHED_NAME, strlen (CAMEL_UNMATCHED_NAME)) == 0)) { + info = vee_store_create_unmatched_fi (); if (res == NULL) res = info; @@ -445,9 +503,13 @@ camel_vee_store_class_init (CamelVeeStoreClass *class) CamelServiceClass *service_class; CamelStoreClass *store_class; + g_type_class_add_private (class, sizeof (CamelVeeStorePrivate)); + object_class = G_OBJECT_CLASS (class); object_class->finalize = vee_store_finalize; object_class->constructed = vee_store_constructed; + object_class->get_property = vee_store_get_property; + object_class->set_property = vee_store_set_property; service_class = CAMEL_SERVICE_CLASS (class); service_class->get_name = vee_store_get_name; @@ -460,6 +522,16 @@ camel_vee_store_class_init (CamelVeeStoreClass *class) store_class->get_trash_folder_sync = vee_store_get_trash_folder_sync; store_class->delete_folder_sync = vee_store_delete_folder_sync; store_class->rename_folder_sync = vee_store_rename_folder_sync; + + g_object_class_install_property ( + object_class, + PROP_UNMATCHED_ENABLED, + g_param_spec_boolean ( + "unmatched-enabled", + "Unmatched Enabled", + _("Enable _Unmatched folder"), + TRUE, + G_PARAM_READWRITE)); } static void @@ -467,6 +539,10 @@ camel_vee_store_init (CamelVeeStore *vee_store) { CamelStore *store = CAMEL_STORE (vee_store); + vee_store->priv = G_TYPE_INSTANCE_GET_PRIVATE (vee_store, CAMEL_TYPE_VEE_STORE, CamelVeeStorePrivate); + vee_store->priv->vee_data_cache = camel_vee_data_cache_new (); + vee_store->priv->unmatched_enabled = TRUE; + /* we dont want a vtrash/vjunk on this one */ store->flags &= ~(CAMEL_STORE_VTRASH | CAMEL_STORE_VJUNK); } @@ -483,3 +559,399 @@ camel_vee_store_new (void) { return g_object_new (CAMEL_TYPE_VEE_STORE, NULL); } + +CamelVeeDataCache * +camel_vee_store_get_vee_data_cache (CamelVeeStore *vstore) +{ + g_return_val_if_fail (CAMEL_IS_VEE_STORE (vstore), NULL); + + return vstore->priv->vee_data_cache; +} + +CamelVeeFolder * +camel_vee_store_get_unmatched_folder (CamelVeeStore *vstore) +{ + g_return_val_if_fail (CAMEL_IS_VEE_STORE (vstore), NULL); + + if (!camel_vee_store_get_unmatched_enabled (vstore)) + return NULL; + + return vstore->priv->unmatched_folder; +} + +gboolean +camel_vee_store_get_unmatched_enabled (CamelVeeStore *vstore) +{ + g_return_val_if_fail (CAMEL_IS_VEE_STORE (vstore), FALSE); + + return vstore->priv->unmatched_enabled; +} + +void +camel_vee_store_set_unmatched_enabled (CamelVeeStore *vstore, + gboolean is_enabled) +{ + CamelFolderInfo *fi_unmatched; + + g_return_if_fail (CAMEL_IS_VEE_STORE (vstore)); + + if ((vstore->priv->unmatched_enabled ? 1 : 0) == (is_enabled ? 1 : 0)) + return; + + vstore->priv->unmatched_enabled = is_enabled; + g_object_notify (G_OBJECT (vstore), "unmatched-enabled"); + + fi_unmatched = vee_store_create_unmatched_fi (); + + if (is_enabled) { + camel_store_folder_created (CAMEL_STORE (vstore), fi_unmatched); + camel_vee_store_rebuild_unmatched_folder (vstore, NULL, NULL); + } else { + camel_store_folder_deleted (CAMEL_STORE (vstore), fi_unmatched); + } + + camel_folder_info_free (fi_unmatched); +} + +struct AddToUnmatchedData +{ + CamelVeeFolder *unmatched_folder; + CamelFolderChangeInfo *changes; + gboolean unmatched_enabled; + GHashTable *vuid_usage_counts; +}; + +static void +add_to_unmatched_folder_cb (CamelVeeMessageInfoData *mi_data, + CamelFolder *subfolder, + gpointer user_data) +{ + struct AddToUnmatchedData *atud = user_data; + const gchar *vuid; + + g_return_if_fail (atud != NULL); + + vuid = camel_vee_message_info_data_get_vee_message_uid (mi_data); + g_hash_table_insert (atud->vuid_usage_counts, + (gpointer) camel_pstring_strdup (vuid), GINT_TO_POINTER (0)); + + if (atud->unmatched_enabled) + camel_vee_folder_add_vuid (atud->unmatched_folder, mi_data, atud->changes); +} + +void +camel_vee_store_note_subfolder_used (CamelVeeStore *vstore, + CamelFolder *subfolder, + CamelVeeFolder *used_by) +{ + gint counts; + + g_return_if_fail (CAMEL_IS_VEE_STORE (vstore)); + g_return_if_fail (CAMEL_IS_FOLDER (subfolder)); + g_return_if_fail (CAMEL_IS_VEE_FOLDER (used_by)); + + /* only real folders can be part of the unmatched folder */ + if (CAMEL_IS_VEE_FOLDER (subfolder) || + used_by == vstore->priv->unmatched_folder) + return; + + g_mutex_lock (vstore->priv->sf_counts_mutex); + + counts = GPOINTER_TO_INT (g_hash_table_lookup (vstore->priv->subfolder_usage_counts, subfolder)); + counts++; + g_hash_table_insert (vstore->priv->subfolder_usage_counts, subfolder, GINT_TO_POINTER (counts)); + + if (counts == 1) { + struct AddToUnmatchedData atud; + CamelFolder *unmatched_folder; + + camel_vee_data_cache_add_subfolder (vstore->priv->vee_data_cache, subfolder); + + g_mutex_lock (vstore->priv->vu_counts_mutex); + + /* all messages from the folder are unmatched at the beginning */ + atud.unmatched_folder = vstore->priv->unmatched_folder; + atud.changes = camel_folder_change_info_new (); + atud.unmatched_enabled = camel_vee_store_get_unmatched_enabled (vstore); + atud.vuid_usage_counts = vstore->priv->vuid_usage_counts; + + if (atud.unmatched_enabled) + camel_vee_folder_add_folder (vstore->priv->unmatched_folder, subfolder, NULL); + + unmatched_folder = CAMEL_FOLDER (atud.unmatched_folder); + + camel_folder_freeze (unmatched_folder); + + camel_vee_data_cache_foreach_message_info_data (vstore->priv->vee_data_cache, subfolder, + add_to_unmatched_folder_cb, &atud); + + camel_folder_thaw (unmatched_folder); + g_mutex_unlock (vstore->priv->vu_counts_mutex); + + if (camel_folder_change_info_changed (atud.changes)) + camel_folder_changed (unmatched_folder, atud.changes); + camel_folder_change_info_free (atud.changes); + } + + g_mutex_unlock (vstore->priv->sf_counts_mutex); +} + +static void +remove_vuid_count_record_cb (CamelVeeMessageInfoData *mi_data, + CamelFolder *subfolder, + gpointer user_data) +{ + GHashTable *vuid_usage_counts = user_data; + + g_return_if_fail (mi_data != NULL); + g_return_if_fail (user_data != NULL); + + g_hash_table_remove (vuid_usage_counts, camel_vee_message_info_data_get_vee_message_uid (mi_data)); +} + +void +camel_vee_store_note_subfolder_unused (CamelVeeStore *vstore, + CamelFolder *subfolder, + CamelVeeFolder *unused_by) +{ + gint counts; + + g_return_if_fail (CAMEL_IS_VEE_STORE (vstore)); + g_return_if_fail (CAMEL_IS_FOLDER (subfolder)); + g_return_if_fail (CAMEL_IS_VEE_FOLDER (unused_by)); + + /* only real folders can be part of the unmatched folder */ + if (CAMEL_IS_VEE_FOLDER (subfolder) || + unused_by == vstore->priv->unmatched_folder) + return; + + g_mutex_lock (vstore->priv->sf_counts_mutex); + + counts = GPOINTER_TO_INT (g_hash_table_lookup (vstore->priv->subfolder_usage_counts, subfolder)); + g_return_if_fail (counts > 0); + + counts--; + if (counts == 0) { + g_hash_table_remove (vstore->priv->subfolder_usage_counts, subfolder); + if (camel_vee_store_get_unmatched_enabled (vstore)) + camel_vee_folder_remove_folder (vstore->priv->unmatched_folder, subfolder, NULL); + + g_mutex_lock (vstore->priv->vu_counts_mutex); + camel_vee_data_cache_foreach_message_info_data (vstore->priv->vee_data_cache, subfolder, + remove_vuid_count_record_cb, vstore->priv->vuid_usage_counts); + g_mutex_unlock (vstore->priv->vu_counts_mutex); + + camel_vee_data_cache_remove_subfolder (vstore->priv->vee_data_cache, subfolder); + } else { + g_hash_table_insert (vstore->priv->subfolder_usage_counts, subfolder, GINT_TO_POINTER (counts)); + } + + g_mutex_unlock (vstore->priv->sf_counts_mutex); +} + +void +camel_vee_store_note_vuid_used (CamelVeeStore *vstore, + CamelVeeMessageInfoData *mi_data, + CamelVeeFolder *used_by) +{ + gint counts; + const gchar *vuid; + CamelFolder *subfolder; + CamelVeeSubfolderData *sf_data; + + g_return_if_fail (CAMEL_IS_VEE_STORE (vstore)); + g_return_if_fail (used_by != NULL); + g_return_if_fail (mi_data != NULL); + + /* these notifications are ignored from Unmatched folder */ + if (used_by == vstore->priv->unmatched_folder) + return; + + /* unmatched folder holds only real folders */ + sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data); + subfolder = camel_vee_subfolder_data_get_folder (sf_data); + if (CAMEL_IS_VEE_FOLDER (subfolder)) + return; + + g_mutex_lock (vstore->priv->vu_counts_mutex); + + vuid = camel_vee_message_info_data_get_vee_message_uid (mi_data); + + counts = GPOINTER_TO_INT (g_hash_table_lookup (vstore->priv->vuid_usage_counts, vuid)); + counts++; + g_hash_table_insert (vstore->priv->vuid_usage_counts, + (gpointer) camel_pstring_strdup (vuid), GINT_TO_POINTER (counts)); + + if (counts == 1 && camel_vee_store_get_unmatched_enabled (vstore)) { + CamelFolderChangeInfo *changes; + + changes = camel_folder_change_info_new (); + + camel_vee_folder_remove_vuid (vstore->priv->unmatched_folder, mi_data, changes); + + if (camel_folder_change_info_changed (changes)) + camel_folder_changed (CAMEL_FOLDER (vstore->priv->unmatched_folder), changes); + camel_folder_change_info_free (changes); + } + + g_mutex_unlock (vstore->priv->vu_counts_mutex); +} + +void +camel_vee_store_note_vuid_unused (CamelVeeStore *vstore, + CamelVeeMessageInfoData *mi_data, + CamelVeeFolder *unused_by) +{ + gint counts; + const gchar *vuid; + CamelFolder *subfolder; + CamelVeeSubfolderData *sf_data; + + g_return_if_fail (CAMEL_IS_VEE_STORE (vstore)); + g_return_if_fail (unused_by != NULL); + g_return_if_fail (mi_data != NULL); + + /* these notifications are ignored from Unmatched folder */ + if (unused_by == vstore->priv->unmatched_folder) + return; + + /* unmatched folder holds only real folders */ + sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data); + subfolder = camel_vee_subfolder_data_get_folder (sf_data); + if (CAMEL_IS_VEE_FOLDER (subfolder)) + return; + + g_mutex_lock (vstore->priv->vu_counts_mutex); + + vuid = camel_vee_message_info_data_get_vee_message_uid (mi_data); + + counts = GPOINTER_TO_INT (g_hash_table_lookup (vstore->priv->vuid_usage_counts, vuid)); + counts--; + if (counts < 0) { + g_mutex_unlock (vstore->priv->vu_counts_mutex); + g_return_if_fail (counts >= 0); + return; + } + + g_hash_table_insert (vstore->priv->vuid_usage_counts, + (gpointer) camel_pstring_strdup (vuid), GINT_TO_POINTER (counts)); + + if (counts == 0 && camel_vee_store_get_unmatched_enabled (vstore)) { + CamelFolderChangeInfo *changes; + + changes = camel_folder_change_info_new (); + + camel_vee_folder_add_vuid (vstore->priv->unmatched_folder, mi_data, changes); + + if (camel_folder_change_info_changed (changes)) + camel_folder_changed (CAMEL_FOLDER (vstore->priv->unmatched_folder), changes); + camel_folder_change_info_free (changes); + } + + g_mutex_unlock (vstore->priv->vu_counts_mutex); +} + +struct RebuildUnmatchedData +{ + CamelVeeDataCache *data_cache; + CamelVeeFolder *unmatched_folder; + CamelFolderChangeInfo *changes; + GCancellable *cancellable; +}; + +static void +rebuild_unmatched_folder_cb (gpointer key, + gpointer value, + gpointer user_data) +{ + const gchar *vuid = key; + gint counts = GPOINTER_TO_INT (value); + struct RebuildUnmatchedData *rud = user_data; + CamelVeeSubfolderData *si_data; + CamelVeeMessageInfoData *mi_data; + + g_return_if_fail (vuid != NULL); + g_return_if_fail (rud != NULL); + + if (counts != 0 || g_cancellable_is_cancelled (rud->cancellable)) + return; + + mi_data = camel_vee_data_cache_get_message_info_data_by_vuid (rud->data_cache, vuid); + if (!mi_data) + return; + + si_data = camel_vee_message_info_data_get_subfolder_data (mi_data); + + camel_vee_folder_add_folder (rud->unmatched_folder, camel_vee_subfolder_data_get_folder (si_data), NULL); + camel_vee_folder_add_vuid (rud->unmatched_folder, mi_data, rud->changes); + + g_object_unref (mi_data); +} + +static void +vee_store_rebuild_unmatched_folder (CamelSession *session, + GCancellable *cancellable, + CamelVeeStore *vstore, + GError **error) +{ + struct RebuildUnmatchedData rud; + CamelVeeFolder *vunmatched; + CamelFolder *unmatched_folder; + CamelFolderChangeInfo *changes; + + g_return_if_fail (CAMEL_IS_VEE_STORE (vstore)); + + vunmatched = camel_vee_store_get_unmatched_folder (vstore); + /* someone could disable it meanwhile */ + if (!vunmatched) + return; + + unmatched_folder = CAMEL_FOLDER (vunmatched); + g_return_if_fail (unmatched_folder != NULL); + + camel_folder_freeze (unmatched_folder); + + /* start from scratch, with empty folder */ + camel_vee_folder_set_folders (vunmatched, NULL, cancellable); + + changes = camel_folder_change_info_new (); + + rud.data_cache = vstore->priv->vee_data_cache; + rud.unmatched_folder = vunmatched; + rud.changes = changes; + rud.cancellable = cancellable; + + g_hash_table_foreach (vstore->priv->vuid_usage_counts, rebuild_unmatched_folder_cb, &rud); + + camel_folder_thaw (unmatched_folder); + + if (camel_folder_change_info_changed (changes)) + camel_folder_changed (unmatched_folder, changes); + camel_folder_change_info_free (changes); + + g_cancellable_set_error_if_cancelled (cancellable, error); +} + +void +camel_vee_store_rebuild_unmatched_folder (CamelVeeStore *vstore, + GCancellable *cancellable, + GError **error) +{ + g_return_if_fail (CAMEL_IS_VEE_STORE (vstore)); + + /* this operation requires cancellable, thus if called + without it then run in a dedicated thread */ + if (!cancellable) { + CamelSession *session; + + session = camel_service_get_session (CAMEL_SERVICE (vstore)); + + camel_session_submit_job ( + session, (CamelSessionCallback) + vee_store_rebuild_unmatched_folder, + g_object_ref (vstore), + g_object_unref); + } else { + vee_store_rebuild_unmatched_folder (NULL, cancellable, vstore, error); + } +} diff --git a/camel/camel-vee-store.h b/camel/camel-vee-store.h index 890e2cf..69fdb5b 100644 --- a/camel/camel-vee-store.h +++ b/camel/camel-vee-store.h @@ -27,6 +27,7 @@ #define CAMEL_VEE_STORE_H #include +#include /* Standard GObject macros */ #define CAMEL_TYPE_VEE_STORE \ @@ -50,32 +51,41 @@ G_BEGIN_DECLS typedef struct _CamelVeeStore CamelVeeStore; +typedef struct _CamelVeeStorePrivate CamelVeeStorePrivate; typedef struct _CamelVeeStoreClass CamelVeeStoreClass; -/* open mode for folder, vee folder auto-update */ -#define CAMEL_STORE_VEE_FOLDER_AUTO (1 << 16) - -/** - * CAMEL_STORE_VEE_FOLDER_SPECIAL: - * - * Since: 2.24 - **/ -#define CAMEL_STORE_VEE_FOLDER_SPECIAL (1 << 17) - struct _CamelVeeStore { CamelStore parent; - /* Unmatched folder, set up in camel_vee_store_init */ - struct _CamelVeeFolder *folder_unmatched; - GHashTable *unmatched_uids; + CamelVeeStorePrivate *priv; }; struct _CamelVeeStoreClass { CamelStoreClass parent_class; }; -GType camel_vee_store_get_type (void); -CamelVeeStore * camel_vee_store_new (void); +GType camel_vee_store_get_type (void); +CamelVeeStore * camel_vee_store_new (void); +CamelVeeDataCache * camel_vee_store_get_vee_data_cache (CamelVeeStore *vstore); +struct _CamelVeeFolder *camel_vee_store_get_unmatched_folder (CamelVeeStore *vstore); +gboolean camel_vee_store_get_unmatched_enabled (CamelVeeStore *vstore); +void camel_vee_store_set_unmatched_enabled (CamelVeeStore *vstore, + gboolean is_enabled); +void camel_vee_store_note_subfolder_used (CamelVeeStore *vstore, + CamelFolder *subfolder, + struct _CamelVeeFolder *used_by); +void camel_vee_store_note_subfolder_unused (CamelVeeStore *vstore, + CamelFolder *subfolder, + struct _CamelVeeFolder *unused_by); +void camel_vee_store_note_vuid_used (CamelVeeStore *vstore, + CamelVeeMessageInfoData *mi_data, + struct _CamelVeeFolder *used_by); +void camel_vee_store_note_vuid_unused (CamelVeeStore *vstore, + CamelVeeMessageInfoData *mi_data, + struct _CamelVeeFolder *unused_by); +void camel_vee_store_rebuild_unmatched_folder (CamelVeeStore *vstore, + GCancellable *cancellable, + GError **error); G_END_DECLS diff --git a/camel/camel-vee-summary.c b/camel/camel-vee-summary.c index e638ed7..b9a7a11 100644 --- a/camel/camel-vee-summary.c +++ b/camel/camel-vee-summary.c @@ -41,6 +41,11 @@ #define d(x) +struct _CamelVeeSummaryPrivate +{ + GHashTable *vuids_by_subfolder; /* CamelFolder * => GHashTable * of gchar *vuid */ +}; + G_DEFINE_TYPE (CamelVeeSummary, camel_vee_summary, CAMEL_TYPE_FOLDER_SUMMARY) static void @@ -49,8 +54,9 @@ vee_message_info_free (CamelFolderSummary *s, { CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *) info; - camel_pstring_free (info->uid); g_object_unref (mi->orig_summary); + + CAMEL_FOLDER_SUMMARY_CLASS (camel_vee_summary_parent_class)->message_info_free (s, info); } static CamelMessageInfo * @@ -144,6 +150,22 @@ vee_info_user_tag (const CamelMessageInfo *mi, return ret; } +static void +vee_summary_notify_mi_changed (CamelVeeFolder *vfolder, + CamelMessageInfo *mi) +{ + CamelFolderChangeInfo *changes; + + g_return_if_fail (vfolder != NULL); + g_return_if_fail (mi != NULL); + + changes = camel_folder_change_info_new (); + + camel_folder_change_info_change_uid (changes, camel_message_info_uid (mi)); + camel_folder_changed (CAMEL_FOLDER (vfolder), changes); + camel_folder_change_info_free (changes); +} + static gboolean vee_info_set_user_flag (CamelMessageInfo *mi, const gchar *name, @@ -152,25 +174,23 @@ vee_info_set_user_flag (CamelMessageInfo *mi, gint res = FALSE; CamelVeeFolder *vf = (CamelVeeFolder *) camel_folder_summary_get_folder (mi->summary); - if (camel_debug("vfolderexp")) - printf ( - "Expression for vfolder '%s' is '%s'\n", - camel_folder_get_full_name (camel_folder_summary_get_folder (mi->summary)), - g_strescape (vf->expression, "")); - if (mi->uid) { CamelMessageInfo *rmi = camel_folder_summary_get (((CamelVeeMessageInfo *) mi)->orig_summary, mi->uid + 8); + gboolean ignore_changes = !CAMEL_IS_VTRASH_FOLDER (vf); HANDLE_NULL_INFO (FALSE); /* ignore changes done in the folder itself, * unless it's a vTrash or vJunk folder */ - if (!CAMEL_IS_VTRASH_FOLDER (vf)) + if (ignore_changes) camel_vee_folder_ignore_next_changed_event (vf, camel_folder_summary_get_folder (rmi->summary)); res = camel_message_info_set_user_flag (rmi, name, value); camel_message_info_free (rmi); + + if (ignore_changes) + vee_summary_notify_mi_changed (vf, mi); } return res; @@ -185,17 +205,21 @@ vee_info_set_user_tag (CamelMessageInfo *mi, if (mi->uid) { CamelMessageInfo *rmi = camel_folder_summary_get (((CamelVeeMessageInfo *) mi)->orig_summary, mi->uid + 8); - CamelFolder *folder = camel_folder_summary_get_folder (mi->summary); + CamelVeeFolder *vf = (CamelVeeFolder *) camel_folder_summary_get_folder (mi->summary); + gboolean ignore_changes = !CAMEL_IS_VTRASH_FOLDER (vf); HANDLE_NULL_INFO (FALSE); /* ignore changes done in the folder itself, * unless it's a vTrash or vJunk folder */ - if (!CAMEL_IS_VTRASH_FOLDER (folder)) - camel_vee_folder_ignore_next_changed_event ((CamelVeeFolder *) folder, camel_folder_summary_get_folder (rmi->summary)); + if (ignore_changes) + camel_vee_folder_ignore_next_changed_event (vf, camel_folder_summary_get_folder (rmi->summary)); res = camel_message_info_set_user_tag (rmi, name, value); camel_message_info_free (rmi); + + if (ignore_changes) + vee_summary_notify_mi_changed (vf, mi); } return res; @@ -209,21 +233,16 @@ vee_info_set_flags (CamelMessageInfo *mi, gint res = FALSE; CamelVeeFolder *vf = CAMEL_VEE_FOLDER (camel_folder_summary_get_folder (mi->summary)); - if (camel_debug("vfolderexp")) - printf ( - "Expression for vfolder '%s' is '%s'\n", - camel_folder_get_full_name (CAMEL_FOLDER (vf)), - g_strescape (vf->expression, "")); - /* first update original message info... */ if (mi->uid) { CamelMessageInfo *rmi = camel_folder_summary_get (((CamelVeeMessageInfo *) mi)->orig_summary, mi->uid + 8); + gboolean ignore_changes = !CAMEL_IS_VTRASH_FOLDER (vf); HANDLE_NULL_INFO (FALSE); /* ignore changes done in the folder itself, * unless it's a vTrash or vJunk folder */ - if (!CAMEL_IS_VTRASH_FOLDER (vf)) + if (ignore_changes) camel_vee_folder_ignore_next_changed_event (vf, camel_folder_summary_get_folder (rmi->summary)); camel_folder_freeze (camel_folder_summary_get_folder (rmi->summary)); @@ -231,11 +250,24 @@ vee_info_set_flags (CamelMessageInfo *mi, ((CamelVeeMessageInfo *) mi)->old_flags = camel_message_info_flags (rmi); camel_folder_thaw (camel_folder_summary_get_folder (rmi->summary)); + if (res) { + /* update flags on itself too */ + camel_folder_summary_replace_flags (mi->summary, mi); + } + camel_message_info_free (rmi); + + if (ignore_changes) + vee_summary_notify_mi_changed (vf, mi); } - if (res) - CAMEL_FOLDER_SUMMARY_CLASS (camel_vee_summary_parent_class)->info_set_flags (mi, flags, set); + /* Do not call parent class' info_set_flags, to not do flood + of change notifications, rather wait for a notification + from original folder, and propagate the change in counts + through camel_vee_summary_replace_flags(). + */ + /*if (res) + CAMEL_FOLDER_SUMMARY_CLASS (camel_vee_summary_parent_class)->info_set_flags (mi, flags, set);*/ return res; } @@ -249,7 +281,7 @@ message_info_from_uid (CamelFolderSummary *s, info = camel_folder_summary_peek_loaded (s, uid); if (!info) { CamelVeeMessageInfo *vinfo; - gchar tmphash[9]; + CamelFolder *orig_folder; /* This function isn't really nice. But no great way * But in vfolder case, this may not be so bad, as vuid has the hash in first 8 bytes. @@ -264,14 +296,19 @@ message_info_from_uid (CamelFolderSummary *s, /* Create the info and load it, its so easy. */ info = camel_message_info_new (s); - camel_message_info_ref (info); info->dirty = FALSE; - vinfo = (CamelVeeMessageInfo *) info; info->uid = camel_pstring_strdup (uid); - strncpy (tmphash, uid, 8); - tmphash[8] = 0; - vinfo->orig_summary = g_hash_table_lookup (((CamelVeeFolder *) camel_folder_summary_get_folder (s))->hashes, tmphash); + + orig_folder = camel_vee_folder_get_vee_uid_folder ( + (CamelVeeFolder *) camel_folder_summary_get_folder (s), uid); + g_return_val_if_fail (orig_folder != NULL, NULL); + + vinfo = (CamelVeeMessageInfo *) info; + vinfo->orig_summary = orig_folder->summary; + g_object_ref (vinfo->orig_summary); + camel_message_info_ref (info); + camel_folder_summary_insert (s, info, FALSE); } @@ -279,10 +316,27 @@ message_info_from_uid (CamelFolderSummary *s, } static void +vee_summary_finalize (GObject *object) +{ + CamelVeeSummary *vsummary = CAMEL_VEE_SUMMARY (object); + + g_hash_table_destroy (vsummary->priv->vuids_by_subfolder); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (camel_vee_summary_parent_class)->finalize (object); +} + +static void camel_vee_summary_class_init (CamelVeeSummaryClass *class) { + GObjectClass *object_class; CamelFolderSummaryClass *folder_summary_class; + g_type_class_add_private (class, sizeof (CamelVeeSummaryPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = vee_summary_finalize; + folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (class); folder_summary_class->message_info_size = sizeof (CamelVeeMessageInfo); folder_summary_class->content_info_size = 0; @@ -302,6 +356,11 @@ camel_vee_summary_class_init (CamelVeeSummaryClass *class) static void camel_vee_summary_init (CamelVeeSummary *vee_summary) { + vee_summary->priv = G_TYPE_INSTANCE_GET_PRIVATE (vee_summary, + CAMEL_TYPE_VEE_SUMMARY, CamelVeeSummaryPrivate); + + vee_summary->priv->vuids_by_subfolder = + g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_hash_table_destroy); } /** @@ -316,81 +375,171 @@ camel_vee_summary_init (CamelVeeSummary *vee_summary) CamelFolderSummary * camel_vee_summary_new (CamelFolder *parent) { - CamelVeeSummary *s; + CamelFolderSummary *summary; CamelStore *parent_store; const gchar *full_name; - s = g_object_new (CAMEL_TYPE_VEE_SUMMARY, "folder", parent, NULL); + summary = g_object_new (CAMEL_TYPE_VEE_SUMMARY, "folder", parent, NULL); + summary->flags |= CAMEL_FOLDER_SUMMARY_IN_MEMORY_ONLY; + /* not using DB for vee folder summaries, drop the table */ full_name = camel_folder_get_full_name (parent); parent_store = camel_folder_get_parent_store (parent); - camel_db_create_vfolder (parent_store->cdb_w, full_name, NULL); + camel_db_delete_folder (parent_store->cdb_w, full_name, NULL); - return (CamelFolderSummary *) s; + return summary; } -/** - * camel_vee_summary_get_ids: - * - * Since: 2.24 - **/ -GPtrArray * -camel_vee_summary_get_ids (CamelVeeSummary *summary, - gchar hash[8]) +static void +get_uids_for_subfolder (gpointer key, + gpointer value, + gpointer user_data) { - gchar *shash = g_strdup_printf("%c%c%c%c%c%c%c%c", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7]); - CamelFolderSummary *cfs = (CamelFolderSummary *) summary; - CamelStore *parent_store; - GPtrArray *array; - const gchar *full_name; + g_hash_table_insert (user_data, (gpointer) camel_pstring_strdup (key), GINT_TO_POINTER (1)); +} + +GHashTable * +camel_vee_summary_get_uids_for_subfolder (CamelVeeSummary *summary, + CamelFolder *subfolder) +{ + GHashTable *vuids, *known_uids; - /* FIXME[disk-summary] fix exception passing */ - full_name = camel_folder_get_full_name (camel_folder_summary_get_folder (cfs)); - parent_store = camel_folder_get_parent_store (camel_folder_summary_get_folder (cfs)); - array = camel_db_get_vuids_from_vfolder (parent_store->cdb_r, full_name, shash, NULL); + g_return_val_if_fail (CAMEL_IS_VEE_SUMMARY (summary), NULL); + g_return_val_if_fail (CAMEL_IS_FOLDER (subfolder), NULL); - g_free (shash); + camel_folder_summary_lock (&summary->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); - return array; + /* uses direct hash, because strings are supposed to be from the string pool */ + known_uids = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) camel_pstring_free, NULL); + + vuids = g_hash_table_lookup (summary->priv->vuids_by_subfolder, subfolder); + if (vuids) { + g_hash_table_foreach (vuids, get_uids_for_subfolder, known_uids); + } + + camel_folder_summary_unlock (&summary->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); + + return known_uids; } +/* free returned pointer with camel_message_info_free() */ CamelVeeMessageInfo * camel_vee_summary_add (CamelVeeSummary *s, - CamelFolderSummary *summary, - const gchar *uid, - const gchar hash[8]) + CamelVeeMessageInfoData *mi_data) { - CamelVeeMessageInfo *mi; + CamelVeeMessageInfo *vmi; CamelMessageInfo *rmi; - gchar *vuid; - vuid = g_malloc (strlen (uid) + 9); - memcpy (vuid, hash, 8); - strcpy (vuid + 8, uid); + const gchar *uid, *vuid; + CamelVeeSubfolderData *sf_data; + CamelFolder *orig_folder; + GHashTable *vuids; + + g_return_val_if_fail (CAMEL_IS_VEE_SUMMARY (s), NULL); + g_return_val_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (mi_data), NULL); - mi = (CamelVeeMessageInfo *) camel_folder_summary_peek_loaded (&s->summary, vuid); - if (mi) { + camel_folder_summary_lock (&s->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); + + sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data); + uid = camel_vee_message_info_data_get_orig_message_uid (mi_data); + vuid = camel_vee_message_info_data_get_vee_message_uid (mi_data); + orig_folder = camel_vee_subfolder_data_get_folder (sf_data); + + vmi = (CamelVeeMessageInfo *) camel_folder_summary_peek_loaded (&s->summary, vuid); + if (vmi) { /* Possible that the entry is loaded, see if it has the summary */ d(g_message ("%s - already there\n", vuid)); - g_free (vuid); - if (!mi->orig_summary) - mi->orig_summary = g_object_ref (summary); - return mi; + if (!vmi->orig_summary) + vmi->orig_summary = g_object_ref (orig_folder->summary); + + camel_folder_summary_unlock (&s->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); + + return vmi; } - mi = (CamelVeeMessageInfo *) camel_message_info_new (&s->summary); - mi->orig_summary = g_object_ref (summary); - mi->info.uid = (gchar *) camel_pstring_strdup (vuid); - g_free (vuid); - camel_message_info_ref (mi); + vmi = (CamelVeeMessageInfo *) camel_message_info_new (&s->summary); + vmi->orig_summary = g_object_ref (orig_folder->summary); + vmi->info.uid = (gchar *) camel_pstring_strdup (vuid); + + camel_message_info_ref (vmi); /* Get actual flags and store it */ - rmi = camel_folder_summary_get (summary, uid); + rmi = camel_folder_summary_get (orig_folder->summary, uid); if (rmi) { - mi->old_flags = camel_message_info_flags (rmi); + vmi->old_flags = camel_message_info_flags (rmi); camel_message_info_free (rmi); } - camel_folder_summary_insert (&s->summary, (CamelMessageInfo *) mi, FALSE); + vuids = g_hash_table_lookup (s->priv->vuids_by_subfolder, orig_folder); + if (vuids) { + g_hash_table_insert (vuids, (gpointer) camel_pstring_strdup (vuid), GINT_TO_POINTER (1)); + } else { + vuids = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) camel_pstring_free, NULL); + g_hash_table_insert (vuids, (gpointer) camel_pstring_strdup (vuid), GINT_TO_POINTER (1)); + g_hash_table_insert (s->priv->vuids_by_subfolder, orig_folder, vuids); + } + + camel_folder_summary_insert (&s->summary, (CamelMessageInfo *) vmi, FALSE); + camel_folder_summary_unlock (&s->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); + + return vmi; +} + +void +camel_vee_summary_remove (CamelVeeSummary *summary, + const gchar *vuid, + CamelFolder *subfolder) +{ + GHashTable *vuids; + + g_return_if_fail (CAMEL_IS_VEE_SUMMARY (summary)); + g_return_if_fail (vuid != NULL); + g_return_if_fail (subfolder != NULL); + + camel_folder_summary_lock (&summary->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); + + vuids = g_hash_table_lookup (summary->priv->vuids_by_subfolder, subfolder); + if (vuids) { + g_hash_table_remove (vuids, vuid); + if (!g_hash_table_size (vuids)) + g_hash_table_remove (summary->priv->vuids_by_subfolder, subfolder); + } + camel_folder_summary_remove_uid (&summary->summary, vuid); + + camel_folder_summary_unlock (&summary->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); +} + +/** + * camel_vee_summary_replace_flags: + * @summary: a #CamelVeeSummary + * @uid: a message UID to update flags for + * + * Makes sure @summary flags on @uid corresponds to those + * in the subfolder of vee-folder, and updates internal counts + * on @summary as well. + **/ +void +camel_vee_summary_replace_flags (CamelVeeSummary *summary, + const gchar *uid) +{ + CamelMessageInfo *mi; + CamelVeeMessageInfo *vmi; + + g_return_if_fail (CAMEL_IS_VEE_SUMMARY (summary)); + g_return_if_fail (uid != NULL); + + camel_folder_summary_lock (&summary->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); + + mi = camel_folder_summary_get (&summary->summary, uid); + if (!mi) { + camel_folder_summary_unlock (&summary->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); + return; + } + + vmi = (CamelVeeMessageInfo *) mi; + vmi->old_flags = camel_message_info_flags (mi); + + camel_folder_summary_replace_flags (&summary->summary, mi); + camel_message_info_free (mi); - return mi; + camel_folder_summary_unlock (&summary->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK); } diff --git a/camel/camel-vee-summary.h b/camel/camel-vee-summary.h index 2667e7a..4b866ea 100644 --- a/camel/camel-vee-summary.h +++ b/camel/camel-vee-summary.h @@ -50,11 +50,13 @@ G_BEGIN_DECLS +struct _CamelVeeMessageInfoData; struct _CamelVeeFolder; struct _CamelFolder; typedef struct _CamelVeeSummary CamelVeeSummary; typedef struct _CamelVeeSummaryClass CamelVeeSummaryClass; +typedef struct _CamelVeeSummaryPrivate CamelVeeSummaryPrivate; typedef struct _CamelVeeMessageInfo CamelVeeMessageInfo; @@ -66,6 +68,8 @@ struct _CamelVeeMessageInfo { struct _CamelVeeSummary { CamelFolderSummary summary; + + CamelVeeSummaryPrivate *priv; }; struct _CamelVeeSummaryClass { @@ -77,11 +81,15 @@ CamelFolderSummary * camel_vee_summary_new (struct _CamelFolder *parent); CamelVeeMessageInfo * camel_vee_summary_add (CamelVeeSummary *s, - CamelFolderSummary *summary, - const gchar *uid, - const gchar hash[8]); -GPtrArray * camel_vee_summary_get_ids (CamelVeeSummary *summary, - gchar hash[8]); + struct _CamelVeeMessageInfoData *mi_data); +void camel_vee_summary_remove (CamelVeeSummary *summary, + const gchar *vuid, + CamelFolder *subfolder); +void camel_vee_summary_replace_flags (CamelVeeSummary *summary, + const gchar *uid); +GHashTable * camel_vee_summary_get_uids_for_subfolder + (CamelVeeSummary *summary, + CamelFolder *subfolder); G_END_DECLS diff --git a/camel/camel-vtrash-folder.c b/camel/camel-vtrash-folder.c index fff4a39..223eb57 100644 --- a/camel/camel-vtrash-folder.c +++ b/camel/camel-vtrash-folder.c @@ -260,9 +260,7 @@ camel_vtrash_folder_new (CamelStore *parent_store, camel_vee_folder_construct ( CAMEL_VEE_FOLDER (vtrash), CAMEL_STORE_FOLDER_PRIVATE | - CAMEL_STORE_FOLDER_CREATE | - CAMEL_STORE_VEE_FOLDER_AUTO | - CAMEL_STORE_VEE_FOLDER_SPECIAL); + CAMEL_STORE_FOLDER_CREATE); ((CamelFolder *) vtrash)->folder_flags |= vdata[type].flags; camel_vee_folder_set_expression ((CamelVeeFolder *) vtrash, vdata[type].expr); diff --git a/camel/camel.h b/camel/camel.h index e21246f..ceec583 100644 --- a/camel/camel.h +++ b/camel/camel.h @@ -137,6 +137,7 @@ #include #include #include +#include #include #include #include diff --git a/camel/providers/imap/camel-imap-folder.c b/camel/providers/imap/camel-imap-folder.c index ee3f5e6..9570fd7 100644 --- a/camel/providers/imap/camel-imap-folder.c +++ b/camel/providers/imap/camel-imap-folder.c @@ -1505,7 +1505,7 @@ imap_sync_offline (CamelFolder *folder, parent_store = camel_folder_get_parent_store (folder); - if (folder->summary && (folder->summary->flags & CAMEL_SUMMARY_DIRTY) != 0) { + if (folder->summary && (folder->summary->flags & CAMEL_FOLDER_SUMMARY_DIRTY) != 0) { CamelStoreInfo *si; const gchar *full_name; -- 2.7.4