Simplify vFolder's code and other bunch of related time optimizations
authorMilan Crha <mcrha@redhat.com>
Wed, 30 May 2012 16:45:01 +0000 (18:45 +0200)
committerMilan Crha <mcrha@redhat.com>
Wed, 30 May 2012 16:45:01 +0000 (18:45 +0200)
23 files changed:
camel/Makefile.am
camel/camel-db.c
camel/camel-db.h
camel/camel-folder-search.c
camel/camel-folder-summary.c
camel/camel-folder-summary.h
camel/camel-folder.c
camel/camel-imapx-server.c
camel/camel-store.c
camel/camel-string-utils.c
camel/camel-string-utils.h
camel/camel-subscribable.c
camel/camel-vee-data-cache.c [new file with mode: 0644]
camel/camel-vee-data-cache.h [new file with mode: 0644]
camel/camel-vee-folder.c
camel/camel-vee-folder.h
camel/camel-vee-store.c
camel/camel-vee-store.h
camel/camel-vee-summary.c
camel/camel-vee-summary.h
camel/camel-vtrash-folder.c
camel/camel.h
camel/providers/imap/camel-imap-folder.c

index e9ceed4..d1652dc 100644 (file)
@@ -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                     \
index 8fb123e..c8ef256 100644 (file)
@@ -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
index 33be066..deb9a11 100644 (file)
@@ -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);
index c72acc8..ab96996 100644 (file)
@@ -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;
                }
 
index 8dad22c..a80ca37 100644 (file)
@@ -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;
 }
index dacacab..50f335e 100644 (file)
@@ -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,
index 633a41a..a1c5525 100644 (file)
@@ -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));
 
index 9f65e49..a52f521 100644 (file)
@@ -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 */
index 8e55069..12fa79d 100644 (file)
@@ -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);
                }
 
index 817c1d0..76971d9 100644 (file)
@@ -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);
+}
index c84ca38..d6b11e6 100644 (file)
@@ -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
 
index 0bb193b..ba1eed0 100644 (file)
@@ -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 (file)
index 0000000..b0a1284
--- /dev/null
@@ -0,0 +1,638 @@
+/*
+ *  Copyright (C) 2012 Red Hat, Inc. (www.redhat.com)
+ *
+ *  Authors: Milan Crha <mcrha@redhat.com>
+ *
+ * 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 <config.h>
+#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 (file)
index 0000000..99249f5
--- /dev/null
@@ -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 <mcrha@redhat.com>
+ *
+ * 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 <camel/camel.h> can be included directly."
+#endif
+
+#ifndef CAMEL_VEE_DATA_CACHE_H
+#define CAMEL_VEE_DATA_CACHE_H
+
+#include <camel/camel-folder.h>
+
+/* 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 */
index c66b40a..82aa589 100644 (file)
@@ -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 ();
index 8999c1a..ea452ef 100644 (file)
@@ -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
 
index 7e30e99..bd8f481 100644 (file)
@@ -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"
 
 #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);
+       }
+}
index 890e2cf..69fdb5b 100644 (file)
@@ -27,6 +27,7 @@
 #define CAMEL_VEE_STORE_H
 
 #include <camel/camel-store.h>
+#include <camel/camel-vee-data-cache.h>
 
 /* Standard GObject macros */
 #define CAMEL_TYPE_VEE_STORE \
 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
 
index e638ed7..b9a7a11 100644 (file)
 
 #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);
 }
index 2667e7a..4b866ea 100644 (file)
 
 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
 
index fff4a39..223eb57 100644 (file)
@@ -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);
index e21246f..ceec583 100644 (file)
 #include <camel/camel-url.h>
 #include <camel/camel-url-scanner.h>
 #include <camel/camel-utf8.h>
+#include <camel/camel-vee-data-cache.h>
 #include <camel/camel-vee-folder.h>
 #include <camel/camel-vee-store.h>
 #include <camel/camel-vee-summary.h>
index ee3f5e6..9570fd7 100644 (file)
@@ -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;