[evolution-data-server] Simplify vFolder's code and other bunch of related time optimizations



commit 76d8bbcdc47c6cf2c343d21a694110026ecab98d
Author: Milan Crha <mcrha redhat com>
Date:   Wed May 30 18:45:01 2012 +0200

    Simplify vFolder's code and other bunch of related time optimizations

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



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]