Blob Blame History Raw
diff -up evolution-data-server-3.12.11/camel/camel-enums.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/camel-enums.h
--- evolution-data-server-3.12.11/camel/camel-enums.h.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/camel-enums.h	2016-08-15 13:52:41.939976331 +0200
@@ -410,7 +410,8 @@ typedef enum { /*< flags >*/
 	CAMEL_STORE_REAL_JUNK_FOLDER = 1 << 4,
 	CAMEL_STORE_CAN_EDIT_FOLDERS = 1 << 5,
 	CAMEL_STORE_USE_CACHE_DIR = 1 << 6,
-	CAMEL_STORE_CAN_DELETE_FOLDERS_AT_ONCE = 1 << 7
+	CAMEL_STORE_CAN_DELETE_FOLDERS_AT_ONCE = 1 << 7,
+	CAMEL_STORE_SUPPORTS_INITIAL_SETUP = 1 << 8
 } CamelStoreFlags;
 
 /**
diff -up evolution-data-server-3.12.11/camel/camel-offline-store.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/camel-offline-store.c
--- evolution-data-server-3.12.11/camel/camel-offline-store.c.imapx-update-to-upstream	2014-10-31 15:25:33.000000000 +0100
+++ evolution-data-server-3.12.11/camel/camel-offline-store.c	2016-08-15 13:52:41.939976331 +0200
@@ -181,7 +181,7 @@ camel_offline_store_set_online_sync (Cam
 
 	g_return_val_if_fail (CAMEL_IS_OFFLINE_STORE (store), FALSE);
 
-	if (store->priv->online == online)
+	if (camel_offline_store_get_online (store) == online)
 		return TRUE;
 
 	service = CAMEL_SERVICE (store);
diff -up evolution-data-server-3.12.11/camel/camel-operation.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/camel-operation.c
--- evolution-data-server-3.12.11/camel/camel-operation.c.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/camel-operation.c	2016-08-15 13:52:41.939976331 +0200
@@ -49,6 +49,9 @@ struct _CamelOperationPrivate {
 
 enum {
 	STATUS,
+	PUSH_MESSAGE,
+	POP_MESSAGE,
+	PROGRESS,
 	LAST_SIGNAL
 };
 
@@ -173,6 +176,32 @@ camel_operation_class_init (CamelOperati
 		G_TYPE_NONE, 2,
 		G_TYPE_STRING,
 		G_TYPE_INT);
+
+	signals[PUSH_MESSAGE] = g_signal_new (
+		"push-message",
+		G_TYPE_FROM_CLASS (class),
+		G_SIGNAL_RUN_LAST,
+		0,
+		NULL, NULL, NULL,
+		G_TYPE_NONE, 1,
+		G_TYPE_STRING);
+
+	signals[POP_MESSAGE] = g_signal_new (
+		"pop-message",
+		G_TYPE_FROM_CLASS (class),
+		G_SIGNAL_RUN_LAST,
+		0,
+		NULL, NULL, NULL,
+		G_TYPE_NONE, 0);
+
+	signals[PROGRESS] = g_signal_new (
+		"progress",
+		G_TYPE_FROM_CLASS (class),
+		G_SIGNAL_RUN_LAST,
+		0,
+		NULL, NULL, NULL,
+		G_TYPE_NONE, 1,
+		G_TYPE_INT);
 }
 
 static void
@@ -249,6 +278,7 @@ camel_operation_push_message (GCancellab
 {
 	CamelOperation *operation;
 	StatusNode *node;
+	gchar *message;
 	va_list ap;
 
 	if (cancellable == NULL)
@@ -259,14 +289,18 @@ camel_operation_push_message (GCancellab
 
 	g_return_if_fail (CAMEL_IS_OPERATION (cancellable));
 
+	va_start (ap, format);
+	message = g_strdup_vprintf (format, ap);
+	va_end (ap);
+
+	g_signal_emit (cancellable, signals[PUSH_MESSAGE], 0, message);
+
 	LOCK ();
 
 	operation = CAMEL_OPERATION (cancellable);
 
-	va_start (ap, format);
-
 	node = status_node_new ();
-	node->message = g_strdup_vprintf (format, ap);
+	node->message = message; /* takes ownership */
 	node->operation = g_object_ref (operation);
 
 	if (g_queue_is_empty (&operation->priv->status_stack)) {
@@ -288,8 +322,6 @@ camel_operation_push_message (GCancellab
 
 	g_queue_push_head (&operation->priv->status_stack, node);
 
-	va_end (ap);
-
 	UNLOCK ();
 }
 
@@ -317,6 +349,8 @@ camel_operation_pop_message (GCancellabl
 
 	g_return_if_fail (CAMEL_IS_OPERATION (cancellable));
 
+	g_signal_emit (cancellable, signals[POP_MESSAGE], 0);
+
 	LOCK ();
 
 	operation = CAMEL_OPERATION (cancellable);
@@ -376,6 +410,8 @@ camel_operation_progress (GCancellable *
 
 	g_return_if_fail (CAMEL_IS_OPERATION (cancellable));
 
+	g_signal_emit (cancellable, signals[PROGRESS], 0, percent);
+
 	LOCK ();
 
 	operation = CAMEL_OPERATION (cancellable);
diff -up evolution-data-server-3.12.11/camel/camel-store.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/camel-store.c
--- evolution-data-server-3.12.11/camel/camel-store.c.imapx-update-to-upstream	2014-11-03 13:58:08.000000000 +0100
+++ evolution-data-server-3.12.11/camel/camel-store.c	2016-08-15 13:52:41.940976331 +0200
@@ -64,6 +64,7 @@ struct _AsyncContext {
 	gchar *folder_name_2;
 	gboolean expunge;
 	guint32 flags;
+	GHashTable *save_setup;
 };
 
 struct _SignalClosure {
@@ -96,6 +97,11 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (
 static void
 async_context_free (AsyncContext *async_context)
 {
+	if (async_context->save_setup) {
+		g_hash_table_destroy (async_context->save_setup);
+		async_context->save_setup = NULL;
+	}
+
 	g_free (async_context->folder_name_1);
 	g_free (async_context->folder_name_2);
 
@@ -501,6 +507,15 @@ store_synchronize_sync (CamelStore *stor
 }
 
 static gboolean
+store_initial_setup_sync (CamelStore *store,
+			  GHashTable *out_save_setup,
+			  GCancellable *cancellable,
+			  GError **error)
+{
+	return TRUE;
+}
+
+static gboolean
 store_initable_init (GInitable *initable,
                      GCancellable *cancellable,
                      GError **error)
@@ -570,6 +585,7 @@ camel_store_class_init (CamelStoreClass
 	class->get_junk_folder_sync = store_get_junk_folder_sync;
 	class->get_trash_folder_sync = store_get_trash_folder_sync;
 	class->synchronize_sync = store_synchronize_sync;
+	class->initial_setup_sync = store_initial_setup_sync;
 
 	signals[FOLDER_CREATED] = g_signal_new (
 		"folder-created",
@@ -2897,3 +2913,171 @@ camel_store_synchronize_finish (CamelSto
 	return g_task_propagate_boolean (G_TASK (result), error);
 }
 
+/**
+ * camel_store_initial_setup_sync:
+ * @store: a #CamelStore
+ * @out_save_setup: (out) (transfer container) (element-type utf8 utf8): setup values to save
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Runs initial setup for the @store. It's meant to preset some
+ * values the first time the account connects to the server after
+ * it had been created. The function should return %TRUE even if
+ * it didn't populate anything. The default implementation does
+ * just that.
+ *
+ * The save_setup result, if not %NULL, should be freed using
+ * g_hash_table_destroy(). It's not an error to have it %NULL,
+ * it only means the @store doesn't have anything to save.
+ * Both the key and the value in the hash are newly allocated
+ * UTF-8 strings, owned by the hash table.
+ *
+ * The @store advertises support of this function by including
+ * CAMEL_STORE_SUPPORTS_INITIAL_SETUP in CamelStore::flags.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.12.11-25 (3.20)
+ **/
+gboolean
+camel_store_initial_setup_sync (CamelStore *store,
+				GHashTable **out_save_setup,
+				GCancellable *cancellable,
+				GError **error)
+{
+	GHashTable *save_setup;
+	CamelStoreClass *class;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
+	g_return_val_if_fail (out_save_setup != NULL, FALSE);
+
+	*out_save_setup = NULL;
+
+	class = CAMEL_STORE_GET_CLASS (store);
+	g_return_val_if_fail (class->initial_setup_sync != NULL, FALSE);
+
+	save_setup = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+	success = class->initial_setup_sync (store, save_setup, cancellable, error);
+
+	if (!success || !g_hash_table_size (save_setup)) {
+		g_hash_table_destroy (save_setup);
+		save_setup = NULL;
+	}
+
+	CAMEL_CHECK_GERROR (store, initial_setup_sync, success, error);
+
+	*out_save_setup = save_setup;
+
+	return success;
+}
+
+static void
+store_initial_setup_thread (GTask *task,
+			    gpointer source_object,
+			    gpointer task_data,
+			    GCancellable *cancellable)
+{
+	gboolean success;
+	AsyncContext *async_context;
+	GError *local_error = NULL;
+
+	async_context = (AsyncContext *) task_data;
+
+	success = camel_store_initial_setup_sync (
+		CAMEL_STORE (source_object),
+		&async_context->save_setup,
+		cancellable, &local_error);
+
+	if (local_error != NULL) {
+		g_task_return_error (task, local_error);
+	} else {
+		g_task_return_boolean (task, success);
+	}
+}
+
+/**
+ * camel_store_initial_setup:
+ * @store: a #CamelStore
+ * @io_priority: the I/O priority of the request
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Runs initial setup for the @store asynchronously.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call camel_store_initial_setup_finish() to get the result of the operation.
+ *
+ * The @store advertises support of this function by including
+ * CAMEL_STORE_SUPPORTS_INITIAL_SETUP in CamelStore::flags.
+ *
+ * Since: 3.12.11-25 (3.20)
+ **/
+void
+camel_store_initial_setup (CamelStore *store,
+			   gint io_priority,
+			   GCancellable *cancellable,
+			   GAsyncReadyCallback callback,
+			   gpointer user_data)
+{
+	GTask *task;
+	AsyncContext *async_context;
+
+	g_return_if_fail (CAMEL_IS_STORE (store));
+
+	async_context = g_slice_new0 (AsyncContext);
+
+	task = g_task_new (store, cancellable, callback, user_data);
+	g_task_set_source_tag (task, camel_store_initial_setup);
+	g_task_set_priority (task, io_priority);
+
+	g_task_set_task_data (
+		task, async_context,
+		(GDestroyNotify) async_context_free);
+
+	g_task_run_in_thread (task, store_initial_setup_thread);
+
+	g_object_unref (task);
+}
+
+/**
+ * camel_store_initial_setup_finish:
+ * @store: a #CamelStore
+ * @result: a #GAsyncResult
+ * @out_save_setup: (out) (transfer container) (element-type utf8 utf8): setup values to save
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with camel_store_initial_setup().
+ *
+ * The save_setup result, if not %NULL, should be freed using
+ * g_hash_table_destroy(). It's not an error to have it %NULL,
+ * it only means the @store doesn't have anything to save.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.12.11-25 (3.20)
+ **/
+gboolean
+camel_store_initial_setup_finish (CamelStore *store,
+				  GAsyncResult *result,
+				  GHashTable **out_save_setup,
+				  GError **error)
+{
+	AsyncContext *async_context;
+
+	g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
+	g_return_val_if_fail (out_save_setup != NULL, FALSE);
+	g_return_val_if_fail (g_task_is_valid (result, store), FALSE);
+
+	g_return_val_if_fail (
+		g_async_result_is_tagged (
+		result, camel_store_initial_setup), FALSE);
+
+	async_context = g_task_get_task_data (G_TASK (result));
+	*out_save_setup = async_context->save_setup;
+	async_context->save_setup = NULL;
+
+	return g_task_propagate_boolean (G_TASK (result), error);
+}
diff -up evolution-data-server-3.12.11/camel/camel-store.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/camel-store.h
--- evolution-data-server-3.12.11/camel/camel-store.h.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/camel-store.h	2016-08-15 13:52:41.940976331 +0200
@@ -63,6 +63,32 @@
 #define CAMEL_STORE_ERROR \
 	(camel_store_error_quark ())
 
+/**
+ * --@CAMEL_STORE_SETUP_ARCHIVE_FOLDER: Name of an Archive folder key--
+ * @CAMEL_STORE_SETUP_DRAFTS_FOLDER: Name of a Drafts folder key
+ * @CAMEL_STORE_SETUP_SENT_FOLDER: Name of a Sent folder key
+ * @CAMEL_STORE_SETUP_TEMPLATES_FOLDER: Name of a Templates folder key
+ *
+ * Key names to a hash table with values to preset for the account used
+ * as in the camel_store_initial_setup_sync() function.
+ *
+ * The key name consists of up to four parts: Source:Extension:Property[:Type]
+ * Source can be 'Collection', 'Account', 'Submission', 'Transport', 'Backend'.
+ * Extension is any extension name; it's up to the key creator to make sure
+ * the extension belongs to that particular Source.
+ * Property is a property name in the Extension.
+ * Type is an optional letter describing the type of the value; if not set, then
+ * string is used. Available values are: 'b' for boolean, 'i' for integer,
+ * 's' for string, 'f' for folder full path.
+ * All the part values are case sensitive.
+ *
+ * Since: 3.12.11-25 (3.20)
+ **/
+/* #define CAMEL_STORE_SETUP_ARCHIVE_FOLDER	"Account:Mail Account:archive-folder:f" */
+#define CAMEL_STORE_SETUP_DRAFTS_FOLDER		"Submission:Mail Composition:drafts-folder:f"
+#define CAMEL_STORE_SETUP_SENT_FOLDER		"Submission:Mail Submission:sent-folder:f"
+#define CAMEL_STORE_SETUP_TEMPLATES_FOLDER	"Submission:Mail Composition:templates-folder:f"
+
 G_BEGIN_DECLS
 
 /**
@@ -178,9 +204,13 @@ struct _CamelStoreClass {
 						 gboolean expunge,
 						 GCancellable *cancellable,
 						 GError **error);
+	gboolean	(*initial_setup_sync)	(CamelStore *store,
+						 GHashTable *save_setup,
+						 GCancellable *cancellable,
+						 GError **error);
 
 	/* Reserved slots for methods. */
-	gpointer reserved_for_methods[21];
+	gpointer reserved_for_methods[20];
 
 	/* Signals */
 	void		(*folder_created)	(CamelStore *store,
@@ -357,6 +387,20 @@ void		camel_store_synchronize		(CamelSto
 gboolean	camel_store_synchronize_finish	(CamelStore *store,
 						 GAsyncResult *result,
 						 GError **error);
+gboolean	camel_store_initial_setup_sync	(CamelStore *store,
+						 GHashTable **out_save_setup,
+						 GCancellable *cancellable,
+						 GError **error);
+void		camel_store_initial_setup	(CamelStore *store,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	camel_store_initial_setup_finish
+						(CamelStore *store,
+						 GAsyncResult *result,
+						 GHashTable **out_save_setup,
+						 GError **error);
 
 G_END_DECLS
 
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.c.imapx-update-to-upstream	2014-06-16 14:57:07.000000000 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.c	2016-08-15 13:52:41.940976331 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-command.c
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -36,21 +36,11 @@ struct _CamelIMAPXRealCommand {
 
 	volatile gint ref_count;
 
-	CamelIMAPXJob *job;
-
 	/* For building the part. */
 	GString *buffer;
 
-	/* Mailbox to select before running command. */
-	GWeakRef mailbox;
-
 	/* For network/parse errors. */
 	GError *error;
-
-	/* Used for running some commands synchronously. */
-	GCond done_sync_cond;
-	GMutex done_sync_mutex;
-	gboolean done_sync_flag;
 };
 
 /* Safe to cast to a GQueue. */
@@ -60,8 +50,7 @@ struct _CamelIMAPXCommandQueue {
 
 CamelIMAPXCommand *
 camel_imapx_command_new (CamelIMAPXServer *is,
-                         const gchar *name,
-                         CamelIMAPXMailbox *mailbox,
+                         guint32 job_kind,
                          const gchar *format,
                          ...)
 {
@@ -74,14 +63,13 @@ camel_imapx_command_new (CamelIMAPXServe
 	/* Initialize private bits. */
 	real_ic->ref_count = 1;
 	real_ic->buffer = g_string_sized_new (512);
-	g_weak_ref_init (&real_ic->mailbox, mailbox);
-	g_cond_init (&real_ic->done_sync_cond);
-	g_mutex_init (&real_ic->done_sync_mutex);
 
 	/* Initialize public bits. */
 	real_ic->public.is = is;
 	real_ic->public.tag = tag++;
-	real_ic->public.name = name;
+	real_ic->public.job_kind = job_kind;
+	real_ic->public.status = NULL;
+	real_ic->public.completed = FALSE;
 	g_queue_init (&real_ic->public.parts);
 
 	if (format != NULL && *format != '\0') {
@@ -141,22 +129,10 @@ camel_imapx_command_unref (CamelIMAPXCom
 
 		/* Free the private stuff. */
 
-		if (real_ic->job != NULL)
-			camel_imapx_job_unref (real_ic->job);
-
 		g_string_free (real_ic->buffer, TRUE);
 
-		g_weak_ref_clear (&real_ic->mailbox);
-
 		g_clear_error (&real_ic->error);
 
-		g_cond_clear (&real_ic->done_sync_cond);
-		g_mutex_clear (&real_ic->done_sync_mutex);
-
-		/* Do NOT try to free the GError.  If set it should have been
-		 * propagated to the CamelIMAPXJob, so it's either NULL or the
-		 * CamelIMAPXJob owns it now. */
-
 		/* Fill the memory with a bit pattern before releasing
 		 * it back to the slab allocator, so we can more easily
 		 * identify dangling CamelIMAPXCommand pointers. */
@@ -180,64 +156,6 @@ camel_imapx_command_check (CamelIMAPXCom
 	return (real_ic != NULL && real_ic->ref_count > 0);
 }
 
-gint
-camel_imapx_command_compare (CamelIMAPXCommand *ic1,
-                             CamelIMAPXCommand *ic2)
-{
-	g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic1), 0);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic2), 0);
-
-	if (ic1->pri == ic2->pri)
-		return 0;
-
-	return (ic1->pri < ic2->pri) ? -1 : 1;
-}
-
-CamelIMAPXJob *
-camel_imapx_command_get_job (CamelIMAPXCommand *ic)
-{
-	CamelIMAPXRealCommand *real_ic;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic), NULL);
-
-	real_ic = (CamelIMAPXRealCommand *) ic;
-
-	return real_ic->job;
-}
-
-void
-camel_imapx_command_set_job (CamelIMAPXCommand *ic,
-                             CamelIMAPXJob *job)
-{
-	CamelIMAPXRealCommand *real_ic;
-
-	g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic));
-
-	real_ic = (CamelIMAPXRealCommand *) ic;
-
-	if (job != NULL) {
-		g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
-		camel_imapx_job_ref (job);
-	}
-
-	if (real_ic->job != NULL)
-		camel_imapx_job_unref (real_ic->job);
-
-	real_ic->job = job;
-}
-
-CamelIMAPXMailbox *
-camel_imapx_command_ref_mailbox (CamelIMAPXCommand *ic)
-{
-	CamelIMAPXRealCommand *real_ic;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic), NULL);
-
-	real_ic = (CamelIMAPXRealCommand *) ic;
-
-	return g_weak_ref_get (&real_ic->mailbox);
-}
-
 void
 camel_imapx_command_add (CamelIMAPXCommand *ic,
                          const gchar *format,
@@ -280,7 +198,7 @@ camel_imapx_command_addv (CamelIMAPXComm
 
 	g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic));
 
-	c (ic->is->tagprefix, "adding command, format = '%s'\n", format);
+	c (camel_imapx_server_get_tagprefix (ic->is), "adding command, format = '%s'\n", format);
 
 	buffer = ((CamelIMAPXRealCommand *) ic)->buffer;
 
@@ -330,12 +248,12 @@ camel_imapx_command_addv (CamelIMAPXComm
 				break;
 			case 'D': /* datawrapper */
 				D = va_arg (ap, CamelDataWrapper *);
-				c (ic->is->tagprefix, "got data wrapper '%p'\n", D);
+				c (camel_imapx_server_get_tagprefix (ic->is), "got data wrapper '%p'\n", D);
 				camel_imapx_command_add_part (ic, CAMEL_IMAPX_COMMAND_DATAWRAPPER, D);
 				break;
 			case 'P': /* filename path */
 				P = va_arg (ap, gchar *);
-				c (ic->is->tagprefix, "got file path '%s'\n", P);
+				c (camel_imapx_server_get_tagprefix (ic->is), "got file path '%s'\n", P);
 				camel_imapx_command_add_part (ic, CAMEL_IMAPX_COMMAND_FILE, P);
 				break;
 			case 't': /* token */
@@ -344,7 +262,7 @@ camel_imapx_command_addv (CamelIMAPXComm
 				break;
 			case 's': /* simple string */
 				s = va_arg (ap, gchar *);
-				c (ic->is->tagprefix, "got string '%s'\n", g_str_has_prefix (format, "LOGIN") ? "***" : s);
+				c (camel_imapx_server_get_tagprefix (ic->is), "got string '%s'\n", g_str_has_prefix (format, "LOGIN") ? "***" : s);
 			output_string:
 				if (s && *s) {
 					guchar mask = imapx_is_mask (s);
@@ -400,19 +318,19 @@ camel_imapx_command_addv (CamelIMAPXComm
 			case 'u':
 				if (llong == 1) {
 					l = va_arg (ap, glong);
-					c (ic->is->tagprefix, "got glong '%d'\n", (gint) l);
+					c (camel_imapx_server_get_tagprefix (ic->is), "got glong '%d'\n", (gint) l);
 					memcpy (literal_format, start, p - start);
 					literal_format[p - start] = 0;
 					g_string_append_printf (buffer, literal_format, l);
 				} else if (llong == 2) {
 					guint64 i64 = va_arg (ap, guint64);
-					c (ic->is->tagprefix, "got guint64 '%d'\n", (gint) i64);
+					c (camel_imapx_server_get_tagprefix (ic->is), "got guint64 '%d'\n", (gint) i64);
 					memcpy (literal_format, start, p - start);
 					literal_format[p - start] = 0;
 					g_string_append_printf (buffer, literal_format, i64);
 				} else {
 					d = va_arg (ap, gint);
-					c (ic->is->tagprefix, "got gint '%d'\n", d);
+					c (camel_imapx_server_get_tagprefix (ic->is), "got gint '%d'\n", d);
 					memcpy (literal_format, start, p - start);
 					literal_format[p - start] = 0;
 					g_string_append_printf (buffer, literal_format, d);
@@ -426,7 +344,7 @@ camel_imapx_command_addv (CamelIMAPXComm
 		case '\\':	/* only for \\ really, we dont support \n\r etc at all */
 			c = *p;
 			if (c) {
-				g_assert (c == '\\');
+				g_warn_if_fail (c == '\\');
 				g_string_append_len (buffer, ps, p - ps);
 				p++;
 				ps = p;
@@ -472,6 +390,8 @@ camel_imapx_command_add_part (CamelIMAPX
 		/* we presume we'll need to get additional data only if we're not authenticated yet */
 		g_object_ref (ob);
 		mechanism = camel_sasl_get_mechanism (CAMEL_SASL (ob));
+		if (g_strcmp0 (mechanism, "Google") == 0)
+			mechanism = "XOAUTH2";
 		g_string_append (buffer, mechanism);
 		if (!camel_sasl_get_authenticated ((CamelSasl *) ob))
 			type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
@@ -502,7 +422,7 @@ camel_imapx_command_add_part (CamelIMAPX
 	if (type & CAMEL_IMAPX_COMMAND_LITERAL_PLUS) {
 		g_string_append_c (buffer, '{');
 		g_string_append_printf (buffer, "%u", ob_size);
-		if (CAMEL_IMAPX_HAVE_CAPABILITY (ic->is->cinfo, LITERALPLUS)) {
+		if (camel_imapx_server_have_capability (ic->is, IMAPX_CAPABILITY_LITERALPLUS)) {
 			g_string_append_c (buffer, '+');
 		} else {
 			type &= ~CAMEL_IMAPX_COMMAND_LITERAL_PLUS;
@@ -533,282 +453,12 @@ camel_imapx_command_close (CamelIMAPXCom
 	buffer = ((CamelIMAPXRealCommand *) ic)->buffer;
 
 	if (buffer->len > 5 && g_ascii_strncasecmp (buffer->str, "LOGIN", 5) == 0) {
-		c (ic->is->tagprefix, "completing command buffer is [%d] 'LOGIN...'\n", (gint) buffer->len);
+		c (camel_imapx_server_get_tagprefix (ic->is), "completing command buffer is [%d] 'LOGIN...'\n", (gint) buffer->len);
 	} else {
-		c (ic->is->tagprefix, "completing command buffer is [%d] '%.*s'\n", (gint) buffer->len, (gint) buffer->len, buffer->str);
+		c (camel_imapx_server_get_tagprefix (ic->is), "completing command buffer is [%d] '%.*s'\n", (gint) buffer->len, (gint) buffer->len, buffer->str);
 	}
 	if (buffer->len > 0)
 		camel_imapx_command_add_part (ic, CAMEL_IMAPX_COMMAND_SIMPLE, NULL);
 
 	g_string_set_size (buffer, 0);
 }
-
-void
-camel_imapx_command_wait (CamelIMAPXCommand *ic)
-{
-	CamelIMAPXRealCommand *real_ic;
-
-	g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic));
-
-	real_ic = (CamelIMAPXRealCommand *) ic;
-
-	g_mutex_lock (&real_ic->done_sync_mutex);
-	while (!real_ic->done_sync_flag)
-		g_cond_wait (
-			&real_ic->done_sync_cond,
-			&real_ic->done_sync_mutex);
-	g_mutex_unlock (&real_ic->done_sync_mutex);
-}
-
-void
-camel_imapx_command_done (CamelIMAPXCommand *ic)
-{
-	CamelIMAPXRealCommand *real_ic;
-
-	g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic));
-
-	real_ic = (CamelIMAPXRealCommand *) ic;
-
-	g_mutex_lock (&real_ic->done_sync_mutex);
-	real_ic->done_sync_flag = TRUE;
-	g_cond_broadcast (&real_ic->done_sync_cond);
-	g_mutex_unlock (&real_ic->done_sync_mutex);
-}
-
-/**
- * camel_imapx_command_failed:
- * @ic: a #CamelIMAPXCommand
- * @error: the error which caused the failure
- *
- * Copies @error to be returned in camel_imapx_command_set_error_if_failed().
- * Call this function if a networking or parsing error occurred to force all
- * active IMAP commands to abort processing.
- *
- * Since: 3.10
- **/
-void
-camel_imapx_command_failed (CamelIMAPXCommand *ic,
-                            const GError *error)
-{
-	CamelIMAPXRealCommand *real_ic;
-
-	g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic));
-	g_return_if_fail (error != NULL);
-
-	real_ic = (CamelIMAPXRealCommand *) ic;
-
-	/* Do not overwrite errors, the first passed in wins */
-	if (real_ic->error != NULL)
-		return;
-
-	real_ic->error = g_error_copy (error);
-}
-
-gboolean
-camel_imapx_command_set_error_if_failed (CamelIMAPXCommand *ic,
-                                         GError **error)
-{
-	CamelIMAPXRealCommand *real_ic;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic), FALSE);
-
-	real_ic = (CamelIMAPXRealCommand *) ic;
-
-	/* Check for a networking or parsing error. */
-	if (real_ic->error != NULL) {
-		g_propagate_error (error, real_ic->error);
-		real_ic->error = NULL;
-		return TRUE;
-	}
-
-	/* Check if the IMAP server rejected the command. */
-	if (ic->status != NULL && ic->status->result != IMAPX_OK) {
-
-		/* FIXME Map IMAP response codes to more
-		 *       meaningful GError domains/codes.
-		 *
-		 *       switch (ic->status->condition) {
-		 *               case IMAPX_AUTHENTICATIONFAILED:
-		 *                      g_set_error (...);
-		 *                      break;
-		 *               ...
-		 *       }
-		 */
-
-		if (ic->status->text != NULL)
-			g_set_error (
-				error, CAMEL_IMAPX_ERROR, 1,
-				"%s", ic->status->text);
-		else
-			g_set_error (
-				error, CAMEL_IMAPX_ERROR, 1,
-				"%s", _("Unknown error"));
-		return TRUE;
-	}
-
-	if (real_ic->job)
-		return camel_imapx_job_set_error_if_failed (real_ic->job, error);
-
-	return FALSE;
-}
-
-CamelIMAPXCommandQueue *
-camel_imapx_command_queue_new (void)
-{
-	/* An initialized GQueue is simply zero-filled,
-	 * so we can skip calling g_queue_init() here. */
-	return g_slice_new0 (CamelIMAPXCommandQueue);
-}
-
-void
-camel_imapx_command_queue_free (CamelIMAPXCommandQueue *queue)
-{
-	CamelIMAPXCommand *ic;
-
-	g_return_if_fail (queue != NULL);
-
-	while ((ic = g_queue_pop_head ((GQueue *) queue)) != NULL)
-		camel_imapx_command_unref (ic);
-
-	g_slice_free (CamelIMAPXCommandQueue, queue);
-}
-
-void
-camel_imapx_command_queue_transfer (CamelIMAPXCommandQueue *from,
-                                    CamelIMAPXCommandQueue *to)
-{
-	GList *link;
-
-	g_return_if_fail (from != NULL);
-	g_return_if_fail (to != NULL);
-
-	while ((link = g_queue_pop_head_link ((GQueue *) from)) != NULL)
-		g_queue_push_tail_link ((GQueue *) to, link);
-}
-
-void
-camel_imapx_command_queue_push_tail (CamelIMAPXCommandQueue *queue,
-                                     CamelIMAPXCommand *ic)
-{
-	g_return_if_fail (queue != NULL);
-	g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic));
-
-	camel_imapx_command_ref (ic);
-
-	g_queue_push_tail ((GQueue *) queue, ic);
-}
-
-void
-camel_imapx_command_queue_insert_sorted (CamelIMAPXCommandQueue *queue,
-                                         CamelIMAPXCommand *ic)
-{
-	g_return_if_fail (queue != NULL);
-	g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic));
-
-	camel_imapx_command_ref (ic);
-
-	g_queue_insert_sorted (
-		(GQueue *) queue, ic, (GCompareDataFunc)
-		camel_imapx_command_compare, NULL);
-}
-
-gboolean
-camel_imapx_command_queue_is_empty (CamelIMAPXCommandQueue *queue)
-{
-	g_return_val_if_fail (queue != NULL, TRUE);
-
-	return g_queue_is_empty ((GQueue *) queue);
-}
-
-guint
-camel_imapx_command_queue_get_length (CamelIMAPXCommandQueue *queue)
-{
-	g_return_val_if_fail (queue != NULL, 0);
-
-	return g_queue_get_length ((GQueue *) queue);
-}
-
-CamelIMAPXCommand *
-camel_imapx_command_queue_peek_head (CamelIMAPXCommandQueue *queue)
-{
-	g_return_val_if_fail (queue != NULL, NULL);
-
-	return g_queue_peek_head ((GQueue *) queue);
-}
-
-GList *
-camel_imapx_command_queue_peek_head_link (CamelIMAPXCommandQueue *queue)
-{
-	g_return_val_if_fail (queue != NULL, NULL);
-
-	return g_queue_peek_head_link ((GQueue *) queue);
-}
-
-gboolean
-camel_imapx_command_queue_remove (CamelIMAPXCommandQueue *queue,
-                                  CamelIMAPXCommand *ic)
-{
-	g_return_val_if_fail (queue != NULL, FALSE);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic), FALSE);
-
-	if (g_queue_remove ((GQueue *) queue, ic)) {
-		camel_imapx_command_unref (ic);
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-void
-camel_imapx_command_queue_delete_link (CamelIMAPXCommandQueue *queue,
-                                       GList *link)
-{
-	g_return_if_fail (queue != NULL);
-	g_return_if_fail (link != NULL);
-
-	/* Verify the link is actually in the queue. */
-	if (g_queue_link_index ((GQueue *) queue, link) == -1) {
-		g_warning ("%s: Link not found in queue", G_STRFUNC);
-		return;
-	}
-
-	camel_imapx_command_unref ((CamelIMAPXCommand *) link->data);
-	g_queue_delete_link ((GQueue *) queue, link);
-}
-
-/**
- * camel_imapx_command_queue_ref_by_tag:
- * @queue: a #CamelIMAPXCommandQueue
- * @tag: a #CamelIMAPXCommand tag
- *
- * Returns the #CamelIMAPXCommand in @queue with a matching @tag, or %NULL
- * if no match is found.
- *
- * The returned #CamelIMAPXCommand is referenced for thread-safety and should
- * be unreferenced with camel_imapx_command_unref() when finished with it.
- *
- * Since: 3.10
- **/
-CamelIMAPXCommand *
-camel_imapx_command_queue_ref_by_tag (CamelIMAPXCommandQueue *queue,
-                                      guint32 tag)
-{
-	CamelIMAPXCommand *match = NULL;
-	GList *head, *link;
-
-	g_return_val_if_fail (queue != NULL, NULL);
-
-	head = camel_imapx_command_queue_peek_head_link (queue);
-
-	for (link = head; link != NULL; link = g_list_next (link)) {
-		CamelIMAPXCommand *command = link->data;
-
-		if (command->tag == tag) {
-			match = camel_imapx_command_ref (command);
-			break;
-		}
-	}
-
-	return match;
-}
-
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.h.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.h	2016-08-15 13:52:41.943976330 +0200
@@ -1,24 +1,23 @@
 /*
  * camel-imapx-command.h
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
 #ifndef CAMEL_IMAPX_COMMAND_H
 #define CAMEL_IMAPX_COMMAND_H
 
-#include "camel-imapx-mailbox.h"
 #include "camel-imapx-utils.h"
 
 #define CAMEL_IS_IMAPX_COMMAND(command) \
@@ -27,7 +26,6 @@
 G_BEGIN_DECLS
 
 /* Avoid a circular reference. */
-struct _CamelIMAPXJob;
 struct _CamelIMAPXServer;
 
 typedef struct _CamelIMAPXCommand CamelIMAPXCommand;
@@ -66,39 +64,27 @@ struct _CamelIMAPXCommand {
 	struct _CamelIMAPXServer *is;
 	gint pri;
 
-	/* Command name/type (e.g. FETCH) */
-	const gchar *name;
+	guint32 job_kind; /* CamelIMAPXJobKind */
 
-	/* Status for command, indicates it is complete if != NULL. */
+	/* Status for command. */
 	struct _status_info *status;
 
 	guint32 tag;
+	gboolean completed;
 
 	GQueue parts;
 	GList *current_part;
-
-	/* Responsible for free'ing the command. */
-	CamelIMAPXCommandFunc complete;
 };
 
 CamelIMAPXCommand *
 		camel_imapx_command_new		(struct _CamelIMAPXServer *is,
-						 const gchar *name,
-						 CamelIMAPXMailbox *mailbox,
+						 guint32 job_kind,
 						 const gchar *format,
 						 ...);
 CamelIMAPXCommand *
 		camel_imapx_command_ref		(CamelIMAPXCommand *ic);
 void		camel_imapx_command_unref	(CamelIMAPXCommand *ic);
 gboolean	camel_imapx_command_check	(CamelIMAPXCommand *ic);
-gint		camel_imapx_command_compare	(CamelIMAPXCommand *ic1,
-						 CamelIMAPXCommand *ic2);
-struct _CamelIMAPXJob *
-		camel_imapx_command_get_job	(CamelIMAPXCommand *ic);
-void		camel_imapx_command_set_job	(CamelIMAPXCommand *ic,
-						 struct _CamelIMAPXJob *job);
-CamelIMAPXMailbox *
-		camel_imapx_command_ref_mailbox	(CamelIMAPXCommand *ic);
 void		camel_imapx_command_add		(CamelIMAPXCommand *ic,
 						 const gchar *format,
 						 ...);
@@ -109,51 +95,6 @@ void		camel_imapx_command_add_part	(Came
 						 CamelIMAPXCommandPartType type,
 						 gpointer data);
 void		camel_imapx_command_close	(CamelIMAPXCommand *ic);
-void		camel_imapx_command_wait	(CamelIMAPXCommand *ic);
-void		camel_imapx_command_done	(CamelIMAPXCommand *ic);
-void		camel_imapx_command_failed	(CamelIMAPXCommand *ic,
-						 const GError *error);
-gboolean	camel_imapx_command_set_error_if_failed
-						(CamelIMAPXCommand *ic,
-						 GError **error);
-
-/* These are simple GQueue wrappers for CamelIMAPXCommands.
- * They help make sure reference counting is done properly.
- * Add more wrappers as needed, don't circumvent them. */
-
-typedef struct _CamelIMAPXCommandQueue CamelIMAPXCommandQueue;
-
-CamelIMAPXCommandQueue *
-		camel_imapx_command_queue_new	(void);
-void		camel_imapx_command_queue_free	(CamelIMAPXCommandQueue *queue);
-void		camel_imapx_command_queue_transfer
-						(CamelIMAPXCommandQueue *from,
-						 CamelIMAPXCommandQueue *to);
-void		camel_imapx_command_queue_push_tail
-						(CamelIMAPXCommandQueue *queue,
-						 CamelIMAPXCommand *ic);
-void		camel_imapx_command_queue_insert_sorted
-						(CamelIMAPXCommandQueue *queue,
-						 CamelIMAPXCommand *ic);
-gboolean	camel_imapx_command_queue_is_empty
-						(CamelIMAPXCommandQueue *queue);
-guint		camel_imapx_command_queue_get_length
-						(CamelIMAPXCommandQueue *queue);
-CamelIMAPXCommand *
-		camel_imapx_command_queue_peek_head
-						(CamelIMAPXCommandQueue *queue);
-GList *		camel_imapx_command_queue_peek_head_link
-						(CamelIMAPXCommandQueue *queue);
-gboolean	camel_imapx_command_queue_remove
-						(CamelIMAPXCommandQueue *queue,
-						 CamelIMAPXCommand *ic);
-void		camel_imapx_command_queue_delete_link
-						(CamelIMAPXCommandQueue *queue,
-						 GList *link);
-CamelIMAPXCommand *
-		camel_imapx_command_queue_ref_by_tag
-						(CamelIMAPXCommandQueue *queue,
-						 guint32 tag);
 
 G_END_DECLS
 
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.c.imapx-update-to-upstream	2014-11-07 08:34:58.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.c	2016-08-15 13:52:41.945976330 +0200
@@ -18,7 +18,16 @@
  * Authors: Chenthill Palanisamy <pchenthill@novell.com>
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
 #include "camel-imapx-conn-manager.h"
+#include "camel-imapx-folder.h"
+#include "camel-imapx-job.h"
 #include "camel-imapx-settings.h"
 #include "camel-imapx-store.h"
 #include "camel-imapx-utils.h"
@@ -34,6 +43,9 @@
 #define CON_WRITE_UNLOCK(x) \
 	(g_rw_lock_writer_unlock (&(x)->priv->rw_lock))
 
+#define JOB_QUEUE_LOCK(x) g_rec_mutex_lock (&(x)->priv->job_queue_lock)
+#define JOB_QUEUE_UNLOCK(x) g_rec_mutex_unlock (&(x)->priv->job_queue_lock)
+
 #define CAMEL_IMAPX_CONN_MANAGER_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
 	((obj), CAMEL_TYPE_IMAPX_CONN_MANAGER, CamelIMAPXConnManagerPrivate))
@@ -41,23 +53,32 @@
 typedef struct _ConnectionInfo ConnectionInfo;
 
 struct _CamelIMAPXConnManagerPrivate {
-	/* XXX Might be easier for this to be a hash table,
-	 *     with CamelIMAPXServer pointers as the keys. */
-	GList *connections;
+	GList *connections; /* ConnectionInfo * */
 	GWeakRef store;
 	GRWLock rw_lock;
 	guint limit_max_connections;
 
 	GMutex pending_connections_lock;
 	GSList *pending_connections; /* GCancellable * */
+
+	gchar last_tagprefix;
+
+	GRecMutex job_queue_lock;
+	GSList *job_queue; /* CamelIMAPXJob * */
+
+	GMutex busy_connections_lock;
+	GCond busy_connections_cond;
+
+	GMutex busy_mailboxes_lock; /* used for both busy_mailboxes and idle_mailboxes */
+	GHashTable *busy_mailboxes; /* CamelIMAPXMailbox ~> gint */
+	GHashTable *idle_mailboxes; /* CamelIMAPXMailbox ~> gint */
 };
 
 struct _ConnectionInfo {
 	GMutex lock;
 	CamelIMAPXServer *is;
-	GHashTable *folder_names;
-	gchar *selected_folder;
-	GError *shutdown_error;
+	gboolean busy;
+	gulong refresh_mailbox_handler_id;
 	volatile gint ref_count;
 };
 
@@ -66,42 +87,101 @@ enum {
 	PROP_STORE
 };
 
+enum {
+	CONNECTION_CREATED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
 G_DEFINE_TYPE (
 	CamelIMAPXConnManager,
 	camel_imapx_conn_manager,
 	G_TYPE_OBJECT)
 
+static gboolean
+imapx_conn_manager_copy_message_sync (CamelIMAPXConnManager *conn_man,
+				      CamelIMAPXMailbox *mailbox,
+				      CamelIMAPXMailbox *destination,
+				      GPtrArray *uids,
+				      gboolean delete_originals,
+				      gboolean remove_deleted_flags,
+				      gboolean skip_sync_changes,
+				      GCancellable *cancellable,
+				      GError **error);
+
+typedef struct _MailboxRefreshData {
+	CamelIMAPXConnManager *conn_man;
+	CamelIMAPXMailbox *mailbox;
+} MailboxRefreshData;
+
 static void
-imapx_conn_shutdown (CamelIMAPXServer *is,
-		     const GError *error,
-		     CamelIMAPXConnManager *con_man);
+mailbox_refresh_data_free (MailboxRefreshData *data)
+{
+	if (data) {
+		g_clear_object (&data->conn_man);
+		g_clear_object (&data->mailbox);
+		g_free (data);
+	}
+}
+
+static gpointer
+imapx_conn_manager_idle_mailbox_refresh_thread (gpointer user_data)
+{
+	MailboxRefreshData *data = user_data;
+	GError *local_error = NULL;
+
+	g_return_val_if_fail (data != NULL, NULL);
+
+	/* passing NULL cancellable means to use only the job's abort cancellable */
+	if (!camel_imapx_conn_manager_refresh_info_sync (data->conn_man, data->mailbox, NULL, &local_error)) {
+		c ('*', "%s: Failed to refresh mailbox '%s': %s\n", G_STRFUNC,
+			camel_imapx_mailbox_get_name (data->mailbox),
+			local_error ? local_error->message : "Unknown error");
+	}
+
+	mailbox_refresh_data_free (data);
+	g_clear_error (&local_error);
+
+	return NULL;
+}
 
 static void
-imapx_conn_update_select (CamelIMAPXServer *is,
-                          CamelIMAPXMailbox *mailbox,
-                          CamelIMAPXConnManager *con_man);
-static void
-imapx_conn_mailbox_closed (CamelIMAPXServer *is,
-			   CamelIMAPXMailbox *mailbox,
-			   CamelIMAPXConnManager *con_man);
+imapx_conn_manager_refresh_mailbox_cb (CamelIMAPXServer *is,
+				       CamelIMAPXMailbox *mailbox,
+				       CamelIMAPXConnManager *conn_man)
+{
+	MailboxRefreshData *data;
+	GThread *thread;
+	GError *local_error = NULL;
+
+	g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is));
+	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
+
+	data = g_new0 (MailboxRefreshData, 1);
+	data->conn_man = g_object_ref (conn_man);
+	data->mailbox = g_object_ref (mailbox);
+
+	thread = g_thread_try_new (NULL, imapx_conn_manager_idle_mailbox_refresh_thread, data, &local_error);
+	if (!thread) {
+		g_warning ("%s: Failed to create IDLE mailbox refresh thread: %s", G_STRFUNC, local_error ? local_error->message : "Unknown error");
+		mailbox_refresh_data_free (data);
+	} else {
+		g_thread_unref (thread);
+	}
+
+	g_clear_error (&local_error);
+}
 
 static ConnectionInfo *
 connection_info_new (CamelIMAPXServer *is)
 {
 	ConnectionInfo *cinfo;
-	GHashTable *folder_names;
-
-	folder_names = g_hash_table_new_full (
-		(GHashFunc) g_str_hash,
-		(GEqualFunc) g_str_equal,
-		(GDestroyNotify) g_free,
-		(GDestroyNotify) NULL);
 
 	cinfo = g_slice_new0 (ConnectionInfo);
 	g_mutex_init (&cinfo->lock);
 	cinfo->is = g_object_ref (is);
-	cinfo->folder_names = folder_names;
-	cinfo->shutdown_error = NULL;
 	cinfo->ref_count = 1;
 
 	return cinfo;
@@ -125,247 +205,348 @@ connection_info_unref (ConnectionInfo *c
 	g_return_if_fail (cinfo->ref_count > 0);
 
 	if (g_atomic_int_dec_and_test (&cinfo->ref_count)) {
-		camel_imapx_server_shutdown (cinfo->is, cinfo->shutdown_error);
-		g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, imapx_conn_shutdown, NULL);
-		g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, imapx_conn_update_select, NULL);
-		g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, imapx_conn_mailbox_closed, NULL);
+		if (cinfo->refresh_mailbox_handler_id)
+			g_signal_handler_disconnect (cinfo->is, cinfo->refresh_mailbox_handler_id);
 
 		g_mutex_clear (&cinfo->lock);
 		g_object_unref (cinfo->is);
-		g_hash_table_destroy (cinfo->folder_names);
-		g_free (cinfo->selected_folder);
-		g_clear_error (&cinfo->shutdown_error);
 
 		g_slice_free (ConnectionInfo, cinfo);
 	}
 }
 
-static void
-connection_info_cancel_and_unref (ConnectionInfo *cinfo)
-{
-	g_return_if_fail (cinfo != NULL);
-	g_return_if_fail (cinfo->ref_count > 0);
-
-	g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, imapx_conn_shutdown, NULL);
-	g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, imapx_conn_update_select, NULL);
-	g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, imapx_conn_mailbox_closed, NULL);
-	camel_imapx_server_shutdown (cinfo->is, cinfo->shutdown_error);
-	connection_info_unref (cinfo);
-}
-
 static gboolean
-connection_info_is_available (ConnectionInfo *cinfo)
+connection_info_try_reserve (ConnectionInfo *cinfo)
 {
-	gboolean available;
+	gboolean reserved = FALSE;
 
 	g_return_val_if_fail (cinfo != NULL, FALSE);
 
 	g_mutex_lock (&cinfo->lock);
 
-	/* Available means it's not tracking any folder names or no jobs are running. */
-	available = (g_hash_table_size (cinfo->folder_names) == 0) ||
-		    camel_imapx_server_get_command_count (cinfo->is) == 0;
+	if (!cinfo->busy) {
+		cinfo->busy = TRUE;
+		reserved = TRUE;
+	}
 
 	g_mutex_unlock (&cinfo->lock);
 
-	return available;
+	return reserved;
 }
 
 static gboolean
-connection_info_has_folder_name (ConnectionInfo *cinfo,
-                                 const gchar *folder_name)
+connection_info_get_busy (ConnectionInfo *cinfo)
 {
-	gpointer value;
+	gboolean busy;
 
 	g_return_val_if_fail (cinfo != NULL, FALSE);
 
-	if (folder_name == NULL)
-		return FALSE;
-
 	g_mutex_lock (&cinfo->lock);
 
-	value = g_hash_table_lookup (cinfo->folder_names, folder_name);
+	busy = cinfo->busy;
 
 	g_mutex_unlock (&cinfo->lock);
 
-	return (value != NULL);
+	return busy;
 }
 
 static void
-connection_info_insert_folder_name (ConnectionInfo *cinfo,
-                                    const gchar *folder_name)
+connection_info_set_busy (ConnectionInfo *cinfo,
+			  gboolean busy)
 {
 	g_return_if_fail (cinfo != NULL);
-	g_return_if_fail (folder_name != NULL);
 
 	g_mutex_lock (&cinfo->lock);
 
-	g_hash_table_insert (
-		cinfo->folder_names,
-		g_strdup (folder_name),
-		GINT_TO_POINTER (1));
+	cinfo->busy = busy;
 
 	g_mutex_unlock (&cinfo->lock);
 }
 
 static void
-connection_info_remove_folder_name (ConnectionInfo *cinfo,
-                                    const gchar *folder_name)
+imapx_conn_manager_signal_busy_connections (CamelIMAPXConnManager *conn_man)
 {
-	g_return_if_fail (cinfo != NULL);
-	g_return_if_fail (folder_name != NULL);
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
 
-	g_mutex_lock (&cinfo->lock);
+	g_mutex_lock (&conn_man->priv->busy_connections_lock);
+	g_cond_broadcast (&conn_man->priv->busy_connections_cond);
+	g_mutex_unlock (&conn_man->priv->busy_connections_lock);
+}
+
+static void
+imapx_conn_manager_unmark_busy (CamelIMAPXConnManager *conn_man,
+				ConnectionInfo *cinfo)
+{
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
+	g_return_if_fail (cinfo != NULL);
+	g_return_if_fail (connection_info_get_busy (cinfo));
 
-	g_hash_table_remove (cinfo->folder_names, folder_name);
+	connection_info_set_busy (cinfo, FALSE);
 
-	g_mutex_unlock (&cinfo->lock);
+	imapx_conn_manager_signal_busy_connections (conn_man);
 }
 
-static gchar *
-connection_info_dup_selected_folder (ConnectionInfo *cinfo)
+static gboolean
+imapx_conn_manager_remove_info (CamelIMAPXConnManager *conn_man,
+                                ConnectionInfo *cinfo)
 {
-	gchar *selected_folder;
+	GList *list, *link;
+	gboolean removed = FALSE;
 
-	g_return_val_if_fail (cinfo != NULL, NULL);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+	g_return_val_if_fail (cinfo != NULL, FALSE);
 
-	g_mutex_lock (&cinfo->lock);
+	CON_WRITE_LOCK (conn_man);
 
-	selected_folder = g_strdup (cinfo->selected_folder);
+	list = conn_man->priv->connections;
+	link = g_list_find (list, cinfo);
 
-	g_mutex_unlock (&cinfo->lock);
+	if (link != NULL) {
+		list = g_list_delete_link (list, link);
+		connection_info_unref (cinfo);
+		removed = TRUE;
+	}
+
+	conn_man->priv->connections = list;
+
+	CON_WRITE_UNLOCK (conn_man);
+
+	if (removed)
+		imapx_conn_manager_signal_busy_connections (conn_man);
 
-	return selected_folder;
+	return removed;
 }
 
 static void
-connection_info_set_selected_folder (ConnectionInfo *cinfo,
-                                     const gchar *selected_folder)
+imapx_conn_manager_cancel_pending_connections (CamelIMAPXConnManager *conn_man)
 {
-	g_return_if_fail (cinfo != NULL);
+	GSList *link;
 
-	g_mutex_lock (&cinfo->lock);
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
 
-	g_free (cinfo->selected_folder);
-	cinfo->selected_folder = g_strdup (selected_folder);
+	g_mutex_lock (&conn_man->priv->pending_connections_lock);
+	for (link = conn_man->priv->pending_connections; link; link = g_slist_next (link)) {
+		GCancellable *cancellable = link->data;
 
-	g_mutex_unlock (&cinfo->lock);
+		if (cancellable)
+			g_cancellable_cancel (cancellable);
+	}
+	g_mutex_unlock (&conn_man->priv->pending_connections_lock);
 }
 
 static void
-connection_info_set_shutdown_error (ConnectionInfo *cinfo,
-                                    const GError *shutdown_error)
+imapx_conn_manager_abort_jobs (CamelIMAPXConnManager *conn_man)
 {
-	g_return_if_fail (cinfo != NULL);
+	GSList *link;
 
-	g_mutex_lock (&cinfo->lock);
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
 
-	if (cinfo->shutdown_error != shutdown_error) {
-		g_clear_error (&cinfo->shutdown_error);
-		if (shutdown_error)
-			cinfo->shutdown_error = g_error_copy (shutdown_error);
+	JOB_QUEUE_LOCK (conn_man);
+
+	for (link = conn_man->priv->job_queue; link; link = g_slist_next (link)) {
+		CamelIMAPXJob *job = link->data;
+
+		if (job)
+			camel_imapx_job_abort (job);
 	}
 
-	g_mutex_unlock (&cinfo->lock);
+	JOB_QUEUE_UNLOCK (conn_man);
 }
 
-static GList *
-imapx_conn_manager_list_info (CamelIMAPXConnManager *con_man)
+static CamelFolder *
+imapx_conn_manager_ref_folder_sync (CamelIMAPXConnManager *conn_man,
+				    CamelIMAPXMailbox *mailbox,
+				    GCancellable *cancellable,
+				    GError **error)
 {
-	GList *list;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL);
+	CamelIMAPXStore *store;
+	CamelFolder *folder;
+	gchar *folder_path;
 
-	CON_READ_LOCK (con_man);
+	store = camel_imapx_conn_manager_ref_store (conn_man);
+	folder_path = camel_imapx_mailbox_dup_folder_path (mailbox);
 
-	list = g_list_copy (con_man->priv->connections);
-	g_list_foreach (list, (GFunc) connection_info_ref, NULL);
+	folder = camel_store_get_folder_sync (CAMEL_STORE (store), folder_path, 0, cancellable, NULL);
+	if (folder)
+		camel_imapx_folder_set_mailbox (CAMEL_IMAPX_FOLDER (folder), mailbox);
 
-	CON_READ_UNLOCK (con_man);
+	g_free (folder_path);
+	g_clear_object (&store);
 
-	return list;
+	return folder;
 }
 
-static ConnectionInfo *
-imapx_conn_manager_lookup_info (CamelIMAPXConnManager *con_man,
-                                CamelIMAPXServer *is)
+static void
+imapx_conn_manager_inc_mailbox_hash (CamelIMAPXConnManager *conn_man,
+				     CamelIMAPXMailbox *mailbox,
+				     GHashTable *mailboxes_hash)
 {
-	ConnectionInfo *cinfo = NULL;
-	GList *list, *link;
+	gint count;
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
+	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
+	g_return_if_fail (mailboxes_hash != NULL);
 
-	CON_READ_LOCK (con_man);
+	g_mutex_lock (&conn_man->priv->busy_mailboxes_lock);
 
-	list = con_man->priv->connections;
+	count = GPOINTER_TO_INT (g_hash_table_lookup (mailboxes_hash, mailbox));
+	count++;
 
-	for (link = list; link != NULL; link = g_list_next (link)) {
-		ConnectionInfo *candidate = link->data;
+	g_hash_table_insert (mailboxes_hash, g_object_ref (mailbox), GINT_TO_POINTER (count));
 
-		if (candidate->is == is) {
-			cinfo = connection_info_ref (candidate);
-			break;
-		}
+	g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock);
+}
+
+static void
+imapx_conn_manager_dec_mailbox_hash (CamelIMAPXConnManager *conn_man,
+				     CamelIMAPXMailbox *mailbox,
+				     GHashTable *mailboxes_hash)
+{
+	gint count;
+
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
+	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
+	g_return_if_fail (mailboxes_hash != NULL);
+
+	g_mutex_lock (&conn_man->priv->busy_mailboxes_lock);
+
+	count = GPOINTER_TO_INT (g_hash_table_lookup (mailboxes_hash, mailbox));
+	if (!count) {
+		g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock);
+		return;
 	}
 
-	CON_READ_UNLOCK (con_man);
+	count--;
 
-	return cinfo;
+	if (count)
+		g_hash_table_insert (mailboxes_hash, g_object_ref (mailbox), GINT_TO_POINTER (count));
+	else
+		g_hash_table_remove (mailboxes_hash, mailbox);
+
+	g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock);
 }
 
 static gboolean
-imapx_conn_manager_remove_info (CamelIMAPXConnManager *con_man,
-                                ConnectionInfo *cinfo)
+imapx_conn_manager_is_mailbox_hash (CamelIMAPXConnManager *conn_man,
+				    CamelIMAPXMailbox *mailbox,
+				    GHashTable *mailboxes_hash)
 {
-	GList *list, *link;
-	gboolean removed = FALSE;
+	gint count;
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), FALSE);
-	g_return_val_if_fail (cinfo != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+	g_return_val_if_fail (mailboxes_hash != NULL, FALSE);
 
-	CON_WRITE_LOCK (con_man);
+	g_mutex_lock (&conn_man->priv->busy_mailboxes_lock);
 
-	list = con_man->priv->connections;
-	link = g_list_find (list, cinfo);
+	count = GPOINTER_TO_INT (g_hash_table_lookup (mailboxes_hash, mailbox));
 
-	if (link != NULL) {
-		list = g_list_delete_link (list, link);
-		connection_info_unref (cinfo);
-		removed = TRUE;
-	}
+	g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock);
+
+	return count > 0;
+}
 
-	con_man->priv->connections = list;
+static void
+imapx_conn_manager_clear_mailboxes_hashes (CamelIMAPXConnManager *conn_man)
+{
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
 
-	CON_WRITE_UNLOCK (con_man);
+	g_mutex_lock (&conn_man->priv->busy_mailboxes_lock);
 
-	return removed;
+	g_hash_table_remove_all (conn_man->priv->busy_mailboxes);
+	g_hash_table_remove_all (conn_man->priv->idle_mailboxes);
+
+	g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock);
 }
 
 static void
-imax_conn_manager_cancel_pending_connections (CamelIMAPXConnManager *con_man)
+imapx_conn_manager_inc_mailbox_busy (CamelIMAPXConnManager *conn_man,
+				     CamelIMAPXMailbox *mailbox)
 {
-	GSList *link;
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
+	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
 
-	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man));
+	imapx_conn_manager_inc_mailbox_hash (conn_man, mailbox, conn_man->priv->busy_mailboxes);
+}
 
-	g_mutex_lock (&con_man->priv->pending_connections_lock);
-	for (link = con_man->priv->pending_connections; link; link = g_slist_next (link)) {
-		GCancellable *cancellable = link->data;
+static void
+imapx_conn_manager_dec_mailbox_busy (CamelIMAPXConnManager *conn_man,
+				     CamelIMAPXMailbox *mailbox)
+{
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
+	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
 
-		if (cancellable)
-			g_cancellable_cancel (cancellable);
-	}
-	g_mutex_unlock (&con_man->priv->pending_connections_lock);
+	imapx_conn_manager_dec_mailbox_hash (conn_man, mailbox, conn_man->priv->busy_mailboxes);
+}
+
+static gboolean
+imapx_conn_manager_is_mailbox_busy (CamelIMAPXConnManager *conn_man,
+				    CamelIMAPXMailbox *mailbox)
+{
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+
+	return imapx_conn_manager_is_mailbox_hash (conn_man, mailbox, conn_man->priv->busy_mailboxes);
+}
+
+static void
+imapx_conn_manager_inc_mailbox_idle (CamelIMAPXConnManager *conn_man,
+				     CamelIMAPXMailbox *mailbox)
+{
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
+	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
+
+	imapx_conn_manager_inc_mailbox_hash (conn_man, mailbox, conn_man->priv->idle_mailboxes);
+}
+
+static void
+imapx_conn_manager_dec_mailbox_idle (CamelIMAPXConnManager *conn_man,
+				     CamelIMAPXMailbox *mailbox)
+{
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
+	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
+
+	imapx_conn_manager_dec_mailbox_hash (conn_man, mailbox, conn_man->priv->idle_mailboxes);
+}
+
+static gboolean
+imapx_conn_manager_is_mailbox_idle (CamelIMAPXConnManager *conn_man,
+				    CamelIMAPXMailbox *mailbox)
+{
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+
+	return imapx_conn_manager_is_mailbox_hash (conn_man, mailbox, conn_man->priv->idle_mailboxes);
+}
+
+static gboolean
+imapx_conn_manager_has_inbox_idle (CamelIMAPXConnManager *conn_man)
+{
+	CamelIMAPXStore *imapx_store;
+	CamelIMAPXMailbox *inbox_mailbox;
+	gboolean is_idle;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+
+	imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
+	inbox_mailbox = imapx_store ? camel_imapx_store_ref_mailbox (imapx_store, "INBOX") : NULL;
+
+	g_clear_object (&imapx_store);
+
+	is_idle = inbox_mailbox && imapx_conn_manager_is_mailbox_idle (conn_man, inbox_mailbox);
+
+	g_clear_object (&inbox_mailbox);
+
+	return is_idle;
 }
 
 static void
-imapx_conn_manager_set_store (CamelIMAPXConnManager *con_man,
+imapx_conn_manager_set_store (CamelIMAPXConnManager *conn_man,
                               CamelStore *store)
 {
 	g_return_if_fail (CAMEL_IS_STORE (store));
 
-	g_weak_ref_set (&con_man->priv->store, store);
+	g_weak_ref_set (&conn_man->priv->store, store);
 }
 
 static void
@@ -406,18 +587,24 @@ imapx_conn_manager_get_property (GObject
 static void
 imapx_conn_manager_dispose (GObject *object)
 {
-	CamelIMAPXConnManagerPrivate *priv;
+	CamelIMAPXConnManager *conn_man;
 
-	priv = CAMEL_IMAPX_CONN_MANAGER_GET_PRIVATE (object);
+	conn_man = CAMEL_IMAPX_CONN_MANAGER (object);
+
+	imapx_conn_manager_cancel_pending_connections (conn_man);
+	imapx_conn_manager_abort_jobs (conn_man);
 
 	g_list_free_full (
-		priv->connections,
+		conn_man->priv->connections,
 		(GDestroyNotify) connection_info_unref);
-	priv->connections = NULL;
+	conn_man->priv->connections = NULL;
 
-	imax_conn_manager_cancel_pending_connections (CAMEL_IMAPX_CONN_MANAGER (object));
+	g_weak_ref_set (&conn_man->priv->store, NULL);
 
-	g_weak_ref_set (&priv->store, NULL);
+	g_mutex_lock (&conn_man->priv->busy_mailboxes_lock);
+	g_hash_table_remove_all (conn_man->priv->busy_mailboxes);
+	g_hash_table_remove_all (conn_man->priv->idle_mailboxes);
+	g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock);
 
 	/* Chain up to parent's dispose() method. */
 	G_OBJECT_CLASS (camel_imapx_conn_manager_parent_class)->dispose (object);
@@ -431,10 +618,17 @@ imapx_conn_manager_finalize (GObject *ob
 	priv = CAMEL_IMAPX_CONN_MANAGER_GET_PRIVATE (object);
 
 	g_warn_if_fail (priv->pending_connections == NULL);
+	g_warn_if_fail (priv->job_queue == NULL);
 
 	g_rw_lock_clear (&priv->rw_lock);
+	g_rec_mutex_clear (&priv->job_queue_lock);
 	g_mutex_clear (&priv->pending_connections_lock);
+	g_mutex_clear (&priv->busy_connections_lock);
+	g_cond_clear (&priv->busy_connections_cond);
 	g_weak_ref_clear (&priv->store);
+	g_mutex_clear (&priv->busy_mailboxes_lock);
+	g_hash_table_destroy (priv->busy_mailboxes);
+	g_hash_table_destroy (priv->idle_mailboxes);
 
 	/* Chain up to parent's finalize() method. */
 	G_OBJECT_CLASS (camel_imapx_conn_manager_parent_class)->finalize (object);
@@ -459,546 +653,2147 @@ camel_imapx_conn_manager_class_init (Cam
 		g_param_spec_object (
 			"store",
 			"Store",
-			"The CamelStore to which we belong",
-			CAMEL_TYPE_STORE,
+			"The CamelIMAPXStore to which we belong",
+			CAMEL_TYPE_IMAPX_STORE,
 			G_PARAM_READWRITE |
 			G_PARAM_CONSTRUCT_ONLY |
 			G_PARAM_STATIC_STRINGS));
-}
-
-static void
-camel_imapx_conn_manager_init (CamelIMAPXConnManager *con_man)
-{
-	con_man->priv = CAMEL_IMAPX_CONN_MANAGER_GET_PRIVATE (con_man);
 
-	g_rw_lock_init (&con_man->priv->rw_lock);
-	g_mutex_init (&con_man->priv->pending_connections_lock);
-	g_weak_ref_init (&con_man->priv->store, NULL);
+	signals[CONNECTION_CREATED] = g_signal_new (
+		"connection-created",
+		G_OBJECT_CLASS_TYPE (class),
+		G_SIGNAL_RUN_FIRST,
+		G_STRUCT_OFFSET (CamelIMAPXConnManagerClass, connection_created),
+		NULL, NULL, NULL,
+		G_TYPE_NONE, 1,
+		CAMEL_TYPE_IMAPX_SERVER);
 }
 
 static void
-imapx_conn_shutdown (CamelIMAPXServer *is,
-		     const GError *error,
-                     CamelIMAPXConnManager *con_man)
+camel_imapx_conn_manager_init (CamelIMAPXConnManager *conn_man)
 {
-	ConnectionInfo *cinfo;
-
-	/* Returns a new ConnectionInfo reference. */
-	cinfo = imapx_conn_manager_lookup_info (con_man, is);
+	conn_man->priv = CAMEL_IMAPX_CONN_MANAGER_GET_PRIVATE (conn_man);
 
-	if (cinfo != NULL) {
-		imapx_conn_manager_remove_info (con_man, cinfo);
-		connection_info_unref (cinfo);
-	}
-
-	/* If one connection ends with this error, then it means all
-	   other opened connections also may end with the same error,
-	   thus better to kill them all from the list of connections.
-	*/
-	if (g_error_matches (error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		camel_imapx_conn_manager_close_connections (con_man, error);
-	}
+	g_rw_lock_init (&conn_man->priv->rw_lock);
+	g_rec_mutex_init (&conn_man->priv->job_queue_lock);
+	g_mutex_init (&conn_man->priv->pending_connections_lock);
+	g_mutex_init (&conn_man->priv->busy_connections_lock);
+	g_cond_init (&conn_man->priv->busy_connections_cond);
+	g_weak_ref_init (&conn_man->priv->store, NULL);
+	g_mutex_init (&conn_man->priv->busy_mailboxes_lock);
+
+	conn_man->priv->last_tagprefix = 'A' - 1;
+	conn_man->priv->busy_mailboxes = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
+	conn_man->priv->idle_mailboxes = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
 }
 
-static void
-imapx_conn_update_select (CamelIMAPXServer *is,
-                          CamelIMAPXMailbox *mailbox,
-                          CamelIMAPXConnManager *con_man)
+static gchar
+imapx_conn_manager_get_next_free_tagprefix_unlocked (CamelIMAPXConnManager *conn_man)
 {
-	ConnectionInfo *cinfo;
-	gchar *old_selected_folder, *selected_folder = NULL;
+	gchar adept;
+	gint ii;
+	GList *iter;
 
-	/* Returns a new ConnectionInfo reference. */
-	cinfo = imapx_conn_manager_lookup_info (con_man, is);
+	adept = conn_man->priv->last_tagprefix + 1;
 
-	if (cinfo == NULL)
-		return;
+	/* the 'Z' is dedicated to auth types query */
+	if (adept >= 'Z')
+		adept = 'A';
+	else if (adept < 'A')
+		adept = 'A';
+
+	for (ii = 0; ii < 26; ii++) {
+		for (iter = conn_man->priv->connections; iter; iter = g_list_next (iter)) {
+			ConnectionInfo *cinfo = iter->data;
 
-	old_selected_folder = connection_info_dup_selected_folder (cinfo);
+			if (!cinfo || !cinfo->is)
+				continue;
 
-	if (old_selected_folder != NULL) {
-		if (!camel_imapx_server_folder_name_in_jobs (is, old_selected_folder)) {
-			connection_info_remove_folder_name (cinfo, old_selected_folder);
-			c (is->tagprefix, "Removed folder %s from connection folder list - select changed \n", old_selected_folder);
+			if (camel_imapx_server_get_tagprefix (cinfo->is) == adept)
+				break;
 		}
 
-		g_free (old_selected_folder);
+		/* Read all current active connections and none has the same tag prefix */
+		if (!iter)
+			break;
+
+		adept++;
+		if (adept >= 'Z')
+			adept = 'A';
 	}
 
-	if (mailbox)
-		selected_folder = camel_imapx_mailbox_dup_folder_path (mailbox);
-	connection_info_set_selected_folder (cinfo, selected_folder);
-	g_free (selected_folder);
+	g_return_val_if_fail (adept >= 'A' && adept < 'Z', 'Z');
 
-	connection_info_unref (cinfo);
-}
+	conn_man->priv->last_tagprefix = adept;
 
-static void
-imapx_conn_mailbox_closed (CamelIMAPXServer *is,
-			   CamelIMAPXMailbox *mailbox,
-			   CamelIMAPXConnManager *con_man)
-{
-	imapx_conn_update_select (is, NULL, con_man);
+	return adept;
 }
 
-/* This should find a connection if the slots are full, returns NULL if there are slots available for a new connection for a folder */
-static CamelIMAPXServer *
-imapx_find_connection_unlocked (CamelIMAPXConnManager *con_man,
-                                const gchar *folder_name,
-				gboolean for_expensive_job)
+static ConnectionInfo *
+imapx_create_new_connection_unlocked (CamelIMAPXConnManager *conn_man,
+                                      CamelIMAPXMailbox *mailbox,
+                                      GCancellable *cancellable,
+                                      GError **error)
 {
-	CamelStore *store;
-	CamelSettings *settings;
 	CamelIMAPXServer *is = NULL;
+	CamelIMAPXStore *imapx_store;
 	ConnectionInfo *cinfo = NULL;
-	GList *list, *link;
-	guint concurrent_connections, opened_connections, expensive_connections = 0;
-	guint min_jobs = G_MAXUINT;
+	gboolean success;
 
 	/* Caller must be holding CON_WRITE_LOCK. */
 
-	store = camel_imapx_conn_manager_ref_store (con_man);
-	g_return_val_if_fail (store != NULL, NULL);
+	/* Check if we got cancelled while we were waiting. */
+	if (g_cancellable_set_error_if_cancelled (cancellable, error))
+		return NULL;
 
-	settings = camel_service_ref_settings (CAMEL_SERVICE (store));
+	imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
+	g_return_val_if_fail (imapx_store != NULL, NULL);
 
-	concurrent_connections =
-		camel_imapx_settings_get_concurrent_connections (
-		CAMEL_IMAPX_SETTINGS (settings));
-
-	if (con_man->priv->limit_max_connections > 0 &&
-	    con_man->priv->limit_max_connections < concurrent_connections)
-		concurrent_connections = con_man->priv->limit_max_connections;
+	is = camel_imapx_server_new (imapx_store);
+	camel_imapx_server_set_tagprefix (is, imapx_conn_manager_get_next_free_tagprefix_unlocked (conn_man));
 
-	g_object_unref (settings);
+	g_signal_emit (conn_man, signals[CONNECTION_CREATED], 0, is);
 
-	/* XXX Have a dedicated connection for INBOX ? */
+	/* XXX As part of the connect operation the CamelIMAPXServer will
+	 *     have to call camel_session_authenticate_sync(), but it has
+	 *     no way to pass itself through in that call so the service
+	 *     knows which CamelIMAPXServer is trying to authenticate.
+	 *
+	 *     IMAPX is the only provider that does multiple connections
+	 *     like this, so I didn't want to pollute the CamelSession and
+	 *     CamelService authentication APIs with an extra argument.
+	 *     Instead we do this little hack so the service knows which
+	 *     CamelIMAPXServer to act on in its authenticate_sync() method.
+	 *
+	 *     Because we're holding the CAMEL_SERVICE_REC_CONNECT_LOCK
+	 *     we should not have multiple IMAPX connections trying to
+	 *     authenticate at once, so this should be thread-safe.
+	 */
+	camel_imapx_store_set_connecting_server (imapx_store, is, conn_man->priv->connections != NULL);
+	success = camel_imapx_server_connect_sync (is, cancellable, error);
+	camel_imapx_store_set_connecting_server (imapx_store, NULL, FALSE);
 
-	opened_connections = g_list_length (con_man->priv->connections);
-	list = con_man->priv->connections;
+	if (!success)
+		goto exit;
 
-	/* If a folder was not given, find the least-busy connection. */
-	if (folder_name == NULL) {
-		goto least_busy;
-	}
+	cinfo = connection_info_new (is);
 
-	/* First try to find a connection already handling this folder. */
-	for (link = list; link != NULL; link = g_list_next (link)) {
-		ConnectionInfo *candidate = link->data;
+	cinfo->refresh_mailbox_handler_id = g_signal_connect (
+		is, "refresh-mailbox", G_CALLBACK (imapx_conn_manager_refresh_mailbox_cb), conn_man);
 
-		if (camel_imapx_server_has_expensive_command (candidate->is))
-			expensive_connections++;
+	/* Takes ownership of the ConnectionInfo. */
+	conn_man->priv->connections = g_list_append (conn_man->priv->connections, cinfo);
 
-		if (connection_info_has_folder_name (candidate, folder_name) && camel_imapx_server_is_connected (candidate->is) &&
-		    (opened_connections >= concurrent_connections || for_expensive_job || !camel_imapx_server_has_expensive_command (candidate->is))) {
-			if (cinfo) {
-				/* group expensive jobs into one connection */
-				if (for_expensive_job && camel_imapx_server_has_expensive_command (cinfo->is))
-					continue;
+	c (camel_imapx_server_get_tagprefix (is), "Created new connection %p (server:%p) for %s; total connections %d\n",
+		cinfo, cinfo->is,
+		mailbox ? camel_imapx_mailbox_get_name (mailbox) : "[null]",
+		g_list_length (conn_man->priv->connections));
 
-				if (!for_expensive_job && camel_imapx_server_get_command_count (cinfo->is) < camel_imapx_server_get_command_count (candidate->is))
-					continue;
+exit:
+	g_object_unref (imapx_store);
+	g_clear_object (&is);
 
-				connection_info_unref (cinfo);
-			}
+	return cinfo;
+}
 
-			cinfo = connection_info_ref (candidate);
-			if (for_expensive_job && camel_imapx_server_has_expensive_command (cinfo->is))
-				goto exit;
-		}
-	}
+static gint
+imapx_conn_manager_get_max_connections (CamelIMAPXConnManager *conn_man)
+{
+	CamelIMAPXStore *imapx_store;
+	CamelSettings *settings;
+	gint max_connections;
 
- least_busy:
-	if (for_expensive_job) {
-		/* allow only half connections being with expensive operations */
-		if (expensive_connections > 0 &&
-		    expensive_connections < concurrent_connections / 2 &&
-		    opened_connections < concurrent_connections)
-			goto exit;
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), -1);
 
-		/* cinfo here doesn't have any expensive command, thus ignore it */
-		if (cinfo) {
-			connection_info_unref (cinfo);
-			cinfo = NULL;
-		}
+	imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
+	if (!imapx_store)
+		return -1;
 
-		/* Pick the connection with the least number of jobs in progress among those with expensive jobs. */
-		for (link = list; link != NULL; link = g_list_next (link)) {
-			ConnectionInfo *candidate = link->data;
-			guint jobs;
+	settings = camel_service_ref_settings (CAMEL_SERVICE (imapx_store));
 
-			if (!camel_imapx_server_is_connected (candidate->is) ||
-			    !camel_imapx_server_has_expensive_command (candidate->is))
-				continue;
+	max_connections = camel_imapx_settings_get_concurrent_connections (CAMEL_IMAPX_SETTINGS (settings));
 
-			jobs = camel_imapx_server_get_command_count (candidate->is);
+	if (conn_man->priv->limit_max_connections > 0 &&
+	    conn_man->priv->limit_max_connections < max_connections)
+		max_connections = conn_man->priv->limit_max_connections;
 
-			if (cinfo == NULL) {
-				cinfo = connection_info_ref (candidate);
-				min_jobs = jobs;
-
-			} else if (jobs < min_jobs) {
-				connection_info_unref (cinfo);
-				cinfo = connection_info_ref (candidate);
-				min_jobs = jobs;
-			}
-		}
+	g_object_unref (settings);
+	g_object_unref (imapx_store);
 
-		if (cinfo)
-			goto exit;
-	}
+	return max_connections > 0 ? max_connections : 1;
+}
 
-	/* Next try to find a connection not handling any folders. */
-	for (link = list; link != NULL; link = g_list_next (link)) {
-		ConnectionInfo *candidate = link->data;
+static void
+imapx_conn_manager_connection_wait_cancelled_cb (GCancellable *cancellable,
+						 CamelIMAPXConnManager *conn_man)
+{
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
 
-		if (camel_imapx_server_is_connected (candidate->is) &&
-		    connection_info_is_available (candidate)) {
-			if (cinfo)
-				connection_info_unref (cinfo);
-			cinfo = connection_info_ref (candidate);
-			goto exit;
-		}
-	}
+	imapx_conn_manager_signal_busy_connections (conn_man);
+}
 
-	/* open a new connection, if there is a room for it */
-	if (opened_connections < concurrent_connections && (!for_expensive_job || opened_connections < concurrent_connections / 2)) {
-		if (cinfo && camel_imapx_server_get_command_count (cinfo->is) != 0) {
-			connection_info_unref (cinfo);
-			cinfo = NULL;
-		}
-		goto exit;
-	} else {
-		if (cinfo)
-			min_jobs = camel_imapx_server_get_command_count (cinfo->is);
-	}
+static ConnectionInfo *
+camel_imapx_conn_manager_ref_connection (CamelIMAPXConnManager *conn_man,
+					 CamelIMAPXMailbox *mailbox,
+					 gboolean *out_is_new_connection,
+					 GCancellable *cancellable,
+					 GError **error)
+{
+	ConnectionInfo *cinfo = NULL;
+	CamelIMAPXStore *imapx_store;
+	CamelSession *session;
+	GError *local_error = NULL;
 
-	/* Pick the connection with the least number of jobs in progress. */
-	for (link = list; link != NULL; link = g_list_next (link)) {
-		ConnectionInfo *candidate = link->data;
-		gint n_commands;
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), NULL);
 
-		if (!camel_imapx_server_is_connected (candidate->is))
-			continue;
+	if (out_is_new_connection)
+		*out_is_new_connection = FALSE;
 
-		n_commands = camel_imapx_server_get_command_count (candidate->is);
+	imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
+	if (!imapx_store)
+		return NULL;
 
-		if (cinfo == NULL) {
-			cinfo = connection_info_ref (candidate);
-			min_jobs = n_commands;
+	session = camel_service_ref_session (CAMEL_SERVICE (imapx_store));
 
-		} else if (n_commands < min_jobs) {
-			connection_info_unref (cinfo);
-			cinfo = connection_info_ref (candidate);
-			min_jobs = n_commands;
+	if (camel_offline_store_get_online (CAMEL_OFFLINE_STORE (imapx_store)) &&
+	    session && camel_session_get_online (session)) {
+
+		g_mutex_lock (&conn_man->priv->pending_connections_lock);
+		if (cancellable) {
+			g_object_ref (cancellable);
+		} else {
+			cancellable = g_cancellable_new ();
+		}
+		conn_man->priv->pending_connections = g_slist_prepend (conn_man->priv->pending_connections, cancellable);
+		g_mutex_unlock (&conn_man->priv->pending_connections_lock);
+
+		/* Hold the writer lock while we requisition a CamelIMAPXServer
+		 * to prevent other threads from adding or removing connections. */
+		CON_READ_LOCK (conn_man);
+
+		/* Check if we've got cancelled while waiting for the lock. */
+		while (!cinfo && !g_cancellable_set_error_if_cancelled (cancellable, &local_error)) {
+			gint opened_connections, max_connections;
+			GList *link;
+
+			for (link = conn_man->priv->connections; link; link = g_list_next (link)) {
+				ConnectionInfo *candidate = link->data;
+
+				if (candidate && connection_info_try_reserve (candidate)) {
+					cinfo = connection_info_ref (candidate);
+					break;
+				}
+			}
+
+			if (cinfo)
+				break;
+
+			opened_connections = g_list_length (conn_man->priv->connections);
+			max_connections = imapx_conn_manager_get_max_connections (conn_man);
+
+			if (max_connections <= 0)
+				break;
+
+			if (!cinfo && opened_connections < max_connections) {
+				GError *local_error_2 = NULL;
+
+				CON_READ_UNLOCK (conn_man);
+				CON_WRITE_LOCK (conn_man);
+				cinfo = imapx_create_new_connection_unlocked (conn_man, mailbox, cancellable, &local_error_2);
+				if (cinfo)
+					connection_info_set_busy (cinfo, TRUE);
+				CON_WRITE_UNLOCK (conn_man);
+				CON_READ_LOCK (conn_man);
+
+				if (!cinfo) {
+					gboolean limit_connections =
+						g_error_matches (local_error_2, CAMEL_IMAPX_SERVER_ERROR,
+						CAMEL_IMAPX_SERVER_ERROR_CONCURRENT_CONNECT_FAILED) &&
+						conn_man->priv->connections;
+
+					c ('*', "Failed to open a new connection, while having %d opened, with error: %s; will limit connections: %s\n",
+						g_list_length (conn_man->priv->connections),
+						local_error_2 ? local_error_2->message : "Unknown error",
+						limit_connections ? "yes" : "no");
+
+					if (limit_connections) {
+						/* limit to one-less than current connection count - be nice to the server */
+						conn_man->priv->limit_max_connections = g_list_length (conn_man->priv->connections) - 1;
+						if (!conn_man->priv->limit_max_connections)
+							conn_man->priv->limit_max_connections = 1;
+
+						g_clear_error (&local_error_2);
+					} else {
+						if (local_error_2)
+							g_propagate_error (&local_error, local_error_2);
+						break;
+					}
+				} else {
+					connection_info_ref (cinfo);
+
+					if (out_is_new_connection)
+						*out_is_new_connection = TRUE;
+				}
+			}
+
+			if (!cinfo) {
+				gulong handler_id;
+
+				CON_READ_UNLOCK (conn_man);
+
+				handler_id = g_cancellable_connect (cancellable, G_CALLBACK (imapx_conn_manager_connection_wait_cancelled_cb), conn_man, NULL);
+
+				g_mutex_lock (&conn_man->priv->busy_connections_lock);
+				g_cond_wait (&conn_man->priv->busy_connections_cond, &conn_man->priv->busy_connections_lock);
+				g_mutex_unlock (&conn_man->priv->busy_connections_lock);
+
+				if (handler_id)
+					g_cancellable_disconnect (cancellable, handler_id);
+
+				CON_READ_LOCK (conn_man);
+			}
+		}
+
+		CON_READ_UNLOCK (conn_man);
+
+		g_mutex_lock (&conn_man->priv->pending_connections_lock);
+		conn_man->priv->pending_connections = g_slist_remove (conn_man->priv->pending_connections, cancellable);
+		g_object_unref (cancellable);
+		g_mutex_unlock (&conn_man->priv->pending_connections_lock);
+	}
+
+	g_clear_object (&imapx_store);
+	g_clear_object (&session);
+
+	if (!cinfo && (!local_error || local_error->domain == G_RESOLVER_ERROR)) {
+		if (local_error) {
+			g_set_error (
+				error, CAMEL_SERVICE_ERROR,
+				CAMEL_SERVICE_ERROR_UNAVAILABLE,
+				_("You must be working online to complete this operation (%s)"),
+				local_error->message);
+
+			g_clear_error (&local_error);
+		} else {
+			g_set_error_literal (
+				&local_error, CAMEL_SERVICE_ERROR,
+				CAMEL_SERVICE_ERROR_UNAVAILABLE,
+				_("You must be working online to complete this operation"));
+		}
+	}
+
+	if (local_error)
+		g_propagate_error (error, local_error);
+
+	return cinfo;
+}
+
+/****************************/
+
+CamelIMAPXConnManager *
+camel_imapx_conn_manager_new (CamelStore *store)
+{
+	g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
+
+	return g_object_new (
+		CAMEL_TYPE_IMAPX_CONN_MANAGER, "store", store, NULL);
+}
+
+CamelIMAPXStore *
+camel_imapx_conn_manager_ref_store (CamelIMAPXConnManager *conn_man)
+{
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), NULL);
+
+	return g_weak_ref_get (&conn_man->priv->store);
+}
+
+gboolean
+camel_imapx_conn_manager_connect_sync (CamelIMAPXConnManager *conn_man,
+				       GCancellable *cancellable,
+				       GError **error)
+{
+	ConnectionInfo *cinfo;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+
+	CON_READ_LOCK (conn_man);
+	if (conn_man->priv->connections) {
+		CON_READ_UNLOCK (conn_man);
+		return TRUE;
+	}
+	CON_READ_UNLOCK (conn_man);
+
+	imapx_conn_manager_clear_mailboxes_hashes (conn_man);
+
+	cinfo = camel_imapx_conn_manager_ref_connection (conn_man, NULL, NULL, cancellable, error);
+	if (cinfo) {
+		imapx_conn_manager_unmark_busy (conn_man, cinfo);
+		connection_info_unref (cinfo);
+	}
+
+	return cinfo != NULL;
+}
+
+gboolean
+camel_imapx_conn_manager_disconnect_sync (CamelIMAPXConnManager *conn_man,
+					  GCancellable *cancellable,
+					  GError **error)
+{
+	GList *link, *connections;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+
+	/* Do this before acquiring the write lock, because any pending
+	   connection holds the write lock, thus makes this request starve. */
+	imapx_conn_manager_cancel_pending_connections (conn_man);
+	imapx_conn_manager_abort_jobs (conn_man);
+
+	CON_WRITE_LOCK (conn_man);
+
+	c ('*', "Disconnecting all %d connections\n", g_list_length (conn_man->priv->connections));
+
+	connections = conn_man->priv->connections;
+	conn_man->priv->connections = NULL;
+
+	CON_WRITE_UNLOCK (conn_man);
+
+	for (link = connections; link; link = g_list_next (link)) {
+		ConnectionInfo *cinfo = link->data;
+		GError *local_error = NULL;
+
+		if (!cinfo)
+			continue;
+
+		if (!camel_imapx_server_disconnect_sync (cinfo->is, cancellable, &local_error)) {
+			c (camel_imapx_server_get_tagprefix (cinfo->is), "   Failed to disconnect from the server: %s\n",
+				local_error ? local_error->message : "Unknown error");
+		}
+
+		connection_info_unref (cinfo);
+		g_clear_error (&local_error);
+	}
+
+	g_list_free (connections);
+
+	imapx_conn_manager_clear_mailboxes_hashes (conn_man);
+
+	return TRUE;
+}
+
+static gboolean
+imapx_conn_manager_should_wait_for (CamelIMAPXConnManager *conn_man,
+				    CamelIMAPXJob *new_job,
+				    CamelIMAPXJob *queued_job)
+{
+	guint32 job_kind;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+	g_return_val_if_fail (queued_job != NULL, FALSE);
+
+	if (camel_imapx_job_get_kind (new_job) == CAMEL_IMAPX_JOB_GET_MESSAGE)
+		return FALSE;
+
+	job_kind = camel_imapx_job_get_kind (queued_job);
+
+	/* List jobs with high priority. */
+	return job_kind == CAMEL_IMAPX_JOB_GET_MESSAGE ||
+	       job_kind == CAMEL_IMAPX_JOB_COPY_MESSAGE ||
+	       job_kind == CAMEL_IMAPX_JOB_MOVE_MESSAGE ||
+	       job_kind == CAMEL_IMAPX_JOB_EXPUNGE;
+}
+
+gboolean
+camel_imapx_conn_manager_run_job_sync (CamelIMAPXConnManager *conn_man,
+				       CamelIMAPXJob *job,
+				       CamelIMAPXJobMatchesFunc finish_before_job,
+				       GCancellable *cancellable,
+				       GError **error)
+{
+	GSList *link;
+	ConnectionInfo *cinfo;
+	gboolean success = FALSE, is_new_connection = FALSE;
+	GError *local_error = NULL;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+	g_return_val_if_fail (job != NULL, FALSE);
+
+	JOB_QUEUE_LOCK (conn_man);
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
+		JOB_QUEUE_UNLOCK (conn_man);
+		return FALSE;
+	}
+
+	link = conn_man->priv->job_queue;
+	while (link) {
+		CamelIMAPXJob *queued_job = link->data;
+		gboolean matches;
+
+		g_warn_if_fail (queued_job != NULL);
+		g_warn_if_fail (queued_job != job);
+
+		if (!queued_job) {
+			link = g_slist_next (link);
+			continue;
+		}
+
+		matches = camel_imapx_job_matches (job, queued_job);
+		if (matches || (finish_before_job && finish_before_job (job, queued_job)) ||
+		    imapx_conn_manager_should_wait_for (conn_man, job, queued_job)) {
+			camel_imapx_job_ref (queued_job);
+
+			JOB_QUEUE_UNLOCK (conn_man);
+
+			camel_imapx_job_wait_sync (queued_job, cancellable);
+
+			if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
+				camel_imapx_job_unref (queued_job);
+				return FALSE;
+			}
+
+			if (matches) {
+				gpointer result = NULL;
+				GDestroyNotify destroy_result = NULL;
+
+				/* Do not inherit cancelled errors, just try again */
+				if (!camel_imapx_job_was_cancelled (queued_job) &&
+				    camel_imapx_job_copy_result (queued_job, &success, &result, &local_error, &destroy_result)) {
+					camel_imapx_job_set_result (job, success, result, local_error, destroy_result);
+					camel_imapx_job_unref (queued_job);
+
+					if (local_error)
+						g_propagate_error (error, local_error);
+
+					return success;
+				}
+			}
+
+			JOB_QUEUE_LOCK (conn_man);
+
+			camel_imapx_job_unref (queued_job);
+
+			/* The queue could change, start from the beginning. */
+			link = conn_man->priv->job_queue;
+		} else {
+			link = g_slist_next (link);
+		}
+	}
+
+	conn_man->priv->job_queue = g_slist_prepend (conn_man->priv->job_queue, job);
+
+	JOB_QUEUE_UNLOCK (conn_man);
+
+	do {
+		g_clear_error (&local_error);
+
+		cinfo = camel_imapx_conn_manager_ref_connection (conn_man, camel_imapx_job_get_mailbox (job), &is_new_connection, cancellable, error);
+		if (cinfo) {
+			CamelIMAPXMailbox *job_mailbox;
+
+			job_mailbox = camel_imapx_job_get_mailbox (job);
+
+			if (job_mailbox)
+				imapx_conn_manager_inc_mailbox_busy (conn_man, job_mailbox);
+
+			if (camel_imapx_server_is_in_idle (cinfo->is)) {
+				CamelIMAPXMailbox *idle_mailbox;
+
+				idle_mailbox = camel_imapx_server_ref_idle_mailbox (cinfo->is);
+				if (idle_mailbox)
+					imapx_conn_manager_dec_mailbox_idle (conn_man, idle_mailbox);
+				g_clear_object (&idle_mailbox);
+			}
+
+			success = camel_imapx_server_stop_idle_sync (cinfo->is, cancellable, &local_error);
+
+			if (success && camel_imapx_server_can_use_idle (cinfo->is)) {
+				GList *link, *connection_infos, *disconnected_infos = NULL;
+
+				CON_READ_LOCK (conn_man);
+				connection_infos = g_list_copy (conn_man->priv->connections);
+				g_list_foreach (connection_infos, (GFunc) connection_info_ref, NULL);
+				CON_READ_UNLOCK (conn_man);
+
+				/* Stop IDLE on all connections serving the same mailbox,
+				   to avoid notifications for changes done by itself */
+				for (link = connection_infos; link && !g_cancellable_is_cancelled (cancellable); link = g_list_next (link)) {
+					ConnectionInfo *other_cinfo = link->data;
+					CamelIMAPXMailbox *other_mailbox;
+
+					if (!other_cinfo || other_cinfo == cinfo || connection_info_get_busy (other_cinfo) ||
+					    !camel_imapx_server_is_in_idle (other_cinfo->is))
+						continue;
+
+					other_mailbox = camel_imapx_server_ref_idle_mailbox (other_cinfo->is);
+					if (job_mailbox == other_mailbox) {
+						if (!camel_imapx_server_stop_idle_sync (other_cinfo->is, cancellable, &local_error)) {
+							c (camel_imapx_server_get_tagprefix (other_cinfo->is),
+								"Failed to stop IDLE call (will be removed) on connection %p (server:%p) due to error: %s\n",
+								other_cinfo, other_cinfo->is, local_error ? local_error->message : "Unknown error");
+
+							camel_imapx_server_disconnect_sync (other_cinfo->is, cancellable, NULL);
+
+							disconnected_infos = g_list_prepend (disconnected_infos, connection_info_ref (other_cinfo));
+						} else {
+							imapx_conn_manager_dec_mailbox_idle (conn_man, other_mailbox);
+						}
+
+						g_clear_error (&local_error);
+					}
+
+					g_clear_object (&other_mailbox);
+				}
+
+				for (link = disconnected_infos; link; link = g_list_next (link)) {
+					ConnectionInfo *other_cinfo = link->data;
+
+					imapx_conn_manager_remove_info (conn_man, other_cinfo);
+				}
+
+				g_list_free_full (disconnected_infos, (GDestroyNotify) connection_info_unref);
+				g_list_free_full (connection_infos, (GDestroyNotify) connection_info_unref);
+			}
+
+			if (success)
+				success = camel_imapx_job_run_sync (job, cinfo->is, cancellable, &local_error);
+
+			if (job_mailbox)
+				imapx_conn_manager_dec_mailbox_busy (conn_man, job_mailbox);
+
+			if (success) {
+				CamelIMAPXMailbox *idle_mailbox = NULL;
+
+				if (!imapx_conn_manager_has_inbox_idle (conn_man)) {
+					CamelIMAPXStore *imapx_store;
+
+					imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
+					idle_mailbox = imapx_store ? camel_imapx_store_ref_mailbox (imapx_store, "INBOX") : NULL;
+
+					g_clear_object (&imapx_store);
+				}
+
+				if (!idle_mailbox)
+					idle_mailbox = camel_imapx_server_ref_selected (cinfo->is);
+
+				/* Can start IDLE on the connection only if the IDLE folder is not busy
+				   and not in IDLE already, to avoid multiple IDLE notifications on the same mailbox */
+				if (idle_mailbox && camel_imapx_server_can_use_idle (cinfo->is) &&
+				    !imapx_conn_manager_is_mailbox_busy (conn_man, idle_mailbox) &&
+				    !imapx_conn_manager_is_mailbox_idle (conn_man, idle_mailbox)) {
+					camel_imapx_server_schedule_idle_sync (cinfo->is, idle_mailbox, cancellable, NULL);
+
+					if (camel_imapx_server_is_in_idle (cinfo->is)) {
+						g_clear_object (&idle_mailbox);
+
+						idle_mailbox = camel_imapx_server_ref_idle_mailbox (cinfo->is);
+						if (idle_mailbox)
+							imapx_conn_manager_inc_mailbox_idle (conn_man, idle_mailbox);
+					}
+				}
+
+				g_clear_object (&idle_mailbox);
+
+				imapx_conn_manager_unmark_busy (conn_man, cinfo);
+			} else if (!local_error || ((local_error->domain == G_IO_ERROR || local_error->domain == G_TLS_ERROR || local_error->domain == CAMEL_IMAPX_ERROR ||
+				   g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) &&
+				   !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))) {
+				c (camel_imapx_server_get_tagprefix (cinfo->is), "Removed connection %p (server:%p) due to error: %s\n",
+					cinfo, cinfo->is, local_error ? local_error->message : "Unknown error");
+
+				camel_imapx_server_disconnect_sync (cinfo->is, cancellable, NULL);
+				imapx_conn_manager_remove_info (conn_man, cinfo);
+
+				if (!local_error ||
+				    g_error_matches (local_error, G_TLS_ERROR, G_TLS_ERROR_MISC) ||
+				    g_error_matches (local_error, G_TLS_ERROR, G_TLS_ERROR_EOF) ||
+				    g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CLOSED)) {
+					GError *tmp = local_error;
+
+					local_error = NULL;
+
+					/* This message won't get into UI. */
+					g_set_error (&local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT,
+						"Reconnect after failure: %s", tmp ? tmp->message : "Unknown error");
+
+					g_clear_error (&tmp);
+				}
+			} else {
+				c (camel_imapx_server_get_tagprefix (cinfo->is), "Unmark connection %p (server:%p) busy after failure, error: %s\n",
+					cinfo, cinfo->is, local_error ? local_error->message : "Unknown error");
+
+				imapx_conn_manager_unmark_busy (conn_man, cinfo);
+			}
+
+			connection_info_unref (cinfo);
+		}
+
+		/* If there's a reconnect required for a new connection, then there happened
+		   something really wrong, thus rather give up. */
+	} while (!success && !is_new_connection && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT));
+
+	if (local_error)
+		g_propagate_error (error, local_error);
+
+	JOB_QUEUE_LOCK (conn_man);
+	conn_man->priv->job_queue = g_slist_remove (conn_man->priv->job_queue, job);
+	JOB_QUEUE_UNLOCK (conn_man);
+
+	camel_imapx_job_done (job);
+
+	return success;
+}
+
+static gboolean
+imapx_conn_manager_nothing_matches (CamelIMAPXJob *job,
+				    CamelIMAPXJob *other_job)
+{
+	/* For jobs where none can match. */
+	return FALSE;
+}
+
+static gboolean
+imapx_conn_manager_matches_sync_changes_or_refresh_info (CamelIMAPXJob *job,
+							 CamelIMAPXJob *other_job)
+{
+	CamelIMAPXJobKind other_job_kind;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (other_job != NULL, FALSE);
+	g_return_val_if_fail (job != other_job, FALSE);
+
+	if (camel_imapx_job_get_mailbox (job) != camel_imapx_job_get_mailbox (other_job))
+		return FALSE;
+
+	other_job_kind = camel_imapx_job_get_kind (other_job);
+
+	return other_job_kind == CAMEL_IMAPX_JOB_SYNC_CHANGES ||
+	       other_job_kind == CAMEL_IMAPX_JOB_REFRESH_INFO;
+}
+
+struct ListJobData {
+	gchar *pattern;
+	CamelStoreGetFolderInfoFlags flags;
+};
+
+static void
+list_job_data_free (gpointer ptr)
+{
+	struct ListJobData *job_data = ptr;
+
+	if (job_data) {
+		g_free (job_data->pattern);
+		g_free (job_data);
+	}
+}
+
+static gboolean
+imapx_conn_manager_list_run_sync (CamelIMAPXJob *job,
+				  CamelIMAPXServer *server,
+				  GCancellable *cancellable,
+				  GError **error)
+{
+	struct ListJobData *job_data;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
+
+	job_data = camel_imapx_job_get_user_data (job);
+	g_return_val_if_fail (job_data != NULL, FALSE);
+
+	return camel_imapx_server_list_sync (server, job_data->pattern, job_data->flags, cancellable, error);
+}
+
+static gboolean
+imapx_conn_manager_list_matches (CamelIMAPXJob *job,
+				 CamelIMAPXJob *other_job)
+{
+	struct ListJobData *job_data, *other_job_data;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (other_job != NULL, FALSE);
+
+	if (camel_imapx_job_get_kind (job) != CAMEL_IMAPX_JOB_LIST ||
+	    camel_imapx_job_get_kind (job) != camel_imapx_job_get_kind (other_job))
+		return FALSE;
+
+	job_data = camel_imapx_job_get_user_data (job);
+	other_job_data = camel_imapx_job_get_user_data (other_job);
+
+	if (!job_data || !other_job_data)
+		return FALSE;
+
+	return job_data->flags == other_job_data->flags &&
+	       g_strcmp0 (job_data->pattern, other_job_data->pattern) == 0;
+}
+
+gboolean
+camel_imapx_conn_manager_list_sync (CamelIMAPXConnManager *conn_man,
+				    const gchar *pattern,
+				    CamelStoreGetFolderInfoFlags flags,
+				    GCancellable *cancellable,
+				    GError **error)
+{
+	CamelIMAPXJob *job;
+	struct ListJobData *job_data;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_LIST, NULL,
+		imapx_conn_manager_list_run_sync,
+		imapx_conn_manager_list_matches,
+		NULL);
+
+	job_data = g_new0 (struct ListJobData, 1);
+	job_data->pattern = g_strdup (pattern);
+	job_data->flags = flags;
+
+	camel_imapx_job_set_user_data (job, job_data, list_job_data_free);
+
+	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
+	if (success)
+		camel_imapx_job_copy_result (job, &success, NULL, error, NULL);
+
+	camel_imapx_job_unref (job);
+
+	return success;
+}
+
+static gboolean
+imapx_conn_manager_refresh_info_run_sync (CamelIMAPXJob *job,
+					  CamelIMAPXServer *server,
+					  GCancellable *cancellable,
+					  GError **error)
+{
+	CamelIMAPXMailbox *mailbox;
+	gboolean success;
+	GError *local_error = NULL;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
+
+	mailbox = camel_imapx_job_get_mailbox (job);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+
+	success = camel_imapx_server_refresh_info_sync (server, mailbox, cancellable, &local_error);
+
+	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
+
+	if (local_error)
+		g_propagate_error (error, local_error);
+
+	return success;
+}
+
+gboolean
+camel_imapx_conn_manager_refresh_info_sync (CamelIMAPXConnManager *conn_man,
+					    CamelIMAPXMailbox *mailbox,
+					    GCancellable *cancellable,
+					    GError **error)
+{
+	CamelIMAPXJob *job;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+
+	if (!camel_imapx_conn_manager_sync_changes_sync (conn_man, mailbox, cancellable, error))
+		return FALSE;
+
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_REFRESH_INFO, mailbox,
+		imapx_conn_manager_refresh_info_run_sync, NULL, NULL);
+
+	success = camel_imapx_conn_manager_run_job_sync (conn_man, job,
+		imapx_conn_manager_matches_sync_changes_or_refresh_info,
+		cancellable, error);
+
+	camel_imapx_job_unref (job);
+
+	return success;
+}
+
+static gboolean
+imapx_conn_manager_move_to_real_junk_sync (CamelIMAPXConnManager *conn_man,
+					   CamelFolder *folder,
+					   GCancellable *cancellable,
+					   gboolean *out_need_to_expunge,
+					   GError **error)
+{
+	CamelIMAPXFolder *imapx_folder;
+	CamelIMAPXMailbox *mailbox;
+	CamelIMAPXSettings *settings;
+	GPtrArray *uids_to_copy;
+	gchar *real_junk_path = NULL;
+	gboolean success = TRUE;
+
+	*out_need_to_expunge = FALSE;
+
+	/* Caller already obtained the mailbox from the folder,
+	 * so the folder should still have it readily available. */
+	imapx_folder = CAMEL_IMAPX_FOLDER (folder);
+	mailbox = camel_imapx_folder_ref_mailbox (imapx_folder);
+	g_return_val_if_fail (mailbox != NULL, FALSE);
+
+	uids_to_copy = g_ptr_array_new_with_free_func (
+		(GDestroyNotify) camel_pstring_free);
+
+	settings = CAMEL_IMAPX_SETTINGS (camel_service_ref_settings (CAMEL_SERVICE (camel_folder_get_parent_store (folder))));
+	if (camel_imapx_settings_get_use_real_junk_path (settings)) {
+		real_junk_path = camel_imapx_settings_dup_real_junk_path (settings);
+		camel_imapx_folder_claim_move_to_real_junk_uids (imapx_folder, uids_to_copy);
+	}
+	g_object_unref (settings);
+
+	if (uids_to_copy->len > 0) {
+		CamelIMAPXStore *imapx_store;
+		CamelIMAPXMailbox *destination = NULL;
+
+		imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
+
+		if (real_junk_path != NULL) {
+			folder = camel_store_get_folder_sync (
+				CAMEL_STORE (imapx_store),
+				real_junk_path, 0,
+				cancellable, error);
+		} else {
+			g_set_error (
+				error, CAMEL_FOLDER_ERROR,
+				CAMEL_FOLDER_ERROR_INVALID_PATH,
+				_("No destination folder specified"));
+			folder = NULL;
+		}
+
+		if (folder != NULL) {
+			destination = camel_imapx_folder_list_mailbox (
+				CAMEL_IMAPX_FOLDER (folder),
+				cancellable, error);
+			g_object_unref (folder);
+		}
+
+		/* Avoid duplicating messages in the Junk folder. */
+		if (destination == mailbox) {
+			success = TRUE;
+		} else if (destination != NULL) {
+			success = imapx_conn_manager_copy_message_sync (
+				conn_man, mailbox, destination,
+				uids_to_copy, TRUE, FALSE, TRUE,
+				cancellable, error);
+			*out_need_to_expunge = success;
+		} else {
+			success = FALSE;
+		}
+
+		if (!success) {
+			g_prefix_error (
+				error, "%s: ",
+				_("Unable to move junk messages"));
 		}
+
+		g_clear_object (&destination);
+		g_clear_object (&imapx_store);
+	}
+
+	g_ptr_array_unref (uids_to_copy);
+	g_free (real_junk_path);
+
+	g_clear_object (&mailbox);
+
+	return success;
+}
+
+static gboolean
+imapx_conn_manager_move_to_real_trash_sync (CamelIMAPXConnManager *conn_man,
+					    CamelFolder *folder,
+					    GCancellable *cancellable,
+					    gboolean *out_need_to_expunge,
+					    GError **error)
+{
+	CamelIMAPXFolder *imapx_folder;
+	CamelIMAPXMailbox *mailbox, *destination = NULL;
+	CamelIMAPXSettings *settings;
+	CamelIMAPXStore *imapx_store;
+	GPtrArray *uids_to_copy;
+	gchar *real_trash_path = NULL;
+	guint32 folder_deleted_count = 0;
+	gboolean success = TRUE;
+
+	*out_need_to_expunge = FALSE;
+
+	/* Caller already obtained the mailbox from the folder,
+	 * so the folder should still have it readily available. */
+	imapx_folder = CAMEL_IMAPX_FOLDER (folder);
+	mailbox = camel_imapx_folder_ref_mailbox (imapx_folder);
+	g_return_val_if_fail (mailbox != NULL, FALSE);
+
+	uids_to_copy = g_ptr_array_new_with_free_func (
+		(GDestroyNotify) camel_pstring_free);
+
+	settings = CAMEL_IMAPX_SETTINGS (camel_service_ref_settings (CAMEL_SERVICE (camel_folder_get_parent_store (folder))));
+	if (camel_imapx_settings_get_use_real_trash_path (settings)) {
+		real_trash_path = camel_imapx_settings_dup_real_trash_path (settings);
+		camel_imapx_folder_claim_move_to_real_trash_uids (CAMEL_IMAPX_FOLDER (folder), uids_to_copy);
+	}
+	g_object_unref (settings);
+
+	imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
+
+	if (real_trash_path != NULL) {
+		folder = camel_store_get_folder_sync (
+			CAMEL_STORE (imapx_store),
+			real_trash_path, 0,
+			cancellable, error);
+	} else {
+		if (uids_to_copy->len > 0) {
+			g_set_error (
+				error, CAMEL_FOLDER_ERROR,
+				CAMEL_FOLDER_ERROR_INVALID_PATH,
+				_("No destination folder specified"));
+		}
+
+		folder = NULL;
+	}
+
+	if (folder != NULL) {
+		destination = camel_imapx_folder_list_mailbox (
+			CAMEL_IMAPX_FOLDER (folder),
+			cancellable, error);
+		folder_deleted_count = camel_folder_summary_get_deleted_count (folder->summary);
+		g_object_unref (folder);
+	}
+
+	/* Avoid duplicating messages in the Trash folder. */
+	if (destination == mailbox) {
+		success = TRUE;
+		/* Deleted messages in the real Trash folder will be permanently deleted immediately. */
+		*out_need_to_expunge = folder_deleted_count > 0 || uids_to_copy->len > 0;
+	} else if (destination != NULL) {
+		if (uids_to_copy->len > 0) {
+			success = imapx_conn_manager_copy_message_sync (
+				conn_man, mailbox, destination,
+				uids_to_copy, TRUE, TRUE, TRUE,
+				cancellable, error);
+			*out_need_to_expunge = success;
+		}
+	} else if (uids_to_copy->len > 0) {
+		success = FALSE;
+	}
+
+	if (!success) {
+		g_prefix_error (
+			error, "%s: ",
+			_("Unable to move deleted messages"));
+	}
+
+	g_ptr_array_unref (uids_to_copy);
+	g_free (real_trash_path);
+
+	g_clear_object (&imapx_store);
+	g_clear_object (&destination);
+	g_clear_object (&mailbox);
+
+	return success;
+}
+
+static gboolean
+imapx_conn_manager_expunge_sync (CamelIMAPXConnManager *conn_man,
+				 CamelIMAPXMailbox *mailbox,
+				 gboolean skip_sync_changes,
+				 GCancellable *cancellable,
+				 GError **error);
+
+static gboolean
+imapx_conn_manager_sync_changes_run_sync (CamelIMAPXJob *job,
+					  CamelIMAPXServer *server,
+					  GCancellable *cancellable,
+					  GError **error)
+{
+	CamelIMAPXMailbox *mailbox;
+	GError *local_error = NULL;
+	gboolean can_influence_flags, success;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
+
+	mailbox = camel_imapx_job_get_mailbox (job);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+
+	can_influence_flags = GPOINTER_TO_INT (camel_imapx_job_get_user_data (job)) == 1;
+
+	success = camel_imapx_server_sync_changes_sync (server, mailbox, can_influence_flags, cancellable, &local_error);
+
+	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
+
+	if (local_error)
+		g_propagate_error (error, local_error);
+
+	return success;
+}
+
+static gboolean
+imapx_conn_manager_sync_changes_matches (CamelIMAPXJob *job,
+					 CamelIMAPXJob *other_job)
+{
+	gboolean job_can_influence_flags, other_job_can_influence_flags;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (other_job != NULL, FALSE);
+
+	if (camel_imapx_job_get_kind (job) != CAMEL_IMAPX_JOB_SYNC_CHANGES ||
+	    camel_imapx_job_get_kind (job) != camel_imapx_job_get_kind (other_job))
+		return FALSE;
+
+	job_can_influence_flags = GPOINTER_TO_INT (camel_imapx_job_get_user_data (job)) == 1;
+	other_job_can_influence_flags = GPOINTER_TO_INT (camel_imapx_job_get_user_data (other_job)) == 1;
+
+	return job_can_influence_flags == other_job_can_influence_flags;
+}
+
+gboolean
+camel_imapx_conn_manager_sync_changes_sync (CamelIMAPXConnManager *conn_man,
+					    CamelIMAPXMailbox *mailbox,
+					    GCancellable *cancellable,
+					    GError **error)
+{
+	CamelIMAPXJob *job;
+	CamelFolder *folder = NULL;
+	gboolean need_to_expunge = FALSE, expunge = FALSE;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_SYNC_CHANGES, mailbox,
+		imapx_conn_manager_sync_changes_run_sync,
+		imapx_conn_manager_sync_changes_matches, NULL);
+
+	/* Skip store of the \Deleted flag */
+	camel_imapx_job_set_user_data (job, GINT_TO_POINTER (1), NULL);
+
+	success = camel_imapx_conn_manager_run_job_sync (conn_man, job,
+		imapx_conn_manager_matches_sync_changes_or_refresh_info,
+		cancellable, error);
+
+	camel_imapx_job_unref (job);
+
+	if (success) {
+		folder = imapx_conn_manager_ref_folder_sync (conn_man, mailbox, cancellable, error);
+		if (!folder)
+			success = FALSE;
+	}
+
+	if (success) {
+		success = imapx_conn_manager_move_to_real_junk_sync (
+			conn_man, folder, cancellable,
+			&need_to_expunge, error);
+		expunge |= need_to_expunge;
+	}
+
+	if (success) {
+		success = imapx_conn_manager_move_to_real_trash_sync (
+			conn_man, folder, cancellable,
+			&need_to_expunge, error);
+		expunge |= need_to_expunge;
+	}
+
+	if (success && expunge) {
+		job = camel_imapx_job_new (CAMEL_IMAPX_JOB_SYNC_CHANGES, mailbox,
+			imapx_conn_manager_sync_changes_run_sync,
+			imapx_conn_manager_sync_changes_matches, NULL);
+
+		/* Store also the \Deleted flag */
+		camel_imapx_job_set_user_data (job, GINT_TO_POINTER (0), NULL);
+
+		success = camel_imapx_conn_manager_run_job_sync (conn_man, job,
+			imapx_conn_manager_matches_sync_changes_or_refresh_info,
+			cancellable, error);
+
+		camel_imapx_job_unref (job);
+
+		success = imapx_conn_manager_expunge_sync (conn_man, mailbox, TRUE, cancellable, error);
+	}
+
+	g_clear_object (&folder);
+
+	return success;
+}
+
+static gboolean
+imapx_conn_manager_expunge_run_sync (CamelIMAPXJob *job,
+				     CamelIMAPXServer *server,
+				     GCancellable *cancellable,
+				     GError **error)
+{
+	CamelIMAPXMailbox *mailbox;
+	GError *local_error = NULL;
+	gboolean success;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
+
+	mailbox = camel_imapx_job_get_mailbox (job);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+
+	success = camel_imapx_server_expunge_sync (server, mailbox, cancellable, &local_error);
+
+	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
+
+	if (local_error)
+		g_propagate_error (error, local_error);
+
+	return success;
+}
+
+static gboolean
+imapx_conn_manager_expunge_sync (CamelIMAPXConnManager *conn_man,
+				 CamelIMAPXMailbox *mailbox,
+				 gboolean skip_sync_changes,
+				 GCancellable *cancellable,
+				 GError **error)
+{
+	CamelIMAPXJob *job;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+
+	if (!skip_sync_changes && !camel_imapx_conn_manager_sync_changes_sync (conn_man, mailbox, cancellable, error))
+		return FALSE;
+
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_EXPUNGE, mailbox,
+		imapx_conn_manager_expunge_run_sync, NULL, NULL);
+
+	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
+
+	camel_imapx_job_unref (job);
+
+	return success;
+}
+
+gboolean
+camel_imapx_conn_manager_expunge_sync (CamelIMAPXConnManager *conn_man,
+				       CamelIMAPXMailbox *mailbox,
+				       GCancellable *cancellable,
+				       GError **error)
+{
+	return imapx_conn_manager_expunge_sync (conn_man, mailbox, FALSE, cancellable, error);
+}
+
+struct GetMessageJobData {
+	CamelFolderSummary *summary;
+	CamelDataCache *message_cache;
+	gchar *message_uid;
+};
+
+static void
+get_message_job_data_free (gpointer ptr)
+{
+	struct GetMessageJobData *job_data = ptr;
+
+	if (job_data) {
+		g_clear_object (&job_data->summary);
+		g_clear_object (&job_data->message_cache);
+		g_free (job_data->message_uid);
+		g_free (job_data);
+	}
+}
+
+static gboolean
+imapx_conn_manager_get_message_run_sync (CamelIMAPXJob *job,
+					 CamelIMAPXServer *server,
+					 GCancellable *cancellable,
+					 GError **error)
+{
+	struct GetMessageJobData *job_data;
+	CamelIMAPXMailbox *mailbox;
+	CamelStream *result;
+	GError *local_error = NULL;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
+
+	mailbox = camel_imapx_job_get_mailbox (job);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+
+	job_data = camel_imapx_job_get_user_data (job);
+	g_return_val_if_fail (job_data != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (job_data->summary), FALSE);
+	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (job_data->message_cache), FALSE);
+	g_return_val_if_fail (job_data->message_uid != NULL, FALSE);
+
+	result = camel_imapx_server_get_message_sync (
+		server, mailbox, job_data->summary, job_data->message_cache, job_data->message_uid,
+		cancellable, &local_error);
+
+	camel_imapx_job_set_result (job, result != NULL, result, local_error, result ? g_object_unref : NULL);
+
+	if (local_error)
+		g_propagate_error (error, local_error);
+
+	return result != NULL;
+}
+
+static gboolean
+imapx_conn_manager_get_message_matches (CamelIMAPXJob *job,
+					CamelIMAPXJob *other_job)
+{
+	struct GetMessageJobData *job_data, *other_job_data;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (other_job != NULL, FALSE);
+
+	if (camel_imapx_job_get_kind (job) != CAMEL_IMAPX_JOB_GET_MESSAGE ||
+	    camel_imapx_job_get_kind (job) != camel_imapx_job_get_kind (other_job))
+		return FALSE;
+
+	job_data = camel_imapx_job_get_user_data (job);
+	other_job_data = camel_imapx_job_get_user_data (other_job);
+
+	if (!job_data || !other_job_data)
+		return FALSE;
+
+	return g_strcmp0 (job_data->message_uid, other_job_data->message_uid) == 0;
+}
+
+static void
+imapx_conn_manager_get_message_copy_result (CamelIMAPXJob *job,
+					    gconstpointer set_result,
+					    gpointer *out_result)
+{
+	if (!set_result || !*out_result)
+		return;
+
+	*out_result = g_object_ref ((gpointer) set_result);
+}
+
+CamelStream *
+camel_imapx_conn_manager_get_message_sync (CamelIMAPXConnManager *conn_man,
+					   CamelIMAPXMailbox *mailbox,
+					   CamelFolderSummary *summary,
+					   CamelDataCache *message_cache,
+					   const gchar *message_uid,
+					   GCancellable *cancellable,
+					   GError **error)
+{
+	CamelIMAPXJob *job;
+	struct GetMessageJobData *job_data;
+	CamelStream *result;
+	gpointer result_data = NULL;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), NULL);
+
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_GET_MESSAGE, mailbox,
+		imapx_conn_manager_get_message_run_sync,
+		imapx_conn_manager_get_message_matches,
+		imapx_conn_manager_get_message_copy_result);
+
+	job_data = g_new0 (struct GetMessageJobData, 1);
+	job_data->summary = g_object_ref (summary);
+	job_data->message_cache = g_object_ref (message_cache);
+	job_data->message_uid = g_strdup (message_uid);
+
+	camel_imapx_job_set_user_data (job, job_data, get_message_job_data_free);
+
+	if (camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error) &&
+	    camel_imapx_job_take_result_data (job, &result_data)) {
+		result = result_data;
+	} else {
+		result = NULL;
+	}
+
+	camel_imapx_job_unref (job);
+
+	return result;
+}
+
+struct CopyMessageJobData {
+	CamelIMAPXMailbox *destination;
+	GPtrArray *uids;
+	gboolean delete_originals;
+	gboolean remove_deleted_flags;
+};
+
+static void
+copy_message_job_data_free (gpointer ptr)
+{
+	struct CopyMessageJobData *job_data = ptr;
+
+	if (job_data) {
+		g_clear_object (&job_data->destination);
+		g_ptr_array_free (job_data->uids, TRUE);
+		g_free (job_data);
+	}
+}
+
+static gboolean
+imapx_conn_manager_copy_message_run_sync (CamelIMAPXJob *job,
+					  CamelIMAPXServer *server,
+					  GCancellable *cancellable,
+					  GError **error)
+{
+	struct CopyMessageJobData *job_data;
+	CamelIMAPXMailbox *mailbox;
+	GError *local_error = NULL;
+	gboolean success;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
+
+	mailbox = camel_imapx_job_get_mailbox (job);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+
+	job_data = camel_imapx_job_get_user_data (job);
+	g_return_val_if_fail (job_data != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (job_data->destination), FALSE);
+	g_return_val_if_fail (job_data->uids != NULL, FALSE);
+
+	success = camel_imapx_server_copy_message_sync (
+		server, mailbox, job_data->destination, job_data->uids, job_data->delete_originals,
+		job_data->remove_deleted_flags, cancellable, &local_error);
+
+	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
+
+	if (local_error)
+		g_propagate_error (error, local_error);
+
+	return success;
+}
+
+static gboolean
+imapx_conn_manager_copy_message_sync (CamelIMAPXConnManager *conn_man,
+				      CamelIMAPXMailbox *mailbox,
+				      CamelIMAPXMailbox *destination,
+				      GPtrArray *uids,
+				      gboolean delete_originals,
+				      gboolean remove_deleted_flags,
+				      gboolean skip_sync_changes,
+				      GCancellable *cancellable,
+				      GError **error)
+{
+	CamelIMAPXJob *job;
+	struct CopyMessageJobData *job_data;
+	gboolean success;
+	gint ii;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+
+	if (!skip_sync_changes && !camel_imapx_conn_manager_sync_changes_sync (conn_man, mailbox, cancellable, error))
+		return FALSE;
+
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_COPY_MESSAGE, mailbox,
+		imapx_conn_manager_copy_message_run_sync,
+		imapx_conn_manager_nothing_matches,
+		NULL);
+
+	job_data = g_new0 (struct CopyMessageJobData, 1);
+	job_data->destination = g_object_ref (destination);
+	job_data->uids = g_ptr_array_new_full (uids->len, (GDestroyNotify) camel_pstring_free);
+	job_data->delete_originals = delete_originals;
+	job_data->remove_deleted_flags = remove_deleted_flags;
+
+	for (ii = 0; ii < uids->len; ii++) {
+		g_ptr_array_add (job_data->uids, (gpointer) camel_pstring_strdup (uids->pdata[ii]));
 	}
 
-exit:
-	if (cinfo != NULL && folder_name != NULL)
-		connection_info_insert_folder_name (cinfo, folder_name);
+	camel_imapx_job_set_user_data (job, job_data, copy_message_job_data_free);
+
+	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
+
+	camel_imapx_job_unref (job);
 
-	if (camel_debug_flag (conman)) {
-		printf ("%s: for-expensive:%d will return:%p cmd-count:%d has-expensive:%d found:%d; connections opened:%d max:%d\n", G_STRFUNC, for_expensive_job, cinfo, cinfo ? camel_imapx_server_get_command_count (cinfo->is) : -2, cinfo ? camel_imapx_server_has_expensive_command (cinfo->is) : -2, expensive_connections, g_list_length (list), concurrent_connections);
-		for (link = list; link != NULL; link = g_list_next (link)) {
-			ConnectionInfo *candidate = link->data;
+	if (success) {
+		CamelFolder *dest;
 
-			printf ("   cmds:%d has-expensive:%d avail:%d cinfo:%p server:%p\n", camel_imapx_server_get_command_count (candidate->is), camel_imapx_server_has_expensive_command (candidate->is), connection_info_is_available (candidate), candidate, candidate->is);
+		dest = imapx_conn_manager_ref_folder_sync (conn_man, destination, cancellable, NULL);
+
+		/* Update destination folder only if it's not frozen,
+		 * to avoid updating for each "move" action on a single
+		 * message while filtering. */
+		if (dest && !camel_folder_is_frozen (dest)) {
+			/* Ignore errors here */
+			camel_imapx_conn_manager_refresh_info_sync (conn_man, destination, cancellable, NULL);
 		}
-	}
 
-	if (cinfo != NULL) {
-		is = g_object_ref (cinfo->is);
-		connection_info_unref (cinfo);
+		g_clear_object (&dest);
 	}
 
-	g_object_unref (store);
+	return success;
+}
 
-	return is;
+gboolean
+camel_imapx_conn_manager_copy_message_sync (CamelIMAPXConnManager *conn_man,
+					    CamelIMAPXMailbox *mailbox,
+					    CamelIMAPXMailbox *destination,
+					    GPtrArray *uids,
+					    gboolean delete_originals,
+					    gboolean remove_deleted_flags,
+					    GCancellable *cancellable,
+					    GError **error)
+{
+	return imapx_conn_manager_copy_message_sync (conn_man, mailbox, destination, uids,
+		delete_originals, remove_deleted_flags, FALSE, cancellable, error);
 }
 
-static gchar
-imapx_conn_manager_get_next_free_tagprefix_unlocked (CamelIMAPXConnManager *con_man)
+struct AppendMessageJobData {
+	CamelFolderSummary *summary;
+	CamelDataCache *message_cache;
+	CamelMimeMessage *message;
+	const CamelMessageInfo *mi;
+};
+
+static void
+append_message_job_data_free (gpointer ptr)
 {
-	gchar adept;
-	GList *iter;
+	struct AppendMessageJobData *job_data = ptr;
 
-	/* the 'Z' is dedicated to auth types query */
-	adept = 'A';
-	while (adept < 'Z') {
-		for (iter = con_man->priv->connections; iter; iter = g_list_next (iter)) {
-			ConnectionInfo *cinfo = iter->data;
+	if (job_data) {
+		g_clear_object (&job_data->summary);
+		g_clear_object (&job_data->message_cache);
+		g_clear_object (&job_data->message);
+		g_free (job_data);
+	}
+}
 
-			if (!cinfo || !cinfo->is)
-				continue;
+static gboolean
+imapx_conn_manager_append_message_run_sync (CamelIMAPXJob *job,
+					    CamelIMAPXServer *server,
+					    GCancellable *cancellable,
+					    GError **error)
+{
+	struct AppendMessageJobData *job_data;
+	CamelIMAPXMailbox *mailbox;
+	gchar *appended_uid = NULL;
+	GError *local_error = NULL;
+	gboolean success;
 
-			if (cinfo->is->tagprefix == adept)
-				break;
-		}
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
 
-		/* Read all current active connections and none has the same tag prefix */
-		if (!iter)
-			break;
+	mailbox = camel_imapx_job_get_mailbox (job);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
 
-		adept++;
+	job_data = camel_imapx_job_get_user_data (job);
+	g_return_val_if_fail (job_data != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (job_data->summary), FALSE);
+	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (job_data->message_cache), FALSE);
+	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (job_data->message), FALSE);
+
+	success = camel_imapx_server_append_message_sync (server, mailbox, job_data->summary, job_data->message_cache,
+		job_data->message, job_data->mi, &appended_uid, cancellable, &local_error);
+
+	camel_imapx_job_set_result (job, success, appended_uid, local_error, appended_uid ? g_free : NULL);
+
+	if (local_error)
+		g_propagate_error (error, local_error);
+
+	return success;
+}
+
+gboolean
+camel_imapx_conn_manager_append_message_sync (CamelIMAPXConnManager *conn_man,
+					      CamelIMAPXMailbox *mailbox,
+					      CamelFolderSummary *summary,
+					      CamelDataCache *message_cache,
+					      CamelMimeMessage *message,
+					      const CamelMessageInfo *mi,
+					      gchar **append_uid,
+					      GCancellable *cancellable,
+					      GError **error)
+{
+	CamelIMAPXJob *job;
+	struct AppendMessageJobData *job_data;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_APPEND_MESSAGE, mailbox,
+		imapx_conn_manager_append_message_run_sync,
+		imapx_conn_manager_nothing_matches,
+		NULL);
+
+	job_data = g_new0 (struct AppendMessageJobData, 1);
+	job_data->summary = g_object_ref (summary);
+	job_data->message_cache = g_object_ref (message_cache);
+	job_data->message = g_object_ref (message);
+	job_data->mi = mi;
+
+	camel_imapx_job_set_user_data (job, job_data, append_message_job_data_free);
+
+	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
+	if (success) {
+		gpointer result_data = NULL;
+
+		success = camel_imapx_job_take_result_data (job, &result_data);
+		if (success && append_uid)
+			*append_uid = result_data;
+		else
+			g_free (result_data);
 	}
 
-	g_return_val_if_fail (adept >= 'A' && adept < 'Z', 'Z');
+	camel_imapx_job_unref (job);
 
-	return adept;
+	return success;
 }
 
-static CamelIMAPXServer *
-imapx_create_new_connection_unlocked (CamelIMAPXConnManager *con_man,
-                                      const gchar *folder_name,
-                                      GCancellable *cancellable,
-                                      GError **error)
+static gboolean
+imapx_conn_manager_sync_message_run_sync (CamelIMAPXJob *job,
+					  CamelIMAPXServer *server,
+					  GCancellable *cancellable,
+					  GError **error)
+{
+	struct GetMessageJobData *job_data;
+	CamelIMAPXMailbox *mailbox;
+	GError *local_error = NULL;
+	gboolean success;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
+
+	mailbox = camel_imapx_job_get_mailbox (job);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+
+	job_data = camel_imapx_job_get_user_data (job);
+	g_return_val_if_fail (job_data != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (job_data->summary), FALSE);
+	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (job_data->message_cache), FALSE);
+	g_return_val_if_fail (job_data->message_uid != NULL, FALSE);
+
+	success = camel_imapx_server_sync_message_sync (
+		server, mailbox, job_data->summary, job_data->message_cache, job_data->message_uid,
+		cancellable, &local_error);
+
+	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
+
+	if (local_error)
+		g_propagate_error (error, local_error);
+
+	return success;
+}
+
+gboolean
+camel_imapx_conn_manager_sync_message_sync (CamelIMAPXConnManager *conn_man,
+					    CamelIMAPXMailbox *mailbox,
+					    CamelFolderSummary *summary,
+					    CamelDataCache *message_cache,
+					    const gchar *message_uid,
+					    GCancellable *cancellable,
+					    GError **error)
 {
-	CamelStore *store;
-	CamelIMAPXServer *is = NULL;
-	CamelIMAPXStore *imapx_store;
-	ConnectionInfo *cinfo = NULL;
+	CamelIMAPXJob *job;
+	struct GetMessageJobData *job_data;
 	gboolean success;
 
-	/* Caller must be holding CON_WRITE_LOCK. */
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
 
-	/* Check if we got cancelled while we were waiting. */
-	if (g_cancellable_set_error_if_cancelled (cancellable, error))
-		return NULL;
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_SYNC_MESSAGE, mailbox,
+		imapx_conn_manager_sync_message_run_sync,
+		imapx_conn_manager_get_message_matches,
+		NULL);
 
-	store = camel_imapx_conn_manager_ref_store (con_man);
-	g_return_val_if_fail (store != NULL, NULL);
+	job_data = g_new0 (struct GetMessageJobData, 1);
+	job_data->summary = g_object_ref (summary);
+	job_data->message_cache = g_object_ref (message_cache);
+	job_data->message_uid = g_strdup (message_uid);
 
-	imapx_store = CAMEL_IMAPX_STORE (store);
+	camel_imapx_job_set_user_data (job, job_data, get_message_job_data_free);
 
-	is = camel_imapx_server_new (imapx_store);
-	is->tagprefix = imapx_conn_manager_get_next_free_tagprefix_unlocked (con_man);
+	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
 
-	/* XXX As part of the connect operation the CamelIMAPXServer will
-	 *     have to call camel_session_authenticate_sync(), but it has
-	 *     no way to pass itself through in that call so the service
-	 *     knows which CamelIMAPXServer is trying to authenticate.
-	 *
-	 *     IMAPX is the only provider that does multiple connections
-	 *     like this, so I didn't want to pollute the CamelSession and
-	 *     CamelService authentication APIs with an extra argument.
-	 *     Instead we do this little hack so the service knows which
-	 *     CamelIMAPXServer to act on in its authenticate_sync() method.
-	 *
-	 *     Because we're holding the CAMEL_SERVICE_REC_CONNECT_LOCK
-	 *     we should not have multiple IMAPX connections trying to
-	 *     authenticate at once, so this should be thread-safe.
-	 */
-	camel_imapx_store_set_connecting_server (imapx_store, is, con_man->priv->connections != NULL);
-	success = camel_imapx_server_connect (is, cancellable, error);
-	camel_imapx_store_set_connecting_server (imapx_store, NULL, FALSE);
+	camel_imapx_job_unref (job);
 
-	if (!success) {
-		g_clear_object (&is);
-		goto exit;
-	}
+	return success;
+}
 
-	g_signal_connect (
-		is, "shutdown",
-		G_CALLBACK (imapx_conn_shutdown), con_man);
-
-	g_signal_connect (
-		is, "mailbox-select",
-		G_CALLBACK (imapx_conn_update_select), con_man);
-	g_signal_connect (
-		is, "mailbox-closed",
-		G_CALLBACK (imapx_conn_mailbox_closed), con_man);
+static gboolean
+imapx_conn_manager_create_mailbox_run_sync (CamelIMAPXJob *job,
+					    CamelIMAPXServer *server,
+					    GCancellable *cancellable,
+					    GError **error)
+{
+	const gchar *mailbox_name;
+	GError *local_error = NULL;
+	gboolean success;
 
-	cinfo = connection_info_new (is);
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
 
-	if (folder_name != NULL)
-		connection_info_insert_folder_name (cinfo, folder_name);
+	mailbox_name = camel_imapx_job_get_user_data (job);
+	g_return_val_if_fail (mailbox_name != NULL, FALSE);
 
-	/* Takes ownership of the ConnectionInfo. */
-	con_man->priv->connections = g_list_prepend (
-		con_man->priv->connections, cinfo);
+	success = camel_imapx_server_create_mailbox_sync (server, mailbox_name, cancellable, &local_error);
 
-	c (is->tagprefix, "Created new connection %p (server:%p) for %s; total connections %d\n", cinfo, cinfo->is, folder_name, g_list_length (con_man->priv->connections));
+	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
 
-exit:
-	g_object_unref (store);
+	if (local_error)
+		g_propagate_error (error, local_error);
 
-	return is;
+	return success;
 }
 
-/****************************/
+gboolean
+camel_imapx_conn_manager_create_mailbox_sync (CamelIMAPXConnManager *conn_man,
+					      const gchar *mailbox_name,
+					      GCancellable *cancellable,
+					      GError **error)
+{
+	CamelIMAPXJob *job;
+	gboolean success;
 
-CamelIMAPXConnManager *
-camel_imapx_conn_manager_new (CamelStore *store)
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_CREATE_MAILBOX, NULL,
+		imapx_conn_manager_create_mailbox_run_sync,
+		imapx_conn_manager_nothing_matches,
+		NULL);
+
+	camel_imapx_job_set_user_data (job, g_strdup (mailbox_name), g_free);
+
+	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
+
+	camel_imapx_job_unref (job);
+
+	return success;
+}
+
+static gboolean
+imapx_conn_manager_delete_mailbox_run_sync (CamelIMAPXJob *job,
+					    CamelIMAPXServer *server,
+					    GCancellable *cancellable,
+					    GError **error)
 {
-	g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
+	CamelIMAPXMailbox *mailbox;
+	GError *local_error = NULL;
+	gboolean success;
 
-	return g_object_new (
-		CAMEL_TYPE_IMAPX_CONN_MANAGER, "store", store, NULL);
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
+
+	mailbox = camel_imapx_job_get_mailbox (job);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+
+	success = camel_imapx_server_delete_mailbox_sync (server, mailbox, cancellable, &local_error);
+
+	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
+
+	if (local_error)
+		g_propagate_error (error, local_error);
+
+	return success;
+}
+
+gboolean
+camel_imapx_conn_manager_delete_mailbox_sync (CamelIMAPXConnManager *conn_man,
+					      CamelIMAPXMailbox *mailbox,
+					      GCancellable *cancellable,
+					      GError **error)
+{
+	CamelIMAPXJob *job;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_DELETE_MAILBOX, mailbox,
+		imapx_conn_manager_delete_mailbox_run_sync,
+		imapx_conn_manager_nothing_matches,
+		NULL);
+
+	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
+
+	camel_imapx_job_unref (job);
+
+	return success;
+}
+
+static gboolean
+imapx_conn_manager_rename_mailbox_run_sync (CamelIMAPXJob *job,
+					    CamelIMAPXServer *server,
+					    GCancellable *cancellable,
+					    GError **error)
+{
+	CamelIMAPXMailbox *mailbox;
+	const gchar *new_mailbox_name;
+	GError *local_error = NULL;
+	gboolean success;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
+
+	mailbox = camel_imapx_job_get_mailbox (job);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+
+	new_mailbox_name = camel_imapx_job_get_user_data (job);
+	g_return_val_if_fail (new_mailbox_name != NULL, FALSE);
+
+	success = camel_imapx_server_rename_mailbox_sync (server, mailbox, new_mailbox_name, cancellable, &local_error);
+
+	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
+
+	if (local_error)
+		g_propagate_error (error, local_error);
+
+	return success;
 }
 
-CamelStore *
-camel_imapx_conn_manager_ref_store (CamelIMAPXConnManager *con_man)
+gboolean
+camel_imapx_conn_manager_rename_mailbox_sync (CamelIMAPXConnManager *conn_man,
+					      CamelIMAPXMailbox *mailbox,
+					      const gchar *new_mailbox_name,
+					      GCancellable *cancellable,
+					      GError **error)
 {
-	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL);
+	CamelIMAPXJob *job;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_RENAME_MAILBOX, mailbox,
+		imapx_conn_manager_rename_mailbox_run_sync,
+		imapx_conn_manager_nothing_matches,
+		NULL);
+
+	camel_imapx_job_set_user_data (job, g_strdup (new_mailbox_name), g_free);
 
-	return g_weak_ref_get (&con_man->priv->store);
+	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
+
+	camel_imapx_job_unref (job);
+
+	return success;
 }
 
-CamelIMAPXServer *
-camel_imapx_conn_manager_get_connection (CamelIMAPXConnManager *con_man,
-                                         const gchar *folder_name,
-					 gboolean for_expensive_job,
-                                         GCancellable *cancellable,
-                                         GError **error)
+static gboolean
+imapx_conn_manager_subscribe_mailbox_run_sync (CamelIMAPXJob *job,
+					       CamelIMAPXServer *server,
+					       GCancellable *cancellable,
+					       GError **error)
 {
-	CamelIMAPXServer *is = NULL;
+	CamelIMAPXMailbox *mailbox;
+	GError *local_error = NULL;
+	gboolean success;
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL);
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
 
-	g_mutex_lock (&con_man->priv->pending_connections_lock);
-	if (cancellable) {
-		g_object_ref (cancellable);
-	} else {
-		cancellable = g_cancellable_new ();
-	}
-	con_man->priv->pending_connections = g_slist_prepend (con_man->priv->pending_connections, cancellable);
-	g_mutex_unlock (&con_man->priv->pending_connections_lock);
+	mailbox = camel_imapx_job_get_mailbox (job);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
 
-	/* Hold the writer lock while we requisition a CamelIMAPXServer
-	 * to prevent other threads from adding or removing connections. */
-	CON_WRITE_LOCK (con_man);
-
-	/* Check if we've got cancelled while waiting for the lock. */
-	if (!g_cancellable_set_error_if_cancelled (cancellable, error)) {
-		is = imapx_find_connection_unlocked (con_man, folder_name, for_expensive_job);
-		if (is == NULL) {
-			GError *local_error = NULL;
-
-			is = imapx_create_new_connection_unlocked (con_man, folder_name, cancellable, &local_error);
-
-			if (!is) {
-				gboolean limit_connections =
-					g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR,
-					CAMEL_IMAPX_SERVER_ERROR_CONCURRENT_CONNECT_FAILED) &&
-					con_man->priv->connections;
-
-				c ('*', "Failed to open a new connection, while having %d opened, with error: %s; will limit connections: %s\n",
-					g_list_length (con_man->priv->connections),
-					local_error ? local_error->message : "Unknown error",
-					limit_connections ? "yes" : "no");
-
-				if (limit_connections) {
-					/* limit to one-less than current connection count - be nice to the server */
-					con_man->priv->limit_max_connections = g_list_length (con_man->priv->connections) - 1;
-					if (!con_man->priv->limit_max_connections)
-						con_man->priv->limit_max_connections = 1;
-
-					g_clear_error (&local_error);
-					is = imapx_find_connection_unlocked (con_man, folder_name, for_expensive_job);
-				} else if (local_error) {
-					g_propagate_error (error, local_error);
-				}
-			}
-		}
-	}
+	success = camel_imapx_server_subscribe_mailbox_sync (server, mailbox, cancellable, &local_error);
 
-	CON_WRITE_UNLOCK (con_man);
+	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
 
-	g_mutex_lock (&con_man->priv->pending_connections_lock);
-	con_man->priv->pending_connections = g_slist_remove (con_man->priv->pending_connections, cancellable);
-	g_object_unref (cancellable);
-	g_mutex_unlock (&con_man->priv->pending_connections_lock);
+	if (local_error)
+		g_propagate_error (error, local_error);
 
-	return is;
+	return success;
 }
 
-GList *
-camel_imapx_conn_manager_get_connections (CamelIMAPXConnManager *con_man)
+gboolean
+camel_imapx_conn_manager_subscribe_mailbox_sync (CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 GCancellable *cancellable,
+						 GError **error)
 {
-	GList *list, *link;
+	CamelIMAPXJob *job;
+	gboolean success;
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
 
-	list = imapx_conn_manager_list_info (con_man);
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_SUBSCRIBE_MAILBOX, mailbox,
+		imapx_conn_manager_subscribe_mailbox_run_sync, NULL, NULL);
 
-	/* Swap ConnectionInfo for CamelIMAPXServer in each link. */
-	for (link = list; link != NULL; link = g_list_next (link)) {
-		ConnectionInfo *cinfo = link->data;
-		link->data = g_object_ref (cinfo->is);
-		connection_info_unref (cinfo);
+	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
+
+	camel_imapx_job_unref (job);
+
+	return success;
+}
+
+static gboolean
+imapx_conn_manager_unsubscribe_mailbox_run_sync (CamelIMAPXJob *job,
+						 CamelIMAPXServer *server,
+						 GCancellable *cancellable,
+						 GError **error)
+{
+	CamelIMAPXMailbox *mailbox;
+	GError *local_error = NULL;
+	gboolean success;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
+
+	mailbox = camel_imapx_job_get_mailbox (job);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+
+	success = camel_imapx_server_unsubscribe_mailbox_sync (server, mailbox, cancellable, &local_error);
+
+	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
+
+	if (local_error)
+		g_propagate_error (error, local_error);
+
+	return success;
+}
+
+gboolean
+camel_imapx_conn_manager_unsubscribe_mailbox_sync (CamelIMAPXConnManager *conn_man,
+						   CamelIMAPXMailbox *mailbox,
+						   GCancellable *cancellable,
+						   GError **error)
+{
+	CamelIMAPXJob *job;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_UNSUBSCRIBE_MAILBOX, mailbox,
+		imapx_conn_manager_unsubscribe_mailbox_run_sync, NULL, NULL);
+
+	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
+
+	camel_imapx_job_unref (job);
+
+	return success;
+}
+
+static gboolean
+imapx_conn_manager_update_quota_info_run_sync (CamelIMAPXJob *job,
+					       CamelIMAPXServer *server,
+					       GCancellable *cancellable,
+					       GError **error)
+{
+	CamelIMAPXMailbox *mailbox;
+	GError *local_error = NULL;
+	gboolean success;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
+
+	mailbox = camel_imapx_job_get_mailbox (job);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+
+	success = camel_imapx_server_update_quota_info_sync (server, mailbox, cancellable, &local_error);
+
+	camel_imapx_job_set_result (job, success, NULL, local_error, NULL);
+
+	if (local_error)
+		g_propagate_error (error, local_error);
+
+	return success;
+}
+
+gboolean
+camel_imapx_conn_manager_update_quota_info_sync (CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 GCancellable *cancellable,
+						 GError **error)
+{
+	CamelIMAPXJob *job;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE);
+
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_UPDATE_QUOTA_INFO, mailbox,
+		imapx_conn_manager_update_quota_info_run_sync, NULL, NULL);
+
+	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
+
+	camel_imapx_job_unref (job);
+
+	return success;
+}
+
+static gchar **
+imapx_copy_strv (const gchar * const *words)
+{
+	gchar **copy;
+	gint ii;
+
+	if (!words || !*words)
+		return NULL;
+
+	copy = g_new0 (gchar *, g_strv_length ((gchar **) words) + 1);
+
+	for (ii = 0; words[ii]; ii++) {
+		copy[ii] = g_strdup (words[ii]);
 	}
 
-	return list;
+	copy[ii] = NULL;
+
+	return copy;
 }
 
-/* Used for handling operations that fails to execute and that needs to removed from folder list */
-void
-camel_imapx_conn_manager_update_con_info (CamelIMAPXConnManager *con_man,
-                                          CamelIMAPXServer *is,
-                                          const gchar *folder_name)
+static gboolean
+imapx_equal_strv (const gchar * const *words1,
+		  const gchar * const *words2)
 {
-	ConnectionInfo *cinfo;
+	gint ii;
 
-	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man));
+	if (words1 == words2)
+		return TRUE;
 
-	/* Returns a new ConnectionInfo reference. */
-	cinfo = imapx_conn_manager_lookup_info (con_man, is);
+	if (!words1 || !words2)
+		return FALSE;
 
-	if (cinfo == NULL)
-		return;
+	for (ii = 0; words1[ii] && words2[ii]; ii++) {
+		if (g_strcmp0 (words1[ii], words2[ii]) != 0)
+			return FALSE;
+	}
+
+	return !words1[ii] && !words2[ii];
+}
+
+struct UidSearchJobData {
+	gchar *criteria_prefix;
+	gchar *search_key;
+	gchar **words;
+};
+
+static void
+uid_search_job_data_free (gpointer ptr)
+{
+	struct UidSearchJobData *job_data = ptr;
 
-	if (camel_imapx_server_folder_name_in_jobs (is, folder_name)) {
-		connection_info_remove_folder_name (cinfo, folder_name);
-		c (is->tagprefix, "Removed folder %s from connection folder list - op done \n", folder_name);
+	if (ptr) {
+		g_free (job_data->criteria_prefix);
+		g_free (job_data->search_key);
+		g_strfreev (job_data->words);
+		g_free (job_data);
 	}
+}
+
+static gboolean
+imapx_conn_manager_uid_search_run_sync (CamelIMAPXJob *job,
+					CamelIMAPXServer *server,
+					GCancellable *cancellable,
+					GError **error)
+{
+	struct UidSearchJobData *job_data;
+	CamelIMAPXMailbox *mailbox;
+	GPtrArray *uids = NULL;
+	GError *local_error = NULL;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
+
+	mailbox = camel_imapx_job_get_mailbox (job);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+
+	job_data = camel_imapx_job_get_user_data (job);
+	g_return_val_if_fail (job_data != NULL, FALSE);
+
+	uids = camel_imapx_server_uid_search_sync (server, mailbox, job_data->criteria_prefix,
+		job_data->search_key, (const gchar * const *) job_data->words, cancellable, &local_error);
+
+	camel_imapx_job_set_result (job, uids != NULL, uids, local_error, uids ? (GDestroyNotify) g_ptr_array_free : NULL);
+
+	if (local_error)
+		g_propagate_error (error, local_error);
 
-	connection_info_unref (cinfo);
+	return uids != NULL;
 }
 
-void
-camel_imapx_conn_manager_close_connections (CamelIMAPXConnManager *con_man,
-					    const GError *error)
+static gboolean
+imapx_conn_manager_uid_search_matches (CamelIMAPXJob *job,
+				       CamelIMAPXJob *other_job)
 {
-	GList *iter, *connections;
+	struct UidSearchJobData *job_data, *other_job_data;
 
-	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man));
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (other_job != NULL, FALSE);
 
-	/* Do this before acquiring the write lock, because any pending
-	   connection holds the write lock, thus makes this request starve. */
-	imax_conn_manager_cancel_pending_connections (con_man);
+	if (camel_imapx_job_get_kind (job) != CAMEL_IMAPX_JOB_UID_SEARCH ||
+	    camel_imapx_job_get_kind (job) != camel_imapx_job_get_kind (other_job))
+		return FALSE;
+
+	job_data = camel_imapx_job_get_user_data (job);
+	other_job_data = camel_imapx_job_get_user_data (other_job);
+
+	if (!job_data || !other_job_data)
+		return job_data == other_job_data;
+
+	return g_strcmp0 (job_data->criteria_prefix, other_job_data->criteria_prefix) == 0 &&
+	       g_strcmp0 (job_data->search_key, other_job_data->search_key) == 0 &&
+	       imapx_equal_strv ((const gchar * const  *) job_data->words, (const gchar * const  *) other_job_data->words);
+}
+
+GPtrArray *
+camel_imapx_conn_manager_uid_search_sync (CamelIMAPXConnManager *conn_man,
+					  CamelIMAPXMailbox *mailbox,
+					  const gchar *criteria_prefix,
+					  const gchar *search_key,
+					  const gchar * const *words,
+					  GCancellable *cancellable,
+					  GError **error)
+{
+	struct UidSearchJobData *job_data;
+	GPtrArray *uids = NULL;
+	CamelIMAPXJob *job;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), NULL);
 
-	CON_WRITE_LOCK (con_man);
+	job_data = g_new0 (struct UidSearchJobData, 1);
+	job_data->criteria_prefix = g_strdup (criteria_prefix);
+	job_data->search_key = g_strdup (search_key);
+	job_data->words = imapx_copy_strv (words);
 
-	c('*', "Closing all %d connections, with propagated error: %s\n", g_list_length (con_man->priv->connections), error ? error->message : "none");
+	job = camel_imapx_job_new (CAMEL_IMAPX_JOB_UID_SEARCH, mailbox,
+		imapx_conn_manager_uid_search_run_sync,
+		imapx_conn_manager_uid_search_matches,
+		NULL);
 
-	connections = con_man->priv->connections;
-	con_man->priv->connections = NULL;
+	camel_imapx_job_set_user_data (job, job_data, uid_search_job_data_free);
 
-	CON_WRITE_UNLOCK (con_man);
+	success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error);
+	if (success) {
+		gpointer result_data = NULL;
 
-	for (iter = connections; iter; iter = g_list_next (iter)) {
-		connection_info_set_shutdown_error (iter->data, error);
+		success = camel_imapx_job_take_result_data (job, &result_data);
+		if (success)
+			uids = result_data;
 	}
 
-	g_list_free_full (connections, (GDestroyNotify) connection_info_cancel_and_unref);
+	camel_imapx_job_unref (job);
+
+	return uids;
 }
 
 /* for debugging purposes only */
 void
-camel_imapx_conn_manager_dump_queue_status (CamelIMAPXConnManager *con_man)
+camel_imapx_conn_manager_dump_queue_status (CamelIMAPXConnManager *conn_man)
 {
-	GList *list, *link;
+	GList *llink;
+	GSList *slink;
 
-	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man));
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man));
 
-	list = imapx_conn_manager_list_info (con_man);
+	CON_READ_LOCK (conn_man);
 
-	for (link = list; link != NULL; link = g_list_next (link)) {
-		ConnectionInfo *cinfo = link->data;
-		camel_imapx_server_dump_queue_status (cinfo->is);
-		connection_info_unref (cinfo);
+	printf ("%s: opened connections:%d\n", G_STRFUNC, g_list_length (conn_man->priv->connections));
+
+	for (llink = conn_man->priv->connections; llink != NULL; llink = g_list_next (llink)) {
+		ConnectionInfo *cinfo = llink->data;
+		CamelIMAPXCommand *cmd = NULL;
+
+		if (cinfo)
+			cmd = cinfo->is ? camel_imapx_server_ref_current_command (cinfo->is) : NULL;
+
+		printf ("   connection:%p server:[%c] %p busy:%d command:%s\n", cinfo,
+			cinfo && cinfo->is ? camel_imapx_server_get_tagprefix (cinfo->is) : '?',
+			cinfo ? cinfo->is : NULL, cinfo ? cinfo->busy : FALSE,
+			cmd ? camel_imapx_job_get_kind_name (cmd->job_kind) : "[null]");
+
+		if (cmd)
+			camel_imapx_command_unref (cmd);
+	}
+
+	CON_READ_UNLOCK (conn_man);
+
+	JOB_QUEUE_LOCK (conn_man);
+
+	printf ("Queued jobs:%d\n", g_slist_length (conn_man->priv->job_queue));
+	for (slink = conn_man->priv->job_queue; slink; slink = g_slist_next (slink)) {
+		CamelIMAPXJob *job = slink->data;
+
+		printf ("   job:%p kind:%s mailbox:%s\n", job,
+			job ? camel_imapx_job_get_kind_name (camel_imapx_job_get_kind (job)) : "[null]",
+			job && camel_imapx_job_get_mailbox (job) ? camel_imapx_mailbox_get_name (camel_imapx_job_get_mailbox (job)) : "[null]");
 	}
 
-	g_list_free (list);
+	JOB_QUEUE_UNLOCK (conn_man);
 }
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.h.imapx-update-to-upstream	2014-11-07 08:34:49.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.h	2016-08-15 13:52:41.949976330 +0200
@@ -21,6 +21,8 @@
 #ifndef _CAMEL_IMAPX_CONN_MANAGER_H
 #define _CAMEL_IMAPX_CONN_MANAGER_H
 
+#include "camel-imapx-job.h"
+#include "camel-imapx-mailbox.h"
 #include "camel-imapx-server.h"
 
 G_BEGIN_DECLS
@@ -44,6 +46,8 @@ G_BEGIN_DECLS
 	(G_TYPE_INSTANCE_GET_CLASS \
 	((obj), CAMEL_TYPE_IMAPX_CONN_MANAGER, CamelIMAPXConnManagerClass))
 
+struct _CamelIMAPXStore;
+
 typedef struct _CamelIMAPXConnManager CamelIMAPXConnManager;
 typedef struct _CamelIMAPXConnManagerClass CamelIMAPXConnManagerClass;
 typedef struct _CamelIMAPXConnManagerPrivate CamelIMAPXConnManagerPrivate;
@@ -56,33 +60,131 @@ struct _CamelIMAPXConnManager {
 
 struct _CamelIMAPXConnManagerClass {
 	GObjectClass parent_class;
+
+	/* Signals */
+	void	(* connection_created) (CamelIMAPXConnManager *conn_man,
+					CamelIMAPXServer *server);
 };
 
 GType		camel_imapx_conn_manager_get_type (void);
 CamelIMAPXConnManager *
 		camel_imapx_conn_manager_new	(CamelStore *store);
-CamelStore *	camel_imapx_conn_manager_ref_store
-						(CamelIMAPXConnManager *con_man);
-CamelIMAPXServer *
-		camel_imapx_conn_manager_get_connection
-						(CamelIMAPXConnManager *con_man,
-						 const gchar *folder_name,
-						 gboolean for_expensive_job,
-						 GCancellable *cancellable,
-						 GError **error);
-void		camel_imapx_conn_manager_close_connections
-						(CamelIMAPXConnManager *con_man,
-						 const GError *error);
-GList *		camel_imapx_conn_manager_get_connections
-						(CamelIMAPXConnManager *con_man);
-void		camel_imapx_conn_manager_update_con_info
-						(CamelIMAPXConnManager *con_man,
-						 CamelIMAPXServer *server,
-						 const gchar *folder_name);
+struct _CamelIMAPXStore *
+		camel_imapx_conn_manager_ref_store
+						(CamelIMAPXConnManager *conn_man);
+gboolean	camel_imapx_conn_manager_connect_sync
+						(CamelIMAPXConnManager *conn_man,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_disconnect_sync
+						(CamelIMAPXConnManager *conn_man,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_run_job_sync
+						(CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXJob *job,
+						 CamelIMAPXJobMatchesFunc finish_before_job,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_list_sync
+						(CamelIMAPXConnManager *conn_man,
+						 const gchar *pattern,
+						 CamelStoreGetFolderInfoFlags flags,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_refresh_info_sync
+						(CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_sync_changes_sync
+						(CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_expunge_sync
+						(CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 GCancellable *cancellable,
+						 GError **error);
+CamelStream *	camel_imapx_conn_manager_get_message_sync
+						(CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 CamelFolderSummary *summary,
+						 CamelDataCache *message_cache,
+						 const gchar *message_uid,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_copy_message_sync
+						(CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 CamelIMAPXMailbox *destination,
+						 GPtrArray *uids,
+						 gboolean delete_originals,
+						 gboolean remove_deleted_flags,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_append_message_sync
+						(CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 CamelFolderSummary *summary,
+						 CamelDataCache *message_cache,
+						 CamelMimeMessage *message,
+						 const CamelMessageInfo *mi,
+						 gchar **append_uid,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_sync_message_sync
+						(CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 CamelFolderSummary *summary,
+						 CamelDataCache *message_cache,
+						 const gchar *message_uid,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_create_mailbox_sync
+						(CamelIMAPXConnManager *conn_man,
+						 const gchar *mailbox_name,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_delete_mailbox_sync
+						(CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_rename_mailbox_sync
+						(CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 const gchar *new_mailbox_name,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_subscribe_mailbox_sync
+						(CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_unsubscribe_mailbox_sync
+						(CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_conn_manager_update_quota_info_sync
+						(CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 GCancellable *cancellable,
+						 GError **error);
+GPtrArray *	camel_imapx_conn_manager_uid_search_sync
+						(CamelIMAPXConnManager *conn_man,
+						 CamelIMAPXMailbox *mailbox,
+						 const gchar *criteria_prefix,
+						 const gchar *search_key,
+						 const gchar * const *words,
+						 GCancellable *cancellable,
+						 GError **error);
 
 /* for debugging purposes only */
 void		camel_imapx_conn_manager_dump_queue_status
-						(CamelIMAPXConnManager *con_man);
+						(CamelIMAPXConnManager *conn_man);
 G_END_DECLS
 
 #endif /* _CAMEL_IMAPX_SERVER_H */
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.c.imapx-update-to-upstream	2014-09-02 17:58:14.000000000 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.c	2016-08-15 13:52:41.950976330 +0200
@@ -1,21 +1,21 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* camel-imap-folder.c : class for a imap folder */
-/*
- * Authors: Michael Zucchi <notzed@ximian.com>
+/* camel-imap-folder.c : class for a imap folder
  *
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
- * This library is free software; you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Michael Zucchi <notzed@ximian.com>
  */
 
 #ifdef HAVE_CONFIG_H
@@ -42,6 +42,8 @@
 	(G_TYPE_INSTANCE_GET_PRIVATE \
 	((obj), CAMEL_TYPE_IMAPX_FOLDER, CamelIMAPXFolderPrivate))
 
+extern gint camel_application_is_exiting;
+
 struct _CamelIMAPXFolderPrivate {
 	GMutex property_lock;
 	GWeakRef mailbox;
@@ -49,6 +51,8 @@ struct _CamelIMAPXFolderPrivate {
 	GMutex move_to_hash_table_lock;
 	GHashTable *move_to_real_junk_uids;
 	GHashTable *move_to_real_trash_uids;
+
+	gboolean check_folder;
 };
 
 /* The custom property ID is a CamelArg artifact.
@@ -56,16 +60,17 @@ struct _CamelIMAPXFolderPrivate {
 enum {
 	PROP_0,
 	PROP_MAILBOX,
-	PROP_APPLY_FILTERS = 0x2501
+	PROP_APPLY_FILTERS = 0x2501,
+	PROP_CHECK_FOLDER = 0x2502
 };
 
 G_DEFINE_TYPE (CamelIMAPXFolder, camel_imapx_folder, CAMEL_TYPE_OFFLINE_FOLDER)
 
 static gboolean imapx_folder_get_apply_filters (CamelIMAPXFolder *folder);
 
-static void
-imapx_folder_claim_move_to_real_junk_uids (CamelIMAPXFolder *folder,
-                                           GPtrArray *out_uids_to_copy)
+void
+camel_imapx_folder_claim_move_to_real_junk_uids (CamelIMAPXFolder *folder,
+						 GPtrArray *out_uids_to_copy)
 {
 	GList *keys;
 
@@ -82,9 +87,9 @@ imapx_folder_claim_move_to_real_junk_uid
 	}
 }
 
-static void
-imapx_folder_claim_move_to_real_trash_uids (CamelIMAPXFolder *folder,
-                                            GPtrArray *out_uids_to_copy)
+void
+camel_imapx_folder_claim_move_to_real_trash_uids (CamelIMAPXFolder *folder,
+						  GPtrArray *out_uids_to_copy)
 {
 	GList *keys;
 
@@ -138,6 +143,12 @@ imapx_folder_set_property (GObject *obje
 				g_value_get_boolean (value));
 			return;
 
+		case PROP_CHECK_FOLDER:
+			camel_imapx_folder_set_check_folder (
+				CAMEL_IMAPX_FOLDER (object),
+				g_value_get_boolean (value));
+			return;
+
 		case PROP_MAILBOX:
 			camel_imapx_folder_set_mailbox (
 				CAMEL_IMAPX_FOLDER (object),
@@ -162,6 +173,13 @@ imapx_folder_get_property (GObject *obje
 				CAMEL_IMAPX_FOLDER (object)));
 			return;
 
+		case PROP_CHECK_FOLDER:
+			g_value_set_boolean (
+				value,
+				camel_imapx_folder_get_check_folder (
+				CAMEL_IMAPX_FOLDER (object)));
+			return;
+
 		case PROP_MAILBOX:
 			g_value_take_object (
 				value,
@@ -409,60 +427,31 @@ imapx_append_message_sync (CamelFolder *
 {
 	CamelStore *store;
 	CamelIMAPXStore *imapx_store;
-	CamelIMAPXServer *imapx_server;
+	CamelIMAPXConnManager *conn_man;
 	CamelIMAPXMailbox *mailbox = NULL;
-	const gchar *folder_name;
-	GError *local_error = NULL;
 	gboolean success = FALSE;
 
 	if (appended_uid != NULL)
 		*appended_uid = NULL;
 
 	store = camel_folder_get_parent_store (folder);
-	folder_name = camel_folder_get_full_name (folder);
 
 	imapx_store = CAMEL_IMAPX_STORE (store);
-	imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error);
-
-	if (imapx_server == NULL)
-		goto exit;
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
 
 	mailbox = camel_imapx_folder_list_mailbox (
 		CAMEL_IMAPX_FOLDER (folder), cancellable, error);
 
-	if (mailbox == NULL) {
-		camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
+	if (mailbox == NULL)
 		goto exit;
-	}
 
-	success = camel_imapx_server_append_message (
-		imapx_server, mailbox, folder->summary,
+	success = camel_imapx_conn_manager_append_message_sync (
+		conn_man, mailbox, folder->summary,
 		CAMEL_IMAPX_FOLDER (folder)->cache, message,
-		info, appended_uid, cancellable, &local_error);
-
-	camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		g_clear_error (&local_error);
-		g_clear_object (&imapx_server);
-
-		imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, &local_error);
-		if (imapx_server) {
-			success = camel_imapx_server_append_message (
-				imapx_server, mailbox, folder->summary,
-				CAMEL_IMAPX_FOLDER (folder)->cache, message,
-				info, appended_uid, cancellable, &local_error);
-
-			camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-		}
-	}
-
-	if (local_error)
-		g_propagate_error (error, local_error);
+		info, appended_uid, cancellable, error);
 
 exit:
 	g_clear_object (&mailbox);
-	g_clear_object (&imapx_server);
 
 	return success;
 }
@@ -474,28 +463,21 @@ imapx_expunge_sync (CamelFolder *folder,
 {
 	CamelStore *store;
 	CamelIMAPXStore *imapx_store;
-	CamelIMAPXServer *imapx_server;
+	CamelIMAPXConnManager *conn_man;
 	CamelIMAPXMailbox *mailbox = NULL;
 	GError *local_error = NULL;
-	const gchar *folder_name;
 	gboolean success = FALSE;
 
 	store = camel_folder_get_parent_store (folder);
-	folder_name = camel_folder_get_full_name (folder);
 
 	imapx_store = CAMEL_IMAPX_STORE (store);
-	imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error);
-
-	if (imapx_server == NULL)
-		goto exit;
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
 
 	mailbox = camel_imapx_folder_list_mailbox (
 		CAMEL_IMAPX_FOLDER (folder), cancellable, error);
 
-	if (mailbox == NULL) {
-		camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
+	if (mailbox == NULL)
 		goto exit;
-	}
 
 	if ((store->flags & CAMEL_STORE_VTRASH) == 0) {
 		CamelFolder *trash;
@@ -533,28 +515,10 @@ imapx_expunge_sync (CamelFolder *folder,
 		g_clear_error (&local_error);
 	}
 
-	success = camel_imapx_server_expunge (imapx_server, mailbox, cancellable, &local_error);
-
-	camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		g_clear_error (&local_error);
-		g_clear_object (&imapx_server);
-
-		imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, &local_error);
-		if (imapx_server) {
-			success = camel_imapx_server_expunge (imapx_server, mailbox, cancellable, &local_error);
-
-			camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-		}
-	}
-
-	if (local_error)
-		g_propagate_error (error, local_error);
+	success = camel_imapx_conn_manager_expunge_sync (conn_man, mailbox, cancellable, error);
 
 exit:
 	g_clear_object (&mailbox);
-	g_clear_object (&imapx_server);
 
 	return success;
 }
@@ -612,7 +576,6 @@ imapx_get_message_sync (CamelFolder *fol
 	GIOStream *base_stream;
 	const gchar *path = NULL;
 	gboolean offline_message = FALSE;
-	GError *local_error = NULL;
 
 	imapx_folder = CAMEL_IMAPX_FOLDER (folder);
 	store = camel_folder_get_parent_store (folder);
@@ -630,9 +593,8 @@ imapx_get_message_sync (CamelFolder *fol
 		stream = camel_stream_new (base_stream);
 		g_object_unref (base_stream);
 	} else {
-		CamelIMAPXServer *imapx_server;
+		CamelIMAPXConnManager *conn_man;
 		CamelIMAPXMailbox *mailbox;
-		const gchar *folder_name;
 
 		if (offline_message) {
 			g_set_error (
@@ -642,49 +604,20 @@ imapx_get_message_sync (CamelFolder *fol
 			return NULL;
 		}
 
-		folder_name = camel_folder_get_full_name (folder);
-		imapx_server = camel_imapx_store_ref_server (
-			CAMEL_IMAPX_STORE (store), folder_name, FALSE, cancellable, error);
-
-		if (imapx_server == NULL)
-			return NULL;
+		conn_man = camel_imapx_store_get_conn_manager (CAMEL_IMAPX_STORE (store));
 
 		mailbox = camel_imapx_folder_list_mailbox (
 			CAMEL_IMAPX_FOLDER (folder), cancellable, error);
 
-		if (mailbox == NULL) {
-			camel_imapx_store_folder_op_done (CAMEL_IMAPX_STORE (store), imapx_server, folder_name);
-			g_object_unref (imapx_server);
+		if (mailbox == NULL)
 			return NULL;
-		}
 
-		stream = camel_imapx_server_get_message (
-			imapx_server, mailbox, folder->summary,
+		stream = camel_imapx_conn_manager_get_message_sync (
+			conn_man, mailbox, folder->summary,
 			CAMEL_IMAPX_FOLDER (folder)->cache, uid,
-			cancellable, &local_error);
-
-		camel_imapx_store_folder_op_done (CAMEL_IMAPX_STORE (store), imapx_server, folder_name);
-
-		while (!stream && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-			g_clear_error (&local_error);
-			g_clear_object (&imapx_server);
-
-			imapx_server = camel_imapx_store_ref_server (CAMEL_IMAPX_STORE (store), folder_name, FALSE, cancellable, &local_error);
-			if (imapx_server) {
-				stream = camel_imapx_server_get_message (
-					imapx_server, mailbox, folder->summary,
-					CAMEL_IMAPX_FOLDER (folder)->cache, uid,
-					cancellable, &local_error);
-
-				camel_imapx_store_folder_op_done (CAMEL_IMAPX_STORE (store), imapx_server, folder_name);
-			}
-		}
-
-		if (local_error)
-			g_propagate_error (error, local_error);
+			cancellable, error);
 
 		g_clear_object (&mailbox);
-		g_clear_object (&imapx_server);
 	}
 
 	if (stream != NULL) {
@@ -734,48 +667,23 @@ imapx_get_quota_info_sync (CamelFolder *
 {
 	CamelStore *store;
 	CamelIMAPXStore *imapx_store;
-	CamelIMAPXServer *imapx_server;
+	CamelIMAPXConnManager *conn_man;
 	CamelIMAPXMailbox *mailbox = NULL;
 	CamelFolderQuotaInfo *quota_info = NULL;
-	const gchar *folder_name;
 	gchar **quota_roots;
 	gboolean success = FALSE;
-	GError *local_error = NULL;
 
 	store = camel_folder_get_parent_store (folder);
-	folder_name = camel_folder_get_full_name (folder);
 
 	imapx_store = CAMEL_IMAPX_STORE (store);
-	imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error);
-
-	if (imapx_server == NULL)
-		goto exit;
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
 
 	mailbox = camel_imapx_folder_list_mailbox (
 		CAMEL_IMAPX_FOLDER (folder), cancellable, error);
-	if (mailbox == NULL) {
-		camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
+	if (mailbox == NULL)
 		goto exit;
-	}
-
-	success = camel_imapx_server_update_quota_info (imapx_server, mailbox, cancellable, &local_error);
-
-	camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		g_clear_error (&local_error);
-		g_clear_object (&imapx_server);
-
-		imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, &local_error);
-		if (imapx_server) {
-			success = camel_imapx_server_update_quota_info (imapx_server, mailbox, cancellable, &local_error);
 
-			camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-		}
-	}
-
-	if (local_error)
-		g_propagate_error (error, local_error);
+	success = camel_imapx_conn_manager_update_quota_info_sync (conn_man, mailbox, cancellable, error);
 
 	if (!success)
 		goto exit;
@@ -798,7 +706,6 @@ imapx_get_quota_info_sync (CamelFolder *
 
 exit:
 	g_clear_object (&mailbox);
-	g_clear_object (&imapx_server);
 
 	return quota_info;
 }
@@ -821,12 +728,9 @@ imapx_refresh_info_sync (CamelFolder *fo
 {
 	CamelStore *store;
 	CamelIMAPXStore *imapx_store;
-	CamelIMAPXServer *imapx_server;
+	CamelIMAPXConnManager *conn_man;
 	CamelIMAPXMailbox *mailbox = NULL;
-	CamelFolderChangeInfo *changes;
-	gchar *folder_name;
 	gboolean success = FALSE;
-	GError *local_error = NULL;
 
 	store = camel_folder_get_parent_store (folder);
 
@@ -834,235 +738,14 @@ imapx_refresh_info_sync (CamelFolder *fo
 	if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
 		return TRUE;
 
-	folder_name = g_strdup (camel_folder_get_full_name (folder));
 	imapx_store = CAMEL_IMAPX_STORE (store);
-	imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, TRUE, cancellable, error);
-
-	if (imapx_server == NULL)
-		goto exit;
-
-	mailbox = camel_imapx_folder_list_mailbox (
-		CAMEL_IMAPX_FOLDER (folder), cancellable, error);
-
-	if (mailbox == NULL) {
-		camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-		goto exit;
-	}
-
-	changes = camel_imapx_server_refresh_info (imapx_server, mailbox, cancellable, &local_error);
-
-	camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		g_clear_error (&local_error);
-		g_clear_object (&imapx_server);
-
-		imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, TRUE, cancellable, &local_error);
-		if (imapx_server) {
-			changes = camel_imapx_server_refresh_info (imapx_server, mailbox, cancellable, &local_error);
-
-			camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-		}
-	}
-
-	if (local_error)
-		g_propagate_error (error, local_error);
-
-	if (changes != NULL) {
-		if (camel_folder_change_info_changed (changes))
-			camel_folder_changed (folder, changes);
-		camel_folder_change_info_free (changes);
-		success = TRUE;
-	}
-
-exit:
-	g_clear_object (&mailbox);
-	g_clear_object (&imapx_server);
-	g_free (folder_name);
-
-	return success;
-}
-
-/* Helper for imapx_synchronize_sync() */
-static gboolean
-imapx_move_to_real_junk (CamelIMAPXServer *imapx_server,
-                         CamelFolder *folder,
-                         GCancellable *cancellable,
-                         gboolean *out_need_to_expunge,
-                         GError **error)
-{
-	CamelIMAPXFolder *imapx_folder;
-	CamelIMAPXMailbox *mailbox;
-	CamelIMAPXSettings *settings;
-	GPtrArray *uids_to_copy;
-	gchar *real_junk_path = NULL;
-	gboolean success = TRUE;
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
 
-	*out_need_to_expunge = FALSE;
+	mailbox = camel_imapx_folder_list_mailbox (CAMEL_IMAPX_FOLDER (folder), cancellable, error);
 
-	/* Caller already obtained the mailbox from the folder,
-	 * so the folder should still have it readily available. */
-	imapx_folder = CAMEL_IMAPX_FOLDER (folder);
-	mailbox = camel_imapx_folder_ref_mailbox (imapx_folder);
-	g_return_val_if_fail (mailbox != NULL, FALSE);
-
-	uids_to_copy = g_ptr_array_new_with_free_func (
-		(GDestroyNotify) camel_pstring_free);
-
-	settings = camel_imapx_server_ref_settings (imapx_server);
-	if (camel_imapx_settings_get_use_real_junk_path (settings)) {
-		real_junk_path =
-			camel_imapx_settings_dup_real_junk_path (settings);
-		imapx_folder_claim_move_to_real_junk_uids (
-			imapx_folder, uids_to_copy);
+	if (mailbox) {
+		success = camel_imapx_conn_manager_refresh_info_sync (conn_man, mailbox, cancellable, error);
 	}
-	g_object_unref (settings);
-
-	if (uids_to_copy->len > 0) {
-		CamelIMAPXStore *imapx_store;
-		CamelIMAPXMailbox *destination = NULL;
-
-		imapx_store = camel_imapx_server_ref_store (imapx_server);
-
-		if (real_junk_path != NULL) {
-			folder = camel_store_get_folder_sync (
-				CAMEL_STORE (imapx_store),
-				real_junk_path, 0,
-				cancellable, error);
-		} else {
-			g_set_error (
-				error, CAMEL_FOLDER_ERROR,
-				CAMEL_FOLDER_ERROR_INVALID_PATH,
-				_("No destination folder specified"));
-			folder = NULL;
-		}
-
-		if (folder != NULL) {
-			destination = camel_imapx_folder_list_mailbox (
-				CAMEL_IMAPX_FOLDER (folder),
-				cancellable, error);
-			g_object_unref (folder);
-		}
-
-		/* Avoid duplicating messages in the Junk folder. */
-		if (destination == mailbox) {
-			success = TRUE;
-		} else if (destination != NULL) {
-			success = camel_imapx_server_copy_message (
-				imapx_server,
-				mailbox, destination,
-				uids_to_copy, TRUE,
-				cancellable, error);
-			*out_need_to_expunge = success;
-		} else {
-			success = FALSE;
-		}
-
-		if (!success) {
-			g_prefix_error (
-				error, "%s: ",
-				_("Unable to move junk messages"));
-		}
-
-		g_clear_object (&destination);
-		g_clear_object (&imapx_store);
-	}
-
-	g_ptr_array_unref (uids_to_copy);
-	g_free (real_junk_path);
-
-	g_clear_object (&mailbox);
-
-	return success;
-}
-
-/* Helper for imapx_synchronize_sync() */
-static gboolean
-imapx_move_to_real_trash (CamelIMAPXServer *imapx_server,
-                          CamelFolder *folder,
-                          GCancellable *cancellable,
-                          gboolean *out_need_to_expunge,
-                          GError **error)
-{
-	CamelIMAPXFolder *imapx_folder;
-	CamelIMAPXMailbox *mailbox;
-	CamelIMAPXSettings *settings;
-	GPtrArray *uids_to_copy;
-	gchar *real_trash_path = NULL;
-	gboolean success = TRUE;
-
-	*out_need_to_expunge = FALSE;
-
-	/* Caller already obtained the mailbox from the folder,
-	 * so the folder should still have it readily available. */
-	imapx_folder = CAMEL_IMAPX_FOLDER (folder);
-	mailbox = camel_imapx_folder_ref_mailbox (imapx_folder);
-	g_return_val_if_fail (mailbox != NULL, FALSE);
-
-	uids_to_copy = g_ptr_array_new_with_free_func (
-		(GDestroyNotify) camel_pstring_free);
-
-	settings = camel_imapx_server_ref_settings (imapx_server);
-	if (camel_imapx_settings_get_use_real_trash_path (settings)) {
-		real_trash_path =
-			camel_imapx_settings_dup_real_trash_path (settings);
-		imapx_folder_claim_move_to_real_trash_uids (
-			CAMEL_IMAPX_FOLDER (folder), uids_to_copy);
-	}
-	g_object_unref (settings);
-
-	if (uids_to_copy->len > 0) {
-		CamelIMAPXStore *imapx_store;
-		CamelIMAPXMailbox *destination = NULL;
-
-		imapx_store = camel_imapx_server_ref_store (imapx_server);
-
-		if (real_trash_path != NULL) {
-			folder = camel_store_get_folder_sync (
-				CAMEL_STORE (imapx_store),
-				real_trash_path, 0,
-				cancellable, error);
-		} else {
-			g_set_error (
-				error, CAMEL_FOLDER_ERROR,
-				CAMEL_FOLDER_ERROR_INVALID_PATH,
-				_("No destination folder specified"));
-			folder = NULL;
-		}
-
-		if (folder != NULL) {
-			destination = camel_imapx_folder_list_mailbox (
-				CAMEL_IMAPX_FOLDER (folder),
-				cancellable, error);
-			g_object_unref (folder);
-		}
-
-		/* Avoid duplicating messages in the Trash folder. */
-		if (destination == mailbox) {
-			success = TRUE;
-		} else if (destination != NULL) {
-			success = camel_imapx_server_copy_message (
-				imapx_server,
-				mailbox, destination,
-				uids_to_copy, TRUE,
-				cancellable, error);
-			*out_need_to_expunge = success;
-		} else {
-			success = FALSE;
-		}
-
-		if (!success) {
-			g_prefix_error (
-				error, "%s: ",
-				_("Unable to move deleted messages"));
-		}
-
-		g_clear_object (&destination);
-		g_clear_object (&imapx_store);
-	}
-
-	g_ptr_array_unref (uids_to_copy);
-	g_free (real_trash_path);
 
 	g_clear_object (&mailbox);
 
@@ -1077,91 +760,33 @@ imapx_synchronize_sync (CamelFolder *fol
 {
 	CamelStore *store;
 	CamelIMAPXStore *imapx_store;
-	CamelIMAPXServer *imapx_server;
+	CamelIMAPXConnManager *conn_man;
 	CamelIMAPXMailbox *mailbox = NULL;
-	const gchar *folder_name;
-	gboolean need_to_expunge;
 	gboolean success = FALSE;
-	GError *local_error = NULL;
 
 	store = camel_folder_get_parent_store (folder);
-	folder_name = camel_folder_get_full_name (folder);
 
 	/* Not connected, thus skip the operation */
 	if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
 		return TRUE;
 
 	imapx_store = CAMEL_IMAPX_STORE (store);
-	/* while it can be expensive job, do not treat it as such, to avoid a blockage
-	   by really expensive jobs */
-	imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error);
-
-	if (imapx_server == NULL)
-		goto exit;
-
-	mailbox = camel_imapx_folder_list_mailbox (
-		CAMEL_IMAPX_FOLDER (folder), cancellable, error);
-	if (mailbox == NULL) {
-		camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-		goto exit;
-	}
-
-	success = camel_imapx_server_sync_changes (imapx_server, mailbox, cancellable, &local_error);
-
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-
-		g_clear_error (&local_error);
-		g_clear_object (&imapx_server);
-
-		imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, &local_error);
-		if (imapx_server) {
-			success = camel_imapx_server_sync_changes (imapx_server, mailbox, cancellable, &local_error);
-		}
-	}
-
-	if (success) {
-		success = imapx_move_to_real_junk (
-			imapx_server, folder, cancellable,
-			&need_to_expunge, error);
-		expunge |= need_to_expunge;
-	}
-
-	if (success) {
-		success = imapx_move_to_real_trash (
-			imapx_server, folder, cancellable,
-			&need_to_expunge, error);
-		expunge |= need_to_expunge;
-	}
-
-	/* Sync twice - make sure deleted flags are written out,
-	 * then sync again incase expunge changed anything */
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
 
-	if (success && expunge) {
-		success = camel_imapx_server_expunge (imapx_server, mailbox, cancellable, &local_error);
+	mailbox = camel_imapx_folder_list_mailbox (CAMEL_IMAPX_FOLDER (folder), cancellable, error);
 
-		while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-			camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-
-			g_clear_error (&local_error);
-			g_clear_object (&imapx_server);
-
-			imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, &local_error);
-			if (imapx_server) {
-				success = camel_imapx_server_expunge (imapx_server, mailbox, cancellable, &local_error);
-			}
+	/* Do not update mailboxes on exit which were not entered yet */
+	if (mailbox == NULL || (camel_application_is_exiting &&
+	    camel_imapx_mailbox_get_permanentflags (mailbox) == ~0)) {
+		success = mailbox != NULL;
+	} else {
+		success = camel_imapx_conn_manager_sync_changes_sync (conn_man, mailbox, cancellable, error);
+		if (success && expunge && camel_folder_summary_get_deleted_count (folder->summary) > 0) {
+			success = camel_imapx_conn_manager_expunge_sync (conn_man, mailbox, cancellable, error);
 		}
 	}
 
-	if (local_error)
-		g_propagate_error (error, local_error);
-
-	if (imapx_server)
-		camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-
-exit:
 	g_clear_object (&mailbox);
-	g_clear_object (&imapx_server);
 
 	return success;
 }
@@ -1174,57 +799,28 @@ imapx_synchronize_message_sync (CamelFol
 {
 	CamelStore *store;
 	CamelIMAPXStore *imapx_store;
-	CamelIMAPXServer *imapx_server;
+	CamelIMAPXConnManager *conn_man;
 	CamelIMAPXMailbox *mailbox = NULL;
-	const gchar *folder_name;
 	gboolean success = FALSE;
-	GError *local_error = NULL;
 
 	store = camel_folder_get_parent_store (folder);
-	folder_name = camel_folder_get_full_name (folder);
 
 	imapx_store = CAMEL_IMAPX_STORE (store);
-	imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error);
-
-	if (imapx_server == NULL)
-		goto exit;
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
 
 	mailbox = camel_imapx_folder_list_mailbox (
 		CAMEL_IMAPX_FOLDER (folder), cancellable, error);
 
-	if (mailbox == NULL) {
-		camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
+	if (mailbox == NULL)
 		goto exit;
-	}
 
-	success = camel_imapx_server_sync_message (
-		imapx_server, mailbox, folder->summary,
+	success = camel_imapx_conn_manager_sync_message_sync (
+		conn_man, mailbox, folder->summary,
 		CAMEL_IMAPX_FOLDER (folder)->cache, uid,
-		cancellable, &local_error);
-
-	camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		g_clear_error (&local_error);
-		g_clear_object (&imapx_server);
-
-		imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, &local_error);
-		if (imapx_server) {
-			success = camel_imapx_server_sync_message (
-				imapx_server, mailbox, folder->summary,
-				CAMEL_IMAPX_FOLDER (folder)->cache, uid,
-				cancellable, &local_error);
-
-			camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-		}
-	}
-
-	if (local_error)
-		g_propagate_error (error, local_error);
+		cancellable, error);
 
 exit:
 	g_clear_object (&mailbox);
-	g_clear_object (&imapx_server);
 
 	return success;
 }
@@ -1240,73 +836,68 @@ imapx_transfer_messages_to_sync (CamelFo
 {
 	CamelStore *store;
 	CamelIMAPXStore *imapx_store;
-	CamelIMAPXServer *imapx_server;
+	CamelIMAPXConnManager *conn_man;
 	CamelIMAPXMailbox *src_mailbox = NULL;
 	CamelIMAPXMailbox *dst_mailbox = NULL;
-	const gchar *folder_name;
 	gboolean success = FALSE;
-	GError *local_error = NULL;
 
 	store = camel_folder_get_parent_store (source);
-	folder_name = camel_folder_get_full_name (source);
 
 	imapx_store = CAMEL_IMAPX_STORE (store);
-	imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error);
-
-	if (imapx_server == NULL)
-		goto exit;
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
 
 	src_mailbox = camel_imapx_folder_list_mailbox (
 		CAMEL_IMAPX_FOLDER (source), cancellable, error);
 
-	if (src_mailbox == NULL) {
-		camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
+	if (src_mailbox == NULL)
 		goto exit;
-	}
 
 	dst_mailbox = camel_imapx_folder_list_mailbox (
 		CAMEL_IMAPX_FOLDER (dest), cancellable, error);
 
-	if (dst_mailbox == NULL) {
-		camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
+	if (dst_mailbox == NULL)
 		goto exit;
-	}
 
-	success = camel_imapx_server_copy_message (
-		imapx_server, src_mailbox, dst_mailbox, uids,
-		delete_originals, cancellable, &local_error);
+	success = camel_imapx_conn_manager_copy_message_sync (
+		conn_man, src_mailbox, dst_mailbox, uids,
+		delete_originals, FALSE, cancellable, error);
 
-	camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
+exit:
+	g_clear_object (&src_mailbox);
+	g_clear_object (&dst_mailbox);
 
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		g_clear_error (&local_error);
-		g_clear_object (&imapx_server);
+	return success;
+}
 
-		imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, &local_error);
-		if (imapx_server) {
-			success = camel_imapx_server_copy_message (
-				imapx_server, src_mailbox, dst_mailbox, uids,
-				delete_originals, cancellable, &local_error);
+static void
+imapx_folder_changed (CamelFolder *folder,
+		      CamelFolderChangeInfo *info)
+{
+	g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
 
-			camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-		}
-	}
+	if (info && info->uid_removed && info->uid_removed->len) {
+		CamelIMAPXFolder *imapx_folder;
+		guint ii;
 
-	if (local_error)
-		g_propagate_error (error, local_error);
+		imapx_folder = CAMEL_IMAPX_FOLDER (folder);
 
-	/* Update destination folder only if it's not frozen,
-	 * to avoid updating for each "move" action on a single
-	 * message while filtering. */
-	if (!camel_folder_is_frozen (dest))
-		imapx_refresh_info_sync (dest, cancellable, NULL);
+		g_mutex_lock (&imapx_folder->priv->move_to_hash_table_lock);
 
-exit:
-	g_clear_object (&src_mailbox);
-	g_clear_object (&dst_mailbox);
-	g_clear_object (&imapx_server);
+		for (ii = 0; ii < info->uid_removed->len; ii++) {
+			const gchar *message_uid = info->uid_removed->pdata[ii];
 
-	return success;
+			if (!message_uid)
+				continue;
+
+			g_hash_table_remove (imapx_folder->priv->move_to_real_trash_uids, message_uid);
+			g_hash_table_remove (imapx_folder->priv->move_to_real_junk_uids, message_uid);
+		}
+
+		g_mutex_unlock (&imapx_folder->priv->move_to_hash_table_lock);
+	}
+
+	/* Chain up to parent's method. */
+	CAMEL_FOLDER_CLASS (camel_imapx_folder_parent_class)->changed (folder, info);
 }
 
 static void
@@ -1364,6 +955,7 @@ camel_imapx_folder_class_init (CamelIMAP
 	folder_class->synchronize_sync = imapx_synchronize_sync;
 	folder_class->synchronize_message_sync = imapx_synchronize_message_sync;
 	folder_class->transfer_messages_to_sync = imapx_transfer_messages_to_sync;
+	folder_class->changed = imapx_folder_changed;
 
 	g_object_class_install_property (
 		object_class,
@@ -1378,6 +970,17 @@ camel_imapx_folder_class_init (CamelIMAP
 
 	g_object_class_install_property (
 		object_class,
+		PROP_CHECK_FOLDER,
+		g_param_spec_boolean (
+			"check-folder",
+			"Check Folder",
+			_("Always check for _new mail in this folder"),
+			FALSE,
+			G_PARAM_READWRITE |
+			CAMEL_PARAM_PERSISTENT));
+
+	g_object_class_install_property (
+		object_class,
 		PROP_MAILBOX,
 		g_param_spec_object (
 			"mailbox",
@@ -1396,14 +999,14 @@ camel_imapx_folder_init (CamelIMAPXFolde
 	GHashTable *move_to_real_trash_uids;
 
 	move_to_real_junk_uids = g_hash_table_new_full (
-		(GHashFunc) g_direct_hash,
-		(GEqualFunc) g_direct_equal,
+		(GHashFunc) g_str_hash,
+		(GEqualFunc) g_str_equal,
 		(GDestroyNotify) camel_pstring_free,
 		(GDestroyNotify) NULL);
 
 	move_to_real_trash_uids = g_hash_table_new_full (
-		(GHashFunc) g_direct_hash,
-		(GEqualFunc) g_direct_equal,
+		(GHashFunc) g_str_hash,
+		(GEqualFunc) g_str_equal,
 		(GDestroyNotify) camel_pstring_free,
 		(GDestroyNotify) NULL);
 
@@ -1449,6 +1052,7 @@ camel_imapx_folder_new (CamelStore *stor
 	gboolean filter_inbox;
 	gboolean filter_junk;
 	gboolean filter_junk_inbox;
+	gboolean store_offline_sync = FALSE;
 
 	d ("opening imap folder '%s'\n", folder_dir);
 
@@ -1462,6 +1066,7 @@ camel_imapx_folder_new (CamelStore *stor
 		"filter-inbox", &filter_inbox,
 		"filter-junk", &filter_junk,
 		"filter-junk-inbox", &filter_junk_inbox,
+		"stay-synchronized", &store_offline_sync,
 		NULL);
 
 	g_object_unref (settings);
@@ -1496,16 +1101,22 @@ camel_imapx_folder_new (CamelStore *stor
 		return NULL;
 	}
 
-	/* Ensure cache will never expire, otherwise
-	 * it causes redownload of messages. */
-	camel_data_cache_set_expire_age (imapx_folder->cache, -1);
-	camel_data_cache_set_expire_access (imapx_folder->cache, -1);
-
 	state_file = g_build_filename (folder_dir, "cmeta", NULL);
 	camel_object_set_state_filename (CAMEL_OBJECT (folder), state_file);
 	g_free (state_file);
 	camel_object_state_read (CAMEL_OBJECT (folder));
 
+	if (store_offline_sync || camel_offline_folder_get_offline_sync (CAMEL_OFFLINE_FOLDER (folder))) {
+		/* Ensure cache will never expire, otherwise
+		 * it causes redownload of messages. */
+		camel_data_cache_set_expire_age (imapx_folder->cache, -1);
+		camel_data_cache_set_expire_access (imapx_folder->cache, -1);
+	} else {
+		/* Set cache expiration for one week. */
+		camel_data_cache_set_expire_age (imapx_folder->cache, 60 * 60 * 24 * 7);
+		camel_data_cache_set_expire_access (imapx_folder->cache, 60 * 60 * 24 * 7);
+	}
+
 	imapx_folder->search = camel_imapx_search_new (CAMEL_IMAPX_STORE (store));
 
 	if (filter_all)
@@ -1615,7 +1226,7 @@ camel_imapx_folder_list_mailbox (CamelIM
                                  GError **error)
 {
 	CamelIMAPXStore *imapx_store;
-	CamelIMAPXServer *server = NULL;
+	CamelIMAPXConnManager *conn_man;
 	CamelIMAPXMailbox *mailbox;
 	CamelStore *parent_store;
 	CamelStoreInfo *store_info;
@@ -1624,7 +1235,6 @@ camel_imapx_folder_list_mailbox (CamelIM
 	gchar *mailbox_name = NULL;
 	gchar *pattern;
 	gboolean success;
-	GError *local_error = NULL;
 
 	g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), FALSE);
 
@@ -1661,36 +1271,14 @@ camel_imapx_folder_list_mailbox (CamelIM
 		goto exit;
 	}
 
-	server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
-	if (server == NULL)
-		goto exit;
-
-	mailbox = camel_imapx_store_ref_mailbox (imapx_store, mailbox_name);
-	if (mailbox != NULL) {
-		camel_imapx_folder_set_mailbox (folder, mailbox);
-		goto exit;
-	}
-
 	/* Last resort is to issue a LIST command.  Maintainer should
 	 * monitor IMAP logs to make sure this is rarely if ever used. */
 
 	pattern = camel_utf8_utf7 (mailbox_name);
 
 	/* This creates a mailbox instance from the LIST response. */
-	success = camel_imapx_server_list (server, pattern, 0, cancellable, &local_error);
-
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		g_clear_error (&local_error);
-		g_clear_object (&server);
-
-		server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
-		if (server) {
-			success = camel_imapx_server_list (server, pattern, 0, cancellable, &local_error);
-		}
-	}
-
-	if (local_error)
-		g_propagate_error (error, local_error);
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
+	success = camel_imapx_conn_manager_list_sync (conn_man, pattern, 0, cancellable, error);
 
 	g_free (pattern);
 
@@ -1711,8 +1299,6 @@ camel_imapx_folder_list_mailbox (CamelIM
 	}
 
 exit:
-	g_clear_object (&server);
-
 	g_free (folder_path);
 	g_free (mailbox_name);
 
@@ -1778,6 +1364,7 @@ camel_imapx_folder_add_move_to_real_junk
 {
 	g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
 	g_return_if_fail (message_uid != NULL);
+	g_return_if_fail (camel_folder_summary_check_uid (CAMEL_FOLDER (folder)->summary, message_uid));
 
 	g_mutex_lock (&folder->priv->move_to_hash_table_lock);
 
@@ -1808,6 +1395,7 @@ camel_imapx_folder_add_move_to_real_tras
 {
 	g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
 	g_return_if_fail (message_uid != NULL);
+	g_return_if_fail (camel_folder_summary_check_uid (CAMEL_FOLDER (folder)->summary, message_uid));
 
 	g_mutex_lock (&folder->priv->move_to_hash_table_lock);
 
@@ -1867,3 +1455,26 @@ camel_imapx_folder_invalidate_local_cach
 	camel_folder_summary_free_array (array);
 }
 
+gboolean
+camel_imapx_folder_get_check_folder (CamelIMAPXFolder *folder)
+{
+	g_return_val_if_fail (folder != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), FALSE);
+
+	return folder->priv->check_folder;
+}
+
+void
+camel_imapx_folder_set_check_folder (CamelIMAPXFolder *folder,
+				     gboolean check_folder)
+{
+	g_return_if_fail (folder != NULL);
+	g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
+
+	if (folder->priv->check_folder == check_folder)
+		return;
+
+	folder->priv->check_folder = check_folder;
+
+	g_object_notify (G_OBJECT (folder), "check-folder");
+}
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.h.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.h	2016-08-15 13:52:41.950976330 +0200
@@ -1,22 +1,21 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* camel-imap-folder.h : Class for a IMAP folder */
-
-/*
- * Authors: Michael Zucchi <notzed@ximian.com>
+/* camel-imap-folder.h : Class for a IMAP folder
  *
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
- * This library is free software; you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Michael Zucchi <notzed@ximian.com>
  */
 
 #ifndef CAMEL_IMAPX_FOLDER_H
@@ -92,6 +91,17 @@ void		camel_imapx_folder_add_move_to_rea
 void		camel_imapx_folder_invalidate_local_cache
 						(CamelIMAPXFolder *folder,
 						 guint64 new_uidvalidity);
+gboolean	camel_imapx_folder_get_check_folder
+						(CamelIMAPXFolder *folder);
+void		camel_imapx_folder_set_check_folder
+						(CamelIMAPXFolder *folder,
+						 gboolean check_folder);
+void		camel_imapx_folder_claim_move_to_real_junk_uids
+						(CamelIMAPXFolder *folder,
+						 GPtrArray *out_uids_to_copy);
+void		camel_imapx_folder_claim_move_to_real_trash_uids
+						(CamelIMAPXFolder *folder,
+						 GPtrArray *out_uids_to_copy);
 
 G_END_DECLS
 
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.c.imapx-update-to-upstream	2014-07-22 12:03:27.000000000 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.c	2016-08-15 13:52:41.951976330 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-input-stream.h
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -67,9 +67,17 @@ imapx_input_stream_fill (CamelIMAPXInput
 	GInputStream *base_stream;
 	gint left = 0;
 
+	if (g_cancellable_is_cancelled (cancellable))
+		return -1;
+
 	base_stream = g_filter_input_stream_get_base_stream (
 		G_FILTER_INPUT_STREAM (is));
 
+	if (error && *error) {
+		g_warning ("%s: Avoiding GIO call with a filled error '%s'", G_STRFUNC, (*error)->message);
+		error = NULL;
+	}
+
 	left = is->priv->end - is->priv->ptr;
 	memcpy (is->priv->buf, is->priv->ptr, left);
 	is->priv->end = is->priv->buf + left;
@@ -136,6 +144,11 @@ imapx_input_stream_read (GInputStream *s
 		memcpy (buffer, priv->ptr, max);
 		priv->ptr += max;
 	} else {
+		if (error && *error) {
+			g_warning ("%s: Avoiding GIO call with a filled error '%s'", G_STRFUNC, (*error)->message);
+			error = NULL;
+		}
+
 		max = MIN (priv->literal, count);
 		max = g_input_stream_read (
 			base_stream, buffer, max, cancellable, error);
@@ -202,6 +215,11 @@ imapx_input_stream_read_nonblocking (GPo
 
 	pollable_stream = G_POLLABLE_INPUT_STREAM (base_stream);
 
+	if (error && *error) {
+		g_warning ("%s: Avoiding GIO call with a filled error '%s'", G_STRFUNC, (*error)->message);
+		error = NULL;
+	}
+
 	/* XXX The function takes a GCancellable but the class method
 	 *     does not.  Should be okay to pass NULL here since this
 	 *     is just a pass-through. */
@@ -344,7 +362,7 @@ camel_imapx_input_stream_atom (CamelIMAP
 
 		default:
 			g_set_error (
-				error, CAMEL_IMAPX_ERROR, 1,
+				error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 				"expecting atom");
 			return FALSE;
 	}
@@ -451,7 +469,7 @@ camel_imapx_input_stream_astring (CamelI
 
 		default:
 			g_set_error (
-				error, CAMEL_IMAPX_ERROR, 1,
+				error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 				"expecting astring");
 			return FALSE;
 	}
@@ -511,7 +529,7 @@ camel_imapx_input_stream_nstring (CamelI
 
 		default:
 			g_set_error (
-				error, CAMEL_IMAPX_ERROR, 1,
+				error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 				"expecting nstring");
 			return FALSE;
 	}
@@ -579,7 +597,7 @@ camel_imapx_input_stream_nstring_bytes (
 
 		default:
 			g_set_error (
-				error, CAMEL_IMAPX_ERROR, 1,
+				error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 				"nstring: token not string");
 			return FALSE;
 	}
@@ -611,7 +629,7 @@ camel_imapx_input_stream_number (CamelIM
 
 		default:
 			g_set_error (
-				error, CAMEL_IMAPX_ERROR, 1,
+				error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 				"expecting number");
 			return FALSE;
 	}
@@ -692,7 +710,10 @@ camel_imapx_input_stream_token (CamelIMA
 		return is->priv->unget_tok;
 	}
 
-	if (is->priv->literal > 0)
+	*data = NULL;
+	*len = 0;
+
+	if (is->priv->literal > 0 && !g_cancellable_is_cancelled (cancellable))
 		g_warning (
 			"stream_token called with literal %d",
 			is->priv->literal);
@@ -859,7 +880,7 @@ protocol_error:
 	else
 		is->priv->ptr = p;
 
-	g_set_error (error, CAMEL_IMAPX_ERROR, 1, "protocol error");
+	g_set_error (error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "protocol error");
 
 	return IMAPX_TOK_ERROR;
 }
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.h.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.h	2016-08-15 13:52:41.951976330 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-input-stream.h
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -49,6 +49,11 @@ typedef struct _CamelIMAPXInputStreamCla
 typedef struct _CamelIMAPXInputStreamPrivate CamelIMAPXInputStreamPrivate;
 
 typedef enum {
+	CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED = 1,
+	CAMEL_IMAPX_ERROR_IGNORE /* may ignore such errors */
+} CamelIMAPXError;
+
+typedef enum {
 	IMAPX_TOK_ERROR = -1,
 	IMAPX_TOK_TOKEN = 256,
 	IMAPX_TOK_STRING,
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.c.imapx-update-to-upstream	2016-08-15 13:52:41.891976333 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.c	2016-08-15 13:52:41.952976330 +0200
@@ -1,495 +1,545 @@
 /*
  * camel-imapx-job.c
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
-
-#include "camel-imapx-job.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 
 #include <string.h>
 
-#include "camel-imapx-folder.h"
-
-typedef struct _CamelIMAPXRealJob CamelIMAPXRealJob;
+#include "camel-imapx-job.h"
 
-/* CamelIMAPXJob + some private bits */
-struct _CamelIMAPXRealJob {
-	CamelIMAPXJob public;
+G_LOCK_DEFINE_STATIC (get_kind_name_funcs);
+static GSList *get_kind_name_funcs = NULL;
 
-	volatile gint ref_count;
+const gchar *
+camel_imapx_job_get_kind_name (guint32 job_kind)
+{
+	GSList *link;
 
-	GCancellable *cancellable;
+	switch ((CamelIMAPXJobKind) job_kind) {
+	case CAMEL_IMAPX_JOB_UNKNOWN:
+		return "UNKNOWN";
+	case CAMEL_IMAPX_JOB_CAPABILITY:
+		return "CAPABILITY";
+	case CAMEL_IMAPX_JOB_STARTTLS:
+		return "STARTTLS";
+	case CAMEL_IMAPX_JOB_AUTHENTICATE:
+		return "AUTHENTICATE";
+	case CAMEL_IMAPX_JOB_LOGIN:
+		return "LOGIN";
+	case CAMEL_IMAPX_JOB_NAMESPACE:
+		return "NAMESPACE";
+	case CAMEL_IMAPX_JOB_SELECT:
+		return "SELECT";
+	case CAMEL_IMAPX_JOB_STATUS:
+		return "STATUS";
+	case CAMEL_IMAPX_JOB_ENABLE:
+		return "ENABLE";
+	case CAMEL_IMAPX_JOB_NOTIFY:
+		return "NOTIFY";
+	case CAMEL_IMAPX_JOB_GET_MESSAGE:
+		return "GET_MESSAGE";
+	case CAMEL_IMAPX_JOB_SYNC_MESSAGE:
+		return "SYNC_MESSAGE";
+	case CAMEL_IMAPX_JOB_APPEND_MESSAGE:
+		return "APPEND_MESSAGE";
+	case CAMEL_IMAPX_JOB_COPY_MESSAGE:
+		return "COPY_MESSAGE";
+	case CAMEL_IMAPX_JOB_MOVE_MESSAGE:
+		return "MOVE_MESSAGE";
+	case CAMEL_IMAPX_JOB_FETCH_NEW_MESSAGES:
+		return "FETCH_NEW_MESSAGES";
+	case CAMEL_IMAPX_JOB_REFRESH_INFO:
+		return "REFRESH_INFO";
+	case CAMEL_IMAPX_JOB_SYNC_CHANGES:
+		return "SYNC_CHANGES";
+	case CAMEL_IMAPX_JOB_EXPUNGE:
+		return "EXPUNGE";
+	case CAMEL_IMAPX_JOB_NOOP:
+		return "NOOP";
+	case CAMEL_IMAPX_JOB_IDLE:
+		return "IDLE";
+	case CAMEL_IMAPX_JOB_DONE:
+		return "DONE";
+	case CAMEL_IMAPX_JOB_LIST:
+		return "LIST";
+	case CAMEL_IMAPX_JOB_LSUB:
+		return "LSUB";
+	case CAMEL_IMAPX_JOB_CREATE_MAILBOX:
+		return "CREATE_MAILBOX";
+	case CAMEL_IMAPX_JOB_DELETE_MAILBOX:
+		return "DELETE_MAILBOX";
+	case CAMEL_IMAPX_JOB_RENAME_MAILBOX:
+		return "RENAME_MAILBOX";
+	case CAMEL_IMAPX_JOB_SUBSCRIBE_MAILBOX:
+		return "SUBSCRIBE_MAILBOX";
+	case CAMEL_IMAPX_JOB_UNSUBSCRIBE_MAILBOX:
+		return "UNSUBSCRIBE_MAILBOX";
+	case CAMEL_IMAPX_JOB_UPDATE_QUOTA_INFO:
+		return "UPDATE_QUOTA_INFO";
+	case CAMEL_IMAPX_JOB_UID_SEARCH:
+		return "UID_SEARCH";
+	case CAMEL_IMAPX_JOB_LAST:
+		break;
+	}
 
-	/* This is set by camel_imapx_job_take_error(),
-	 * and propagated through camel_imapx_job_wait(). */
-	GError *error;
+	G_LOCK (get_kind_name_funcs);
 
-	/* Used for running some jobs synchronously. */
-	GCond done_cond;
-	GMutex done_mutex;
-	gboolean done_flag;
+	for (link = get_kind_name_funcs; link; link = g_slist_next (link)) {
+		CamelIMAPXJobGetKindNameFunc get_kind_name = link->data;
 
-	/* Extra job-specific data. */
-	gpointer data;
-	GDestroyNotify destroy_data;
+		if (get_kind_name) {
+			const gchar *name = get_kind_name (job_kind);
 
-	CamelIMAPXMailbox *mailbox;
-	GMutex mailbox_lock;
+			if (name) {
+				G_UNLOCK (get_kind_name_funcs);
+				return name;
+			}
+		}
+	}
 
-	CamelIMAPXMailbox *guard_mailbox_update; /* uses the mailbox_lock */
-};
+	G_UNLOCK (get_kind_name_funcs);
 
-static void
-imapx_job_cancelled_cb (GCancellable *cancellable,
-                        CamelIMAPXJob *job)
-{
-	/* Unblock camel_imapx_run_job() immediately.
-	 *
-	 * If camel_imapx_job_done() is called sometime later,
-	 * the GCond will broadcast but no one will be listening. */
+	if (job_kind == CAMEL_IMAPX_JOB_LAST)
+		return "LAST";
 
-	camel_imapx_job_done (job);
+	return "???";
 }
 
-CamelIMAPXJob *
-camel_imapx_job_new (GCancellable *cancellable)
+void
+camel_imapx_job_register_get_kind_name_func (CamelIMAPXJobGetKindNameFunc get_kind_name)
 {
-	CamelIMAPXRealJob *real_job;
-
-	real_job = g_slice_new0 (CamelIMAPXRealJob);
-
-	/* Initialize private bits. */
-	real_job->ref_count = 1;
-	g_cond_init (&real_job->done_cond);
-	g_mutex_init (&real_job->done_mutex);
+	g_return_if_fail (get_kind_name != NULL);
 
-	if (cancellable != NULL)
-		g_object_ref (cancellable);
-	real_job->cancellable = cancellable;
+	G_LOCK (get_kind_name_funcs);
 
-	g_mutex_init (&real_job->mailbox_lock);
+	if (!g_slist_find (get_kind_name_funcs, get_kind_name))
+		get_kind_name_funcs = g_slist_prepend (get_kind_name_funcs, get_kind_name);
 
-	return (CamelIMAPXJob *) real_job;
+	G_UNLOCK (get_kind_name_funcs);
 }
 
-CamelIMAPXJob *
-camel_imapx_job_ref (CamelIMAPXJob *job)
+void
+camel_imapx_job_unregister_get_kind_name_func (CamelIMAPXJobGetKindNameFunc get_kind_name)
 {
-	CamelIMAPXRealJob *real_job;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), NULL);
+	g_return_if_fail (get_kind_name != NULL);
 
-	real_job = (CamelIMAPXRealJob *) job;
+	G_LOCK (get_kind_name_funcs);
 
-	g_atomic_int_inc (&real_job->ref_count);
+	g_warn_if_fail (g_slist_find (get_kind_name_funcs, get_kind_name));
+	get_kind_name_funcs = g_slist_remove (get_kind_name_funcs, get_kind_name);
 
-	return job;
+	G_UNLOCK (get_kind_name_funcs);
 }
 
-void
-camel_imapx_job_unref (CamelIMAPXJob *job)
-{
-	CamelIMAPXRealJob *real_job;
+struct _CamelIMAPXJob {
+	volatile gint ref_count;
 
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+	guint32 job_kind;
+	CamelIMAPXMailbox *mailbox;
 
-	real_job = (CamelIMAPXRealJob *) job;
+	CamelIMAPXJobRunSyncFunc run_sync;
+	CamelIMAPXJobMatchesFunc matches;
+	CamelIMAPXJobCopyResultFunc copy_result;
 
-	if (g_atomic_int_dec_and_test (&real_job->ref_count)) {
+	/* Extra job-specific data. */
+	gpointer user_data;
+	GDestroyNotify destroy_user_data;
 
-		/* Free the public stuff. */
+	gboolean result_is_set;
+	gboolean result_success;
+	gpointer result_data;
+	GError *result_error;
+	GDestroyNotify destroy_result_data;
 
-		if (real_job->public.pop_operation_msg)
-			camel_operation_pop_message (real_job->cancellable);
+	GCond done_cond;
+	GMutex done_mutex;
+	gboolean is_done;
 
-		/* Free the private stuff. */
+	GCancellable *abort_cancellable;
+};
 
-		if (real_job->cancellable != NULL)
-			g_object_unref (real_job->cancellable);
+CamelIMAPXJob *
+camel_imapx_job_new (guint32 job_kind,
+		     CamelIMAPXMailbox *mailbox,
+		     CamelIMAPXJobRunSyncFunc run_sync,
+		     CamelIMAPXJobMatchesFunc matches,
+		     CamelIMAPXJobCopyResultFunc copy_result)
+{
+	CamelIMAPXJob *job;
+
+	g_return_val_if_fail (run_sync != NULL, NULL);
+
+	job = g_new0 (CamelIMAPXJob, 1);
+	job->ref_count = 1;
+	job->job_kind = job_kind;
+	job->mailbox = mailbox ? g_object_ref (mailbox) : NULL;
+	job->run_sync = run_sync;
+	job->matches = matches;
+	job->copy_result = copy_result;
+	job->abort_cancellable = camel_operation_new ();
+	job->is_done = FALSE;
 
-		g_clear_error (&real_job->error);
+	g_cond_init (&job->done_cond);
+	g_mutex_init (&job->done_mutex);
 
-		g_cond_clear (&real_job->done_cond);
-		g_mutex_clear (&real_job->done_mutex);
+	return job;
+}
 
-		if (real_job->destroy_data != NULL)
-			real_job->destroy_data (real_job->data);
+CamelIMAPXJob *
+camel_imapx_job_ref (CamelIMAPXJob *job)
+{
+	g_return_val_if_fail (job != NULL, NULL);
 
-		g_mutex_lock (&real_job->mailbox_lock);
-		if (real_job->guard_mailbox_update) {
-			camel_imapx_mailbox_unlock_update (real_job->guard_mailbox_update);
-			g_clear_object (&real_job->guard_mailbox_update);
-		}
-		g_mutex_unlock (&real_job->mailbox_lock);
+	g_atomic_int_inc (&job->ref_count);
 
-		g_clear_object (&real_job->mailbox);
-		g_mutex_clear (&real_job->mailbox_lock);
+	return job;
+}
 
-		/* Fill the memory with a bit pattern before releasing
-		 * it back to the slab allocator, so we can more easily
-		 * identify dangling CamelIMAPXJob pointers. */
-		memset (real_job, 0xaa, sizeof (CamelIMAPXRealJob));
+void
+camel_imapx_job_unref (CamelIMAPXJob *job)
+{
+	g_return_if_fail (job != NULL);
 
-		/* But leave the reference count set to zero, so
-		 * CAMEL_IS_IMAPX_JOB can identify it as bad. */
-		real_job->ref_count = 0;
+	if (g_atomic_int_dec_and_test (&job->ref_count)) {
+		if (job->destroy_user_data)
+			job->destroy_user_data (job->user_data);
 
-		g_slice_free (CamelIMAPXRealJob, real_job);
-	}
-}
+		if (job->result_is_set && job->destroy_result_data)
+			job->destroy_result_data (job->result_data);
 
-gboolean
-camel_imapx_job_check (CamelIMAPXJob *job)
-{
-	CamelIMAPXRealJob *real_job;
+		g_clear_object (&job->mailbox);
+		g_clear_object (&job->abort_cancellable);
+		g_clear_error (&job->result_error);
+
+		g_cond_clear (&job->done_cond);
+		g_mutex_clear (&job->done_mutex);
 
-	real_job = (CamelIMAPXRealJob *) job;
+		job->ref_count = 0xdeadbeef;
 
-	return (real_job != NULL && real_job->ref_count > 0);
+		g_free (job);
+	}
 }
 
-void
-camel_imapx_job_cancel (CamelIMAPXJob *job)
+guint32
+camel_imapx_job_get_kind (CamelIMAPXJob *job)
 {
-	CamelIMAPXRealJob *real_job;
+	g_return_val_if_fail (job != NULL, CAMEL_IMAPX_JOB_UNKNOWN);
 
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+	return job->job_kind;
+}
 
-	real_job = (CamelIMAPXRealJob *) job;
+CamelIMAPXMailbox *
+camel_imapx_job_get_mailbox (CamelIMAPXJob *job)
+{
+	g_return_val_if_fail (job != NULL, NULL);
 
-	g_cancellable_cancel (real_job->cancellable);
+	return job->mailbox;
 }
 
-/**
- * camel_imapx_job_wait:
- * @job: a #CamelIMAPXJob
- * @error: return location for a #GError, or %NULL
- *
- * Blocks until @job completes by way of camel_imapx_job_done().  If @job
- * completed successfully, the function returns %TRUE.  If @job was given
- * a #GError by way of camel_imapx_job_take_error(), or its #GCancellable
- * was cancelled, the function sets @error and returns %FALSE.
- *
- * Returns: whether @job completed successfully
- *
- * Since: 3.10
- **/
-gboolean
-camel_imapx_job_wait (CamelIMAPXJob *job,
-                      GError **error)
+gpointer
+camel_imapx_job_get_user_data (CamelIMAPXJob *job)
 {
-	CamelIMAPXRealJob *real_job;
-	GCancellable *cancellable;
-	gulong cancel_id = 0;
-	gboolean success = TRUE;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE);
-
-	real_job = (CamelIMAPXRealJob *) job;
-	cancellable = camel_imapx_job_get_cancellable (job);
-
-	if (G_IS_CANCELLABLE (cancellable))
-		cancel_id = g_cancellable_connect (
-			cancellable,
-			G_CALLBACK (imapx_job_cancelled_cb),
-			camel_imapx_job_ref (job),
-			(GDestroyNotify) camel_imapx_job_unref);
-
-	g_mutex_lock (&real_job->done_mutex);
-	while (!real_job->done_flag && !g_cancellable_is_cancelled (cancellable))
-		g_cond_wait (
-			&real_job->done_cond,
-			&real_job->done_mutex);
-	g_mutex_unlock (&real_job->done_mutex);
-
-	if (cancel_id > 0)
-		g_cancellable_disconnect (cancellable, cancel_id);
-
-	/* Cancellation takes priority over other errors. */
-	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
-		success = FALSE;
-	} else if (real_job->error != NULL) {
-		/* Copy the error, don't propagate it.
-		 * We want our GError to remain intact. */
-		if (error != NULL) {
-			g_warn_if_fail (*error == NULL);
-			*error = g_error_copy (real_job->error);
-		}
-		success = FALSE;
-	}
+	g_return_val_if_fail (job != NULL, NULL);
 
-	return success;
+	return job->user_data;
 }
 
 void
-camel_imapx_job_done (CamelIMAPXJob *job)
+camel_imapx_job_set_user_data (CamelIMAPXJob *job,
+			       gpointer user_data,
+			       GDestroyNotify destroy_user_data)
 {
-	CamelIMAPXRealJob *real_job;
-
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+	g_return_if_fail (job != NULL);
 
-	real_job = (CamelIMAPXRealJob *) job;
-
-	g_mutex_lock (&real_job->mailbox_lock);
-	if (real_job->guard_mailbox_update) {
-		camel_imapx_mailbox_unlock_update (real_job->guard_mailbox_update);
-		g_clear_object (&real_job->guard_mailbox_update);
-	}
-	g_mutex_unlock (&real_job->mailbox_lock);
+	if (job->destroy_user_data)
+		job->destroy_user_data (job->user_data);
 
-	g_mutex_lock (&real_job->done_mutex);
-	real_job->done_flag = TRUE;
-	g_cond_broadcast (&real_job->done_cond);
-	g_mutex_unlock (&real_job->done_mutex);
+	job->user_data = user_data;
+	job->destroy_user_data = destroy_user_data;
 }
 
 gboolean
-camel_imapx_job_run (CamelIMAPXJob *job,
-                     CamelIMAPXServer *is,
-                     GError **error)
+camel_imapx_job_was_cancelled (CamelIMAPXJob *job)
 {
-	GCancellable *cancellable;
-	gboolean success;
+	g_return_val_if_fail (job != NULL, FALSE);
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-	g_return_val_if_fail (job->start != NULL, FALSE);
-
-	cancellable = ((CamelIMAPXRealJob *) job)->cancellable;
-
-	if (g_cancellable_set_error_if_cancelled (cancellable, error))
+	if (!job->result_is_set)
 		return FALSE;
 
-	success = job->start (job, is, cancellable, error);
-
-	if (success && !job->noreply)
-		success = camel_imapx_job_wait (job, error);
-
-	return success;
+	return g_error_matches (job->result_error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
 }
 
 void
-camel_imapx_job_guard_mailbox_update (CamelIMAPXJob *job,
-				      CamelIMAPXMailbox *mailbox)
-{
-	CamelIMAPXRealJob *real_job;
-
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
-
-	if (mailbox)
-		g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
-
-	real_job = (CamelIMAPXRealJob *) job;
-
-	g_mutex_lock (&real_job->mailbox_lock);
-
-	if (mailbox != real_job->guard_mailbox_update) {
-		if (real_job->guard_mailbox_update) {
-			camel_imapx_mailbox_unlock_update (real_job->guard_mailbox_update);
-			g_clear_object (&real_job->guard_mailbox_update);
-		}
-
-		if (mailbox) {
-			real_job->guard_mailbox_update = g_object_ref (mailbox);
-			camel_imapx_mailbox_lock_update (real_job->guard_mailbox_update);
-		}
+camel_imapx_job_set_result (CamelIMAPXJob *job,
+			    gboolean success,
+			    gpointer result,
+			    const GError *error,
+			    GDestroyNotify destroy_result)
+{
+	g_return_if_fail (job != NULL);
+
+	if (job->result_is_set) {
+		if (job->destroy_result_data)
+			job->destroy_result_data (job->result_data);
+		g_clear_error (&job->result_error);
 	}
 
-	g_mutex_unlock (&real_job->mailbox_lock);
+	job->result_is_set = TRUE;
+	job->result_success = success;
+	job->result_data = result;
+	job->destroy_result_data = destroy_result;
+
+	if (error)
+		job->result_error = g_error_copy (error);
 }
 
+/* This doesn't return whether the job succeeded, but whether the result
+   was set for the job, thus some result copied. All out-arguments are optional. */
 gboolean
-camel_imapx_job_matches (CamelIMAPXJob *job,
-                         CamelIMAPXMailbox *mailbox,
-                         const gchar *uid)
+camel_imapx_job_copy_result (CamelIMAPXJob *job,
+			     gboolean *out_success,
+			     gpointer *out_result,
+			     GError **out_error,
+			     GDestroyNotify *out_destroy_result)
 {
-	/* XXX CamelIMAPXMailbox can be NULL.  I'm less sure about
-	 *     the message UID but let's assume that can be NULL too. */
+	g_return_val_if_fail (job != NULL, FALSE);
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE);
+	if (!job->result_is_set)
+		return FALSE;
 
-	if (mailbox != NULL)
-		g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+	if (out_success)
+		*out_success = job->result_success;
 
-	if (job->matches == NULL)
-		return FALSE;
+	if (out_result) {
+		*out_result = NULL;
 
-	return job->matches (job, mailbox, uid);
-}
+		if (job->copy_result) {
+			job->copy_result (job, job->result_data, out_result);
+		} else if (job->result_data) {
+			g_warn_if_reached ();
+		}
+	}
 
-gpointer
-camel_imapx_job_get_data (CamelIMAPXJob *job)
-{
-	CamelIMAPXRealJob *real_job;
+	if (out_error) {
+		g_warn_if_fail (*out_error == NULL);
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), NULL);
+		if (job->result_error)
+			*out_error = g_error_copy (job->result_error);
+	}
 
-	real_job = (CamelIMAPXRealJob *) job;
+	if (out_destroy_result)
+		*out_destroy_result = job->destroy_result_data;
 
-	return real_job->data;
+	return TRUE;
 }
 
-void
-camel_imapx_job_set_data (CamelIMAPXJob *job,
-                          gpointer data,
-                          GDestroyNotify destroy_data)
+/* Similar to camel_imapx_job_copy_result() except it gives result data
+   to the caller and unsets (not frees) the data in the job. */
+gboolean
+camel_imapx_job_take_result_data (CamelIMAPXJob *job,
+				  gpointer *out_result)
 {
-	CamelIMAPXRealJob *real_job;
+	g_return_val_if_fail (job != NULL, FALSE);
 
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+	if (!job->result_is_set)
+		return FALSE;
+
+	if (out_result) {
+		*out_result = job->result_data;
+	} else if (job->destroy_result_data) {
+		job->destroy_result_data (job->result_data);
+	}
 
-	real_job = (CamelIMAPXRealJob *) job;
+	job->result_data = NULL;
+	g_clear_error (&job->result_error);
 
-	if (real_job->destroy_data != NULL)
-		real_job->destroy_data (real_job->data);
+	job->result_is_set = FALSE;
 
-	real_job->data = data;
-	real_job->destroy_data = destroy_data;
+	return TRUE;
 }
 
 gboolean
-camel_imapx_job_has_mailbox (CamelIMAPXJob *job,
-                             CamelIMAPXMailbox *mailbox)
+camel_imapx_job_matches (CamelIMAPXJob *job,
+			 CamelIMAPXJob *other_job)
 {
-	CamelIMAPXRealJob *real_job;
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (other_job != NULL, FALSE);
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE);
-
-	if (mailbox != NULL)
-		g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+	if (job->job_kind != other_job->job_kind)
+		return FALSE;
 
-	real_job = (CamelIMAPXRealJob *) job;
+	if (job->mailbox != other_job->mailbox)
+		return FALSE;
 
-	/* Not necessary to lock the mutex since
-	 * we're just comparing memory addresses. */
+	if (job->matches)
+		return job->matches (job, other_job);
 
-	return (mailbox == real_job->mailbox);
+	return TRUE;
 }
 
-CamelIMAPXMailbox *
-camel_imapx_job_ref_mailbox (CamelIMAPXJob *job)
+static void
+imapx_job_cancelled_cb (GCancellable *cancellable,
+			CamelIMAPXJob *job)
 {
-	CamelIMAPXRealJob *real_job;
-	CamelIMAPXMailbox *mailbox = NULL;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), NULL);
-
-	real_job = (CamelIMAPXRealJob *) job;
+	camel_imapx_job_abort (job);
+}
 
-	g_mutex_lock (&real_job->mailbox_lock);
+static void
+imapx_job_push_message_cb (CamelOperation *operation,
+			   const gchar *message,
+			   GCancellable *job_cancellable)
+{
+	g_return_if_fail (CAMEL_IS_OPERATION (operation));
+	g_return_if_fail (CAMEL_IS_OPERATION (job_cancellable));
 
-	if (real_job->mailbox != NULL)
-		mailbox = g_object_ref (real_job->mailbox);
+	camel_operation_push_message (job_cancellable, "%s", message);
+}
 
-	g_mutex_unlock (&real_job->mailbox_lock);
+static void
+imapx_job_pop_message_cb (CamelOperation *operation,
+			  GCancellable *job_cancellable)
+{
+	g_return_if_fail (CAMEL_IS_OPERATION (operation));
+	g_return_if_fail (CAMEL_IS_OPERATION (job_cancellable));
 
-	return mailbox;
+	camel_operation_pop_message (job_cancellable);
 }
 
-void
-camel_imapx_job_set_mailbox (CamelIMAPXJob *job,
-                             CamelIMAPXMailbox *mailbox)
+static void
+imapx_job_progress_cb (CamelOperation *operation,
+		       gint percent,
+		       GCancellable *job_cancellable)
 {
-	CamelIMAPXRealJob *real_job;
+	g_return_if_fail (CAMEL_IS_OPERATION (operation));
+	g_return_if_fail (CAMEL_IS_OPERATION (job_cancellable));
+
+	camel_operation_progress (job_cancellable, percent);
+}
 
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+gboolean
+camel_imapx_job_run_sync (CamelIMAPXJob *job,
+			  CamelIMAPXServer *server,
+			  GCancellable *cancellable,
+			  GError **error)
+{
+	GError *local_error = NULL;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (job != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE);
+	g_return_val_if_fail (job->run_sync != NULL, FALSE);
+
+	g_mutex_lock (&job->done_mutex);
+	job->is_done = FALSE;
+	g_mutex_unlock (&job->done_mutex);
+
+	g_cancellable_reset (job->abort_cancellable);
+
+	if (!g_cancellable_set_error_if_cancelled (cancellable, error)) {
+		gulong cancelled_handler_id = 0;
+		gulong push_message_handler_id = 0;
+		gulong pop_message_handler_id = 0;
+		gulong progress_handler_id = 0;
+
+		/* Proxy signals between job's cancellable and the abort_cancellable */
+		if (cancellable)
+			cancelled_handler_id = g_cancellable_connect (cancellable,
+				G_CALLBACK (imapx_job_cancelled_cb), job, NULL);
+
+		if (CAMEL_IS_OPERATION (cancellable)) {
+			push_message_handler_id = g_signal_connect (job->abort_cancellable, "push-message",
+				G_CALLBACK (imapx_job_push_message_cb), cancellable);
+			pop_message_handler_id = g_signal_connect (job->abort_cancellable, "pop-message",
+				G_CALLBACK (imapx_job_pop_message_cb), cancellable);
+			progress_handler_id = g_signal_connect (job->abort_cancellable, "progress",
+				G_CALLBACK (imapx_job_progress_cb), cancellable);
+		}
 
-	real_job = (CamelIMAPXRealJob *) job;
+		success = job->run_sync (job, server, job->abort_cancellable, &local_error);
 
-	if (mailbox != NULL) {
-		g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
-		g_object_ref (mailbox);
+		if (push_message_handler_id)
+			g_signal_handler_disconnect (job->abort_cancellable, push_message_handler_id);
+		if (pop_message_handler_id)
+			g_signal_handler_disconnect (job->abort_cancellable, pop_message_handler_id);
+		if (progress_handler_id)
+			g_signal_handler_disconnect (job->abort_cancellable, progress_handler_id);
+		if (cancelled_handler_id)
+			g_cancellable_disconnect (cancellable, cancelled_handler_id);
 	}
 
-	g_mutex_lock (&real_job->mailbox_lock);
-
-	g_clear_object (&real_job->mailbox);
-	real_job->mailbox = mailbox;
+	if (local_error)
+		g_propagate_error (error, local_error);
 
-	g_mutex_unlock (&real_job->mailbox_lock);
+	return success;
 }
 
-GCancellable *
-camel_imapx_job_get_cancellable (CamelIMAPXJob *job)
+void
+camel_imapx_job_done (CamelIMAPXJob *job)
 {
-	CamelIMAPXRealJob *real_job;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), NULL);
-
-	real_job = (CamelIMAPXRealJob *) job;
+	g_return_if_fail (job != NULL);
 
-	return real_job->cancellable;
+	g_mutex_lock (&job->done_mutex);
+	job->is_done = TRUE;
+	g_cond_broadcast (&job->done_cond);
+	g_mutex_unlock (&job->done_mutex);
 }
 
-/**
- * camel_imapx_job_take_error:
- * @job: a #CamelIMAPXJob
- * @error: a #GError
- *
- * Takes over the caller's ownership of @error, so the caller does not
- * need to free it any more.  Call this when a #CamelIMAPXCommand fails
- * and the @job is to be aborted.
- *
- * The @error will be returned to callers of camel_imapx_job_wait() or
- * camel_imapx_job_run().
- *
- * Since: 3.10
- **/
 void
-camel_imapx_job_take_error (CamelIMAPXJob *job,
-                            GError *error)
+camel_imapx_job_abort (CamelIMAPXJob *job)
 {
-	CamelIMAPXRealJob *real_job;
+	g_return_if_fail (job != NULL);
 
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
-	g_return_if_fail (error != NULL);
+	g_cancellable_cancel (job->abort_cancellable);
+}
 
-	real_job = (CamelIMAPXRealJob *) job;
-	g_return_if_fail (real_job->error != error);
+static void
+camel_imapx_job_wait_cancelled_cb (GCancellable *cancellable,
+				   gpointer user_data)
+{
+	CamelIMAPXJob *job = user_data;
 
-	g_clear_error (&real_job->error);
+	g_return_if_fail (job != NULL);
 
-	real_job->error = error;  /* takes ownership */
+	g_mutex_lock (&job->done_mutex);
+	g_cond_broadcast (&job->done_cond);
+	g_mutex_unlock (&job->done_mutex);
 }
 
-/**
- * camel_imapx_job_set_error_if_failed:
- * @job: a #CamelIMAPXJob
- * @error: a location for a #GError
- *
- * Sets @error to a new GError instance and returns TRUE, if the job has set
- * an error or when it was cancelled.
- *
- * Returns: Whether the job failed.
- *
- * Since: 3.12.4
- **/
-gboolean
-camel_imapx_job_set_error_if_failed (CamelIMAPXJob *job,
-				     GError **error)
+void
+camel_imapx_job_wait_sync (CamelIMAPXJob *job,
+			   GCancellable *cancellable)
 {
-	CamelIMAPXRealJob *real_job;
+	gulong handler_id = 0;
+
+	g_return_if_fail (job != NULL);
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), TRUE);
-	g_return_val_if_fail (error != NULL, TRUE);
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	real_job = (CamelIMAPXRealJob *) job;
+	if (cancellable)
+		handler_id = g_cancellable_connect (cancellable, G_CALLBACK (camel_imapx_job_wait_cancelled_cb), job, NULL);
 
-	if (real_job->error) {
-		g_propagate_error (error, g_error_copy (real_job->error));
-		return TRUE;
+	g_mutex_lock (&job->done_mutex);
+	while (!job->is_done && !g_cancellable_is_cancelled (cancellable)) {
+		g_cond_wait (&job->done_cond, &job->done_mutex);
 	}
+	g_mutex_unlock (&job->done_mutex);
 
-	return g_cancellable_set_error_if_cancelled (real_job->cancellable, error);
+	if (handler_id)
+		g_cancellable_disconnect (cancellable, handler_id);
 }
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.h.imapx-update-to-upstream	2016-08-15 13:52:41.892976333 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.h	2016-08-15 13:52:41.952976330 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-job.h
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -20,72 +20,103 @@
 
 #include "camel-imapx-server.h"
 
-#define CAMEL_IS_IMAPX_JOB(job) \
-	(camel_imapx_job_check (job))
-
 G_BEGIN_DECLS
 
 typedef struct _CamelIMAPXJob CamelIMAPXJob;
 
-struct _uidset_state {
-	gint entries, uids;
-	gint total, limit;
-	guint32 start;
-	guint32 last;
-};
-
-struct _CamelIMAPXJob {
-	/* Whether to pop a status message off the
-	 * GCancellable when the job is finalized. */
-	gboolean pop_operation_msg;
-
-	gboolean	(*start)		(CamelIMAPXJob *job,
-						 CamelIMAPXServer *is,
-						 GCancellable *cancellable,
-						 GError **error);
-	gboolean	(*matches)		(CamelIMAPXJob *job,
-						 CamelIMAPXMailbox *mailbox,
-						 const gchar *uid);
+struct _CamelIMAPXJob;
 
-	guint noreply:1;	/* dont wait for reply */
-	guint32 type;		/* operation type */
-	gint pri;		/* the command priority */
-	volatile gint commands;	/* counts how many commands are outstanding */
-};
+typedef enum {
+	CAMEL_IMAPX_JOB_UNKNOWN = 0,
+	CAMEL_IMAPX_JOB_CAPABILITY,
+	CAMEL_IMAPX_JOB_STARTTLS,
+	CAMEL_IMAPX_JOB_AUTHENTICATE,
+	CAMEL_IMAPX_JOB_LOGIN,
+	CAMEL_IMAPX_JOB_NAMESPACE,
+	CAMEL_IMAPX_JOB_SELECT,
+	CAMEL_IMAPX_JOB_STATUS,
+	CAMEL_IMAPX_JOB_ENABLE,
+	CAMEL_IMAPX_JOB_NOTIFY,
+	CAMEL_IMAPX_JOB_GET_MESSAGE,
+	CAMEL_IMAPX_JOB_SYNC_MESSAGE,
+	CAMEL_IMAPX_JOB_APPEND_MESSAGE,
+	CAMEL_IMAPX_JOB_COPY_MESSAGE,
+	CAMEL_IMAPX_JOB_MOVE_MESSAGE,
+	CAMEL_IMAPX_JOB_FETCH_NEW_MESSAGES,
+	CAMEL_IMAPX_JOB_REFRESH_INFO,
+	CAMEL_IMAPX_JOB_SYNC_CHANGES,
+	CAMEL_IMAPX_JOB_EXPUNGE,
+	CAMEL_IMAPX_JOB_NOOP,
+	CAMEL_IMAPX_JOB_IDLE,
+	CAMEL_IMAPX_JOB_DONE,
+	CAMEL_IMAPX_JOB_LIST,
+	CAMEL_IMAPX_JOB_LSUB,
+	CAMEL_IMAPX_JOB_CREATE_MAILBOX,
+	CAMEL_IMAPX_JOB_DELETE_MAILBOX,
+	CAMEL_IMAPX_JOB_RENAME_MAILBOX,
+	CAMEL_IMAPX_JOB_SUBSCRIBE_MAILBOX,
+	CAMEL_IMAPX_JOB_UNSUBSCRIBE_MAILBOX,
+	CAMEL_IMAPX_JOB_UPDATE_QUOTA_INFO,
+	CAMEL_IMAPX_JOB_UID_SEARCH,
+	CAMEL_IMAPX_JOB_LAST
+} CamelIMAPXJobKind;
+
+typedef const gchar *	(* CamelIMAPXJobGetKindNameFunc)(guint32 job_kind);
+
+const gchar *	camel_imapx_job_get_kind_name	(guint32 job_kind);
+void		camel_imapx_job_register_get_kind_name_func
+						(CamelIMAPXJobGetKindNameFunc get_kind_name);
+void		camel_imapx_job_unregister_get_kind_name_func
+						(CamelIMAPXJobGetKindNameFunc get_kind_name);
+
+typedef gboolean	(* CamelIMAPXJobRunSyncFunc)	(CamelIMAPXJob *job,
+							 CamelIMAPXServer *server,
+							 GCancellable *cancellable,
+							 GError **error);
+typedef gboolean	(* CamelIMAPXJobMatchesFunc)	(CamelIMAPXJob *job,
+							 CamelIMAPXJob *other_job);
+typedef void		(* CamelIMAPXJobCopyResultFunc)	(CamelIMAPXJob *job,
+							 gconstpointer set_result,
+							 gpointer *out_result);
 
-CamelIMAPXJob *	camel_imapx_job_new		(GCancellable *cancellable);
+CamelIMAPXJob *	camel_imapx_job_new		(guint32 job_kind,
+						 CamelIMAPXMailbox *mailbox,
+						 CamelIMAPXJobRunSyncFunc run_sync,
+						 CamelIMAPXJobMatchesFunc matches,
+						 CamelIMAPXJobCopyResultFunc copy_result);
 CamelIMAPXJob *	camel_imapx_job_ref		(CamelIMAPXJob *job);
 void		camel_imapx_job_unref		(CamelIMAPXJob *job);
-gboolean	camel_imapx_job_check		(CamelIMAPXJob *job);
-void		camel_imapx_job_cancel		(CamelIMAPXJob *job);
-gboolean	camel_imapx_job_wait		(CamelIMAPXJob *job,
-						 GError **error);
-void		camel_imapx_job_done		(CamelIMAPXJob *job);
-gboolean	camel_imapx_job_run		(CamelIMAPXJob *job,
-						 CamelIMAPXServer *is,
-						 GError **error);
-void		camel_imapx_job_guard_mailbox_update
-						(CamelIMAPXJob *job,
-						 CamelIMAPXMailbox *mailbox);
-gboolean	camel_imapx_job_matches		(CamelIMAPXJob *job,
-						 CamelIMAPXMailbox *mailbox,
-						 const gchar *uid);
-gpointer	camel_imapx_job_get_data	(CamelIMAPXJob *job);
-void		camel_imapx_job_set_data	(CamelIMAPXJob *job,
-						 gpointer data,
-						 GDestroyNotify destroy_data);
-gboolean	camel_imapx_job_has_mailbox	(CamelIMAPXJob *job,
-						 CamelIMAPXMailbox *mailbox);
+guint32		camel_imapx_job_get_kind	(CamelIMAPXJob *job);
 CamelIMAPXMailbox *
-		camel_imapx_job_ref_mailbox	(CamelIMAPXJob *job);
-void		camel_imapx_job_set_mailbox	(CamelIMAPXJob *job,
-						 CamelIMAPXMailbox *mailbox);
-GCancellable *	camel_imapx_job_get_cancellable	(CamelIMAPXJob *job);
-void		camel_imapx_job_take_error	(CamelIMAPXJob *job,
-						 GError *error);
-gboolean	camel_imapx_job_set_error_if_failed
+		camel_imapx_job_get_mailbox	(CamelIMAPXJob *job);
+gpointer	camel_imapx_job_get_user_data	(CamelIMAPXJob *job);
+void		camel_imapx_job_set_user_data	(CamelIMAPXJob *job,
+						 gpointer user_data,
+						 GDestroyNotify destroy_user_data);
+gboolean	camel_imapx_job_was_cancelled	(CamelIMAPXJob *job);
+void		camel_imapx_job_set_result	(CamelIMAPXJob *job,
+						 gboolean success,
+						 gpointer result,
+						 const GError *error,
+						 GDestroyNotify destroy_result);
+gboolean	camel_imapx_job_copy_result	(CamelIMAPXJob *job,
+						 gboolean *out_success,
+						 gpointer *out_result,
+						 GError **out_error,
+						 GDestroyNotify *out_destroy_result);
+gboolean	camel_imapx_job_take_result_data
 						(CamelIMAPXJob *job,
+						 gpointer *out_result);
+gboolean	camel_imapx_job_matches		(CamelIMAPXJob *job,
+						 CamelIMAPXJob *other_job);
+gboolean	camel_imapx_job_run_sync	(CamelIMAPXJob *job,
+						 CamelIMAPXServer *server,
+						 GCancellable *cancellable,
 						 GError **error);
+void		camel_imapx_job_done		(CamelIMAPXJob *job);
+void		camel_imapx_job_abort		(CamelIMAPXJob *job);
+void		camel_imapx_job_wait_sync	(CamelIMAPXJob *job,
+						 GCancellable *cancellable);
 
 G_END_DECLS
 
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.c.imapx-update-to-upstream	2014-05-22 08:45:46.000000000 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.c	2016-08-15 13:52:41.952976330 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-list-response.c
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -147,7 +147,7 @@ imapx_list_response_parse_childinfo (Cam
 		goto fail;
 	if (tok != '(') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"list childinfo: expecting ')'");
 		goto fail;
 	}
@@ -197,7 +197,7 @@ imapx_list_response_parse_oldname (Camel
 		goto fail;
 	if (tok != '(') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"list oldname: expecting ')'");
 		goto fail;
 	}
@@ -214,7 +214,7 @@ imapx_list_response_parse_oldname (Camel
 		goto fail;
 	if (tok != ')') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"list oldname: expecting ')'");
 		goto fail;
 	}
@@ -325,7 +325,7 @@ camel_imapx_list_response_new (CamelIMAP
 		goto fail;
 	if (tok != '(') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"list: expecting '('");
 		goto fail;
 	}
@@ -343,7 +343,7 @@ camel_imapx_list_response_new (CamelIMAP
 		goto fail;
 	if (tok != ')') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"list: expecting ')'");
 		goto fail;
 	}
@@ -414,7 +414,7 @@ extended_item_repeat:
 
 	} else {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"list: expecting '(' or NEWLINE");
 		goto fail;
 	}
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.h.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.h	2016-08-15 13:52:41.953976330 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-list-response.h
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.c.imapx-update-to-upstream	2014-05-22 08:45:46.000000000 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.c	2016-08-15 13:52:41.953976330 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-logger.c
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.h.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.h	2016-08-15 13:52:41.953976330 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-logger.h
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.c.imapx-update-to-upstream	2016-08-15 13:52:41.892976333 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.c	2016-08-15 14:38:22.156860220 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-mailbox.c
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -48,12 +48,13 @@ struct _CamelIMAPXMailboxPrivate {
 	guint64 highestmodseq;
 	guint32 permanentflags;
 
+	volatile gint change_stamp;
+
 	CamelIMAPXMailboxState state;
 
 	GMutex property_lock;
 	GMutex update_lock;
-	GCond update_cond;
-	gboolean update_is_locked;
+	gint update_count;
 
 	/* Protected by the "property_lock". */
 	GHashTable *attributes;
@@ -101,7 +102,6 @@ imapx_mailbox_finalize (GObject *object)
 
 	g_mutex_clear (&priv->property_lock);
 	g_mutex_clear (&priv->update_lock);
-	g_cond_clear (&priv->update_cond);
 	g_hash_table_destroy (priv->attributes);
 	g_sequence_free (priv->message_map);
 	g_strfreev (priv->quota_roots);
@@ -129,11 +129,11 @@ camel_imapx_mailbox_init (CamelIMAPXMail
 
 	g_mutex_init (&mailbox->priv->property_lock);
 	g_mutex_init (&mailbox->priv->update_lock);
-	g_cond_init (&mailbox->priv->update_cond);
-	mailbox->priv->update_is_locked = FALSE;
 	mailbox->priv->message_map = g_sequence_new (NULL);
 	mailbox->priv->permanentflags = ~0;
 	mailbox->priv->state = CAMEL_IMAPX_MAILBOX_STATE_CREATED;
+	mailbox->priv->update_count = 0;
+	mailbox->priv->change_stamp = 0;
 }
 
 /**
@@ -256,7 +256,7 @@ camel_imapx_mailbox_clone (CamelIMAPXMai
  *
  * Returns: Current (update) state of the mailbox.
  *
- * Since: 3.12.9
+ * Since: 3.16
  **/
 CamelIMAPXMailboxState
 camel_imapx_mailbox_get_state (CamelIMAPXMailbox *mailbox)
@@ -275,7 +275,7 @@ camel_imapx_mailbox_get_state (CamelIMAP
  * structure updates, to identify newly created, updated, renamed
  * or removed mailboxes.
  *
- * Since: 3.12.9
+ * Since: 3.16
  **/
 void
 camel_imapx_mailbox_set_state (CamelIMAPXMailbox *mailbox,
@@ -497,7 +497,12 @@ camel_imapx_mailbox_set_messages (CamelI
 {
 	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
 
+	if (mailbox->priv->messages == messages)
+		return;
+
 	mailbox->priv->messages = messages;
+
+	g_atomic_int_add (&mailbox->priv->change_stamp, 1);
 }
 
 /**
@@ -539,7 +544,12 @@ camel_imapx_mailbox_set_recent (CamelIMA
 {
 	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
 
+	if (mailbox->priv->recent == recent)
+		return;
+
 	mailbox->priv->recent = recent;
+
+	g_atomic_int_add (&mailbox->priv->change_stamp, 1);
 }
 
 /**
@@ -583,7 +593,12 @@ camel_imapx_mailbox_set_unseen (CamelIMA
 {
 	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
 
+	if (mailbox->priv->unseen == unseen)
+		return;
+
 	mailbox->priv->unseen = unseen;
+
+	g_atomic_int_add (&mailbox->priv->change_stamp, 1);
 }
 
 /**
@@ -625,7 +640,12 @@ camel_imapx_mailbox_set_uidnext (CamelIM
 {
 	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
 
+	if (mailbox->priv->uidnext == uidnext)
+		return;
+
 	mailbox->priv->uidnext = uidnext;
+
+	g_atomic_int_add (&mailbox->priv->change_stamp, 1);
 }
 
 /**
@@ -667,7 +687,12 @@ camel_imapx_mailbox_set_uidvalidity (Cam
 {
 	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
 
+	if (mailbox->priv->uidvalidity == uidvalidity)
+		return;
+
 	mailbox->priv->uidvalidity = uidvalidity;
+
+	g_atomic_int_add (&mailbox->priv->change_stamp, 1);
 }
 
 /**
@@ -713,7 +738,12 @@ camel_imapx_mailbox_set_highestmodseq (C
 {
 	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
 
+	if (mailbox->priv->highestmodseq == highestmodseq)
+		return;
+
 	mailbox->priv->highestmodseq = highestmodseq;
+
+	g_atomic_int_add (&mailbox->priv->change_stamp, 1);
 }
 
 /**
@@ -723,7 +753,7 @@ camel_imapx_mailbox_set_highestmodseq (C
  * Returns: PERMANENTFLAGS response for the mailbox, or ~0, if the mailbox
  *    was not selected yet.
  *
- * Since: 3.12.8
+ * Since: 3.16
  **/
 guint32
 camel_imapx_mailbox_get_permanentflags (CamelIMAPXMailbox *mailbox)
@@ -740,7 +770,7 @@ camel_imapx_mailbox_get_permanentflags (
  *
  * Updates the last know value for PERMANENTFLAGS for this mailbox.
  *
- * Since: 3.12.8
+ * Since: 3.16
  **/
 void
 camel_imapx_mailbox_set_permanentflags (CamelIMAPXMailbox *mailbox,
@@ -748,6 +778,11 @@ camel_imapx_mailbox_set_permanentflags (
 {
 	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
 
+	if ((permanentflags & CAMEL_MESSAGE_USER) != 0) {
+		permanentflags |= CAMEL_MESSAGE_JUNK;
+		permanentflags |= CAMEL_MESSAGE_NOTJUNK;
+	}
+
 	mailbox->priv->permanentflags = permanentflags;
 }
 
@@ -1179,53 +1214,49 @@ camel_imapx_mailbox_handle_status_respon
 	g_return_if_fail (CAMEL_IS_IMAPX_STATUS_RESPONSE (response));
 
 	if (camel_imapx_status_response_get_messages (response, &value32))
-		mailbox->priv->messages = value32;
+		camel_imapx_mailbox_set_messages (mailbox, value32);
 
 	if (camel_imapx_status_response_get_recent (response, &value32))
-		mailbox->priv->recent = value32;
+		camel_imapx_mailbox_set_recent (mailbox, value32);
 
 	if (camel_imapx_status_response_get_unseen (response, &value32))
-		mailbox->priv->unseen = value32;
+		camel_imapx_mailbox_set_unseen (mailbox, value32);
 
 	if (camel_imapx_status_response_get_uidnext (response, &value32))
-		mailbox->priv->uidnext = value32;
+		camel_imapx_mailbox_set_uidnext (mailbox, value32);
 
 	if (camel_imapx_status_response_get_uidvalidity (response, &value32))
-		mailbox->priv->uidvalidity = value32;
+		camel_imapx_mailbox_set_uidvalidity (mailbox, value32);
 
 	if (camel_imapx_status_response_get_highestmodseq (response, &value64))
-		mailbox->priv->highestmodseq = value64;
+		camel_imapx_mailbox_set_highestmodseq (mailbox, value64);
 }
 
-/* Prevents running FETCH and STORE at the same time for the given mailbox */
-void
-camel_imapx_mailbox_lock_update (CamelIMAPXMailbox *mailbox)
+gint
+camel_imapx_mailbox_get_update_count (CamelIMAPXMailbox *mailbox)
 {
-	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
+	gint res;
 
 	g_mutex_lock (&mailbox->priv->update_lock);
-
-	while (mailbox->priv->update_is_locked) {
-		g_cond_wait (&mailbox->priv->update_cond, &mailbox->priv->update_lock);
-	}
-
-	mailbox->priv->update_is_locked = TRUE;
-
+	res = mailbox->priv->update_count;
 	g_mutex_unlock (&mailbox->priv->update_lock);
+
+	return res;
 }
 
-/* Prevents running FETCH and STORE at the same time for the given mailbox */
 void
-camel_imapx_mailbox_unlock_update (CamelIMAPXMailbox *mailbox)
+camel_imapx_mailbox_inc_update_count (CamelIMAPXMailbox *mailbox,
+				      gint inc)
 {
-	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
-
 	g_mutex_lock (&mailbox->priv->update_lock);
+	mailbox->priv->update_count += inc;
+	g_mutex_unlock (&mailbox->priv->update_lock);
+}
 
-	if (mailbox->priv->update_is_locked) {
-		mailbox->priv->update_is_locked = FALSE;
-		g_cond_signal (&mailbox->priv->update_cond);
-	}
+gint
+camel_imapx_mailbox_get_change_stamp (CamelIMAPXMailbox *mailbox)
+{
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), 0);
 
-	g_mutex_unlock (&mailbox->priv->update_lock);
+	return mailbox->priv->change_stamp;
 }
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.h.imapx-update-to-upstream	2014-11-20 17:14:49.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.h	2016-08-15 14:38:22.157860220 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-mailbox.h
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -175,9 +175,12 @@ void		camel_imapx_mailbox_handle_status_
 					(CamelIMAPXMailbox *mailbox,
 					 CamelIMAPXStatusResponse *response);
 
-void		camel_imapx_mailbox_lock_update
+gint		camel_imapx_mailbox_get_update_count
 					(CamelIMAPXMailbox *mailbox);
-void		camel_imapx_mailbox_unlock_update
+void		camel_imapx_mailbox_inc_update_count
+					(CamelIMAPXMailbox *mailbox,
+					 gint inc);
+gint		camel_imapx_mailbox_get_change_stamp
 					(CamelIMAPXMailbox *mailbox);
 
 G_END_DECLS
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.c.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.c	2016-08-15 13:52:41.957976330 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-namespace.c
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.h.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.h	2016-08-15 13:52:41.957976330 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-namespace.h
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.c.imapx-update-to-upstream	2014-12-02 16:08:21.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.c	2016-08-15 13:52:41.958976330 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-namespace-response.c
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -117,7 +117,7 @@ imapx_namespace_response_parse_namespace
 	}
 	if (tok != '(') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"namespace: expecting NIL or '('");
 		return FALSE;
 	}
@@ -129,7 +129,7 @@ repeat:
 		return FALSE;
 	if (tok != '(') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"namespace: expecting '('");
 		return FALSE;
 	}
@@ -140,7 +140,7 @@ repeat:
 		return FALSE;
 	if (tok != IMAPX_TOK_STRING) {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"namespace: expecting string");
 		return FALSE;
 	}
@@ -169,7 +169,7 @@ repeat:
 		return FALSE;
 	if (tok != ')') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"namespace: expecting ')'");
 		return FALSE;
 	}
@@ -184,7 +184,7 @@ repeat:
 	}
 	if (tok != ')') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"namespace: expecting '(' or ')'");
 		return FALSE;
 	}
@@ -331,7 +331,7 @@ camel_imapx_namespace_response_list (Cam
  * Adds a @namespace into the list of namespaces. It adds its own
  * reference on the @namespace.
  *
- * Since: 3.12.9
+ * Since: 3.16
  **/
 void
 camel_imapx_namespace_response_add (CamelIMAPXNamespaceResponse *response,
@@ -351,7 +351,7 @@ camel_imapx_namespace_response_add (Came
  * Removes @namespace from the list of namespaces in the @response.
  * If no such namespace exists then does nothing.
  *
- * Since: 3.12.9
+ * Since: 3.16
  **/
 void
 camel_imapx_namespace_response_remove (CamelIMAPXNamespaceResponse *response,
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.h.imapx-update-to-upstream	2014-12-02 16:07:55.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.h	2016-08-15 13:52:41.958976330 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-namespace-response.h
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-provider.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-provider.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-provider.c.imapx-update-to-upstream	2014-05-22 08:45:46.000000000 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-provider.c	2016-08-15 13:52:41.958976330 +0200
@@ -1,23 +1,23 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* camel-pop3-provider.c: pop3 provider registration code */
-/*
- * Authors :
- *   Dan Winship <danw@ximian.com>
- *   Michael Zucchi <notzed@ximian.com>
+/* camel-pop3-provider.c: pop3 provider registration code
  *
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
- * This library is free software; you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors :
+ *   Dan Winship <danw@ximian.com>
+ *   Michael Zucchi <notzed@ximian.com>
  */
 
 #ifdef HAVE_CONFIG_H
@@ -69,16 +69,16 @@ CamelProviderConfEntry imapx_conf_entrie
 	{ CAMEL_PROVIDER_CONF_CHECKBOX, "filter-junk", NULL,
 	  N_("Check new messages for _Junk contents"), "0" },
 	{ CAMEL_PROVIDER_CONF_CHECKBOX, "filter-junk-inbox", "filter-junk",
-	  N_("Only check for Junk messages in the IN_BOX folder"), "0" },
+	  N_("Only check for Junk messages in the In_box folder"), "0" },
 	{ CAMEL_PROVIDER_CONF_CHECKBOX, "stay-synchronized", NULL,
-	  N_("Automatically synchroni_ze remote mail locally"), "0" },
+	  N_("Synchroni_ze remote mail locally in all folders"), "0" },
 	{ CAMEL_PROVIDER_CONF_SECTION_END },
 	{ CAMEL_PROVIDER_CONF_END }
 };
 
 CamelProviderPortEntry imapx_port_entries[] = {
 	{ 143, N_("Default IMAP port"), FALSE },
-	{ 993, N_("IMAP over SSL"), TRUE },
+	{ 993, N_("IMAP over TLS"), TRUE },
 	{ 0, NULL, 0 }
 };
 
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.c.imapx-update-to-upstream	2014-06-06 16:08:31.000000000 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.c	2016-08-15 13:52:41.958976330 +0200
@@ -1,22 +1,27 @@
 /*
  * camel-imapx-search.c
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include "camel-imapx-search.h"
 
+#include <string.h>
 #include <camel/camel.h>
 #include <camel/camel-search-private.h>
 
@@ -154,7 +159,9 @@ static CamelSExpResult *
 imapx_search_process_criteria (CamelSExp *sexp,
                                CamelFolderSearch *search,
                                CamelIMAPXStore *imapx_store,
-                               const GString *criteria,
+                               const GString *criteria_prefix,
+			       const gchar *search_key,
+			       const GPtrArray *words,
                                const gchar *from_function)
 {
 	CamelSExpResult *result;
@@ -173,33 +180,17 @@ imapx_search_process_criteria (CamelSExp
 
 	if (mailbox != NULL) {
 		CamelIMAPXStore *imapx_store;
-		CamelIMAPXServer *imapx_server;
-		const gchar *folder_name;
+		CamelIMAPXConnManager *conn_man;
 
 		imapx_store = camel_imapx_search_ref_store (imapx_search);
 
 		/* there should always be one, held by one of the callers of this function */
 		g_warn_if_fail (imapx_store != NULL);
 
-		folder_name = camel_folder_get_full_name (search->folder);
-		imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, TRUE, imapx_search->priv->cancellable, &local_error);
-		if (imapx_server) {
-			uids = camel_imapx_server_uid_search (imapx_server, mailbox, criteria->str, imapx_search->priv->cancellable, &local_error);
-			camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-
-			while (!uids && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-				g_clear_error (&local_error);
-				g_clear_object (&imapx_server);
-
-				imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, TRUE, imapx_search->priv->cancellable, &local_error);
-				if (imapx_server) {
-					uids = camel_imapx_server_uid_search (imapx_server, mailbox, criteria->str, imapx_search->priv->cancellable, &local_error);
-					camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name);
-				}
-			}
-		}
+		conn_man = camel_imapx_store_get_conn_manager (imapx_store);
+		uids = camel_imapx_conn_manager_uid_search_sync (conn_man, mailbox, criteria_prefix->str, search_key,
+			words ? (const gchar * const *) words->pdata : NULL, imapx_search->priv->cancellable, &local_error);
 
-		g_clear_object (&imapx_server);
 		g_clear_object (&imapx_store);
 		g_object_unref (mailbox);
 	}
@@ -296,6 +287,58 @@ imapx_search_match_all (CamelSExp *sexp,
 	return result;
 }
 
+static GPtrArray *
+imapx_search_gather_words (CamelSExpResult **argv,
+			   gint from_index,
+			   gint argc)
+{
+	GPtrArray *ptrs;
+	GHashTable *words_hash;
+	GHashTableIter iter;
+	gpointer key, value;
+	gint ii, jj;
+
+	g_return_val_if_fail (argv != 0, NULL);
+
+	words_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+	for (ii = from_index; ii < argc; ii++) {
+		struct _camel_search_words *words;
+
+		if (argv[ii]->type != CAMEL_SEXP_RES_STRING)
+			continue;
+
+		/* Handle multiple search words within a single term. */
+		words = camel_search_words_split ((const guchar *) argv[ii]->value.string);
+
+		for (jj = 0; jj < words->len; jj++) {
+			const gchar *word = words->words[jj]->word;
+
+			g_hash_table_insert (words_hash, g_strdup (word), NULL);
+		}
+
+		camel_search_words_free (words);
+	}
+
+	ptrs = g_ptr_array_new_full (g_hash_table_size (words_hash), g_free);
+
+	g_hash_table_iter_init (&iter, words_hash);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		g_ptr_array_add (ptrs, g_strdup (key));
+	}
+
+	if (ptrs->len == 0) {
+		g_ptr_array_free (ptrs, TRUE);
+		ptrs = NULL;
+	} else {
+		g_ptr_array_add (ptrs, NULL);
+	}
+
+	g_hash_table_destroy (words_hash);
+
+	return ptrs;
+}
+
 static CamelSExpResult *
 imapx_search_body_contains (CamelSExp *sexp,
                             gint argc,
@@ -306,7 +349,7 @@ imapx_search_body_contains (CamelSExp *s
 	CamelIMAPXStore *imapx_store;
 	CamelSExpResult *result;
 	GString *criteria;
-	gint ii, jj;
+	GPtrArray *words;
 
 	/* Always do body-search server-side */
 	if (imapx_search->priv->local_data_search) {
@@ -343,39 +386,12 @@ imapx_search_body_contains (CamelSExp *s
 		g_string_append_printf (criteria, "UID %s", uid);
 	}
 
-	for (ii = 0; ii < argc; ii++) {
-		struct _camel_search_words *words;
-		const guchar *term;
-
-		if (argv[ii]->type != CAMEL_SEXP_RES_STRING)
-			continue;
-
-		/* Handle multiple search words within a single term. */
-		term = (const guchar *) argv[ii]->value.string;
-		words = camel_search_words_split (term);
-
-		for (jj = 0; jj < words->len; jj++) {
-			gchar *cp;
-
-			if (criteria->len > 0)
-				g_string_append_c (criteria, ' ');
-
-			g_string_append (criteria, "BODY \"");
-
-			cp = words->words[jj]->word;
-			for (; *cp != '\0'; cp++) {
-				if (*cp == '\\' || *cp == '"')
-					g_string_append_c (criteria, '\\');
-				g_string_append_c (criteria, *cp);
-			}
-
-			g_string_append_c (criteria, '"');
-		}
-	}
+	words = imapx_search_gather_words (argv, 0, argc);
 
-	result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, G_STRFUNC);
+	result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, "BODY", words, G_STRFUNC);
 
 	g_string_free (criteria, TRUE);
+	g_ptr_array_free (words, TRUE);
 	g_object_unref (imapx_store);
 
 	return result;
@@ -401,7 +417,8 @@ imapx_search_header_contains (CamelSExp
 	CamelSExpResult *result;
 	const gchar *headername, *command = NULL;
 	GString *criteria;
-	gint ii, jj;
+	gchar *search_key = NULL;
+	GPtrArray *words;
 
 	/* Match nothing if empty argv or empty summary. */
 	if (argc <= 1 ||
@@ -458,45 +475,17 @@ imapx_search_header_contains (CamelSExp
 	else if (g_ascii_strcasecmp (headername, "Subject") == 0)
 		command = "SUBJECT";
 
-	for (ii = 1; ii < argc; ii++) {
-		struct _camel_search_words *words;
-		const guchar *term;
-
-		if (argv[ii]->type != CAMEL_SEXP_RES_STRING)
-			continue;
-
-		/* Handle multiple search words within a single term. */
-		term = (const guchar *) argv[ii]->value.string;
-		words = camel_search_words_split (term);
-
-		for (jj = 0; jj < words->len; jj++) {
-			gchar *cp;
-
-			if (criteria->len > 0)
-				g_string_append_c (criteria, ' ');
+	words = imapx_search_gather_words (argv, 1, argc);
 
-			if (command)
-				g_string_append (criteria, command);
-			else
-				g_string_append_printf (criteria, "HEADER \"%s\"", headername);
-
-			g_string_append (criteria, " \"");
-
-			cp = words->words[jj]->word;
-			for (; *cp != '\0'; cp++) {
-				if (*cp == '\\' || *cp == '"')
-					g_string_append_c (criteria, '\\');
-				g_string_append_c (criteria, *cp);
-			}
-
-			g_string_append_c (criteria, '"');
-		}
-	}
+	if (!command)
+		search_key = g_strdup_printf ("HEADER \"%s\"", headername);
 
-	result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, G_STRFUNC);
+	result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, command ? command : search_key, words, G_STRFUNC);
 
 	g_string_free (criteria, TRUE);
+	g_ptr_array_free (words, TRUE);
 	g_object_unref (imapx_store);
+	g_free (search_key);
 
 	return result;
 }
@@ -578,7 +567,7 @@ imapx_search_header_exists (CamelSExp *s
 		g_string_append_printf (criteria, "HEADER \"%s\" \"\"", headername);
 	}
 
-	result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, G_STRFUNC);
+	result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, NULL, NULL, G_STRFUNC);
 
 	g_string_free (criteria, TRUE);
 	g_object_unref (imapx_store);
@@ -629,7 +618,7 @@ camel_imapx_search_init (CamelIMAPXSearc
 
 /**
  * camel_imapx_search_new:
- * imapx_store: a #CamelIMAPXStore to which the search belongs
+ * @imapx_store: a #CamelIMAPXStore to which the search belongs
  *
  * Returns a new #CamelIMAPXSearch instance.
  *
@@ -716,7 +705,7 @@ camel_imapx_search_set_store (CamelIMAPX
  * for the whole run of the search and reset them both to NULL after
  * the search is finished.
  *
- * Since: 3.14
+ * Since: 3.16
  **/
 void
 camel_imapx_search_set_cancellable_and_error (CamelIMAPXSearch *search,
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.h.imapx-update-to-upstream	2014-05-22 08:45:46.000000000 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.h	2016-08-15 13:52:41.959976330 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-search.h
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.c.imapx-update-to-upstream	2016-08-15 13:52:41.893976333 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.c	2016-08-15 14:38:22.158860220 +0200
@@ -2,30 +2,24 @@
 /*
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
- * This library is free software; you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
  */
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
-/* XXX Disable deprecation warnings until we require GLib 2.40.
- *
- *     This silences warnings for using GSubprocess functions, which are
- *     only available as of GLib 2.39.  But we do so conditionally, with
- *     GLIB_CHECK_VERSION macros. */
-#define GLIB_DISABLE_DEPRECATION_WARNINGS
-
 #include <errno.h>
 #include <fcntl.h>
 #include <time.h>
@@ -35,10 +29,14 @@
 #include <glib/gi18n-lib.h>
 #include <gio/gnetworking.h>
 
+#include <libical/ical.h>
+
 #ifndef G_OS_WIN32
 #include <glib-unix.h>
 #endif /* G_OS_WIN32 */
 
+#include <camel/camel.h>
+
 #include "camel-imapx-server.h"
 
 #include "camel-imapx-folder.h"
@@ -57,21 +55,22 @@
 #define c(...) camel_imapx_debug(command, __VA_ARGS__)
 #define e(...) camel_imapx_debug(extra, __VA_ARGS__)
 
-#define QUEUE_LOCK(x) g_rec_mutex_lock (&(x)->queue_lock)
-#define QUEUE_UNLOCK(x) g_rec_mutex_unlock (&(x)->queue_lock)
+#define COMMAND_LOCK(x) g_rec_mutex_lock (&(x)->priv->command_lock)
+#define COMMAND_UNLOCK(x) g_rec_mutex_unlock (&(x)->priv->command_lock)
 
 /* Try pipelining fetch requests, 'in bits' */
 #define MULTI_SIZE (32768 * 8)
 
-/* How many outstanding commands do we allow before we just queue them? */
-#define MAX_COMMANDS (10)
-
 #define MAX_COMMAND_LEN 1000
 
 /* Ping the server after a period of inactivity to avoid being logged off.
  * Using a 29 minute inactivity timeout as recommended in RFC 2177 (IDLE). */
 #define INACTIVITY_TIMEOUT_SECONDS (29 * 60)
 
+/* Number of seconds to remain in PENDING state waiting for other commands
+   to be queued, before actually sending IDLE */
+#define IMAPX_IDLE_WAIT_SECONDS 2
+
 #ifdef G_OS_WIN32
 #ifdef gmtime_r
 #undef gmtime_r
@@ -83,92 +82,6 @@
 
 G_DEFINE_QUARK (camel-imapx-server-error-quark, camel_imapx_server_error)
 
-extern gint camel_application_is_exiting;
-
-/* Job-specific structs */
-typedef struct _GetMessageData GetMessageData;
-typedef struct _RefreshInfoData RefreshInfoData;
-typedef struct _SyncChangesData SyncChangesData;
-typedef struct _AppendMessageData AppendMessageData;
-typedef struct _CopyMessagesData CopyMessagesData;
-typedef struct _ListData ListData;
-typedef struct _MailboxData MailboxData;
-typedef struct _SearchData SearchData;
-
-struct _GetMessageData {
-	/* in: uid requested */
-	gchar *uid;
-	CamelDataCache *message_cache;
-	/* in/out: message content stream output */
-	GIOStream *stream;
-	/* working variables */
-	gsize body_offset;
-	gsize fetch_offset;
-	gsize size;
-	gboolean use_multi_fetch;
-};
-
-struct _RefreshInfoData {
-	/* array of refresh info's */
-	GArray *infos;
-	/* used for building uidset stuff */
-	gint index;
-	gint last_index;
-	CamelFetchType fetch_type;
-	gboolean update_unseen;
-	gboolean scan_changes;
-	struct _uidset_state uidset;
-	/* changes during refresh */
-	CamelFolderChangeInfo *changes;
-};
-
-struct _SyncChangesData {
-	CamelFolder *folder;
-	GPtrArray *changed_uids;
-	gboolean own_allocated_changed_uids;
-	guint32 on_set;
-	guint32 off_set;
-	GArray *on_user; /* imapx_flag_change */
-	GArray *off_user;
-	gint unread_change;
-
-	/* Remove recently set DELETED flags before synchronizing.
-	 * This is only set when using a real Trash folder and NOT
-	 * about to expunge the folder. */
-	gboolean remove_deleted_flags;
-};
-
-struct _AppendMessageData {
-	gchar *path;
-	CamelMessageInfo *info;
-	gchar *appended_uid;
-	time_t date_time; /* message's date/time, in UTC */
-};
-
-struct _CopyMessagesData {
-	CamelIMAPXMailbox *destination;
-	GPtrArray *uids;
-	gboolean delete_originals;
-	gboolean use_move_command;
-	gint index;
-	gint last_index;
-	struct _uidset_state uidset;
-};
-
-struct _ListData {
-	gchar *pattern;
-};
-
-struct _MailboxData {
-	CamelIMAPXMailbox *mailbox;
-	gchar *mailbox_name;
-};
-
-struct _SearchData {
-	gchar *criteria;
-	GArray *results;
-};
-
 /* untagged response handling */
 
 /* May need to turn this into separate,
@@ -314,29 +227,16 @@ static const CamelIMAPXUntaggedRespHandl
 };
 
 typedef enum {
-	IMAPX_IDLE_OFF,
-	IMAPX_IDLE_PENDING,	/* Queue is idle; waiting to send IDLE command
-				   soon if nothing more interesting happens */
-	IMAPX_IDLE_ISSUED,	/* Sent IDLE command; waiting for response */
-	IMAPX_IDLE_STARTED,	/* IDLE continuation received; IDLE active */
-	IMAPX_IDLE_CANCEL,	/* Cancelled from ISSUED state; need to send
-				   DONE as soon as we receive continuation */
-	IMAPX_IDLE_WAIT_DONE	/* DONE was issued, waiting for a confirmation response */
-} CamelIMAPXIdleState;
-
-#define IMAPX_IDLE_DWELL_TIME	2 /* Number of seconds to remain in PENDING
-				     state waiting for other commands to be
-				     queued, before actually sending IDLE */
-
-typedef enum {
-	IMAPX_IDLE_STOP_NOOP,
-	IMAPX_IDLE_STOP_WAIT_DONE,
-	IMAPX_IDLE_STOP_SUCCESS,
-	IMAPX_IDLE_STOP_ERROR
-} CamelIMAPXIdleStopResult;
+	IMAPX_IDLE_STATE_OFF,       /* no IDLE running at all */
+	IMAPX_IDLE_STATE_SCHEDULED, /* IDLE scheduled, but still waiting */
+	IMAPX_IDLE_STATE_PREPARING, /* IDLE command going to be processed */
+	IMAPX_IDLE_STATE_RUNNING,   /* IDLE command had been processed, server responded */
+	IMAPX_IDLE_STATE_STOPPING   /* DONE had been issued, waiting for completion */
+} IMAPXIdleState;
 
 struct _CamelIMAPXServerPrivate {
 	GWeakRef store;
+	GCancellable *cancellable; /* the main connection cancellable, it's cancelled on disconnect */
 
 	CamelIMAPXServerUntaggedContext *context;
 	GHashTable *untagged_handlers;
@@ -345,27 +245,19 @@ struct _CamelIMAPXServerPrivate {
 	GInputStream *input_stream;
 	GOutputStream *output_stream;
 	GIOStream *connection;
-#if GLIB_CHECK_VERSION(2,39,0)
 	GSubprocess *subprocess;
-#endif
 	GMutex stream_lock;
 
-	GThread *parser_thread;
-	GMainLoop *parser_main_loop;
-	GMainContext *parser_main_context;
-	GWeakRef parser_cancellable;
-
-	GMutex shutdown_error_lock;
-	GError *shutdown_error;
-
 	GSource *inactivity_timeout;
 	GMutex inactivity_timeout_lock;
 
 	/* Info on currently selected folder. */
 	GMutex select_lock;
 	GWeakRef select_mailbox;
-	GWeakRef select_closing;
 	GWeakRef select_pending;
+	gint last_selected_mailbox_change_stamp;
+
+	GMutex changes_lock;
 	CamelFolderChangeInfo *changes;
 
 	/* Data items to request in STATUS commands:
@@ -392,17 +284,38 @@ struct _CamelIMAPXServerPrivate {
 	gchar inbox_separator;
 
 	/* IDLE support */
-	GRecMutex idle_lock;
-	GThread *idle_thread;
-	GMainLoop *idle_main_loop;
-	GMainContext *idle_main_context;
+	GMutex idle_lock;
+	GCond idle_cond;
+	IMAPXIdleState idle_state;
 	GSource *idle_pending;
-	CamelIMAPXIdleState idle_state;
+	CamelIMAPXMailbox *idle_mailbox;
+	GCancellable *idle_cancellable;
+	guint idle_stamp;
+
+	gboolean is_cyrus;
+
+	/* Info about the current connection; guarded by priv->stream_lock */
+	struct _capability_info *cinfo;
 
-	GMutex jobs_prop_lock;
-	GHashTable *jobs_prop_folder_paths;
-	gint jobs_prop_command_count; /* without IDLE command */
-	gint jobs_prop_expensive_command_count;
+	GRecMutex command_lock;
+
+	gchar tagprefix;
+	guint32 state;
+
+	gboolean use_qresync;
+
+	CamelIMAPXCommand *current_command;
+	CamelIMAPXCommand *continuation_command;
+
+	/* operation data */
+	GIOStream *get_message_stream;
+
+	CamelIMAPXMailbox *fetch_changes_mailbox; /* not referenced */
+	CamelFolder *fetch_changes_folder; /* not referenced */
+	GHashTable *fetch_changes_infos; /* gchar *uid ~> FetchChangesInfo-s */
+	gint64 fetch_changes_last_progress; /* when was called last progress */
+
+	struct _status_info *copyuid_status;
 };
 
 enum {
@@ -411,25 +324,12 @@ enum {
 };
 
 enum {
-	MAILBOX_SELECT,
-	MAILBOX_CLOSED,
-	SHUTDOWN,
+	REFRESH_MAILBOX,
 	LAST_SIGNAL
 };
 
 static guint signals[LAST_SIGNAL];
 
-static void	imapx_uidset_init		(struct _uidset_state *ss,
-						 gint total,
-						 gint limit);
-static gint	imapx_uidset_done		(struct _uidset_state *ss,
-						 CamelIMAPXCommand *ic);
-static gint	imapx_uidset_add		(struct _uidset_state *ss,
-						 CamelIMAPXCommand *ic,
-						 const gchar *uid);
-
-static gboolean	imapx_command_idle_stop		(CamelIMAPXServer *is,
-						 GError **error);
 static gboolean	imapx_continuation		(CamelIMAPXServer *is,
 						 GInputStream *input_stream,
 						 GOutputStream *output_stream,
@@ -437,11 +337,6 @@ static gboolean	imapx_continuation		(Cam
 						 GCancellable *cancellable,
 						 GError **error);
 static void	imapx_disconnect		(CamelIMAPXServer *is);
-static gboolean	imapx_is_command_queue_empty	(CamelIMAPXServer *is);
-static gint	imapx_uid_cmp			(gconstpointer ap,
-						 gconstpointer bp,
-						 gpointer data);
-static void	imapx_command_start_next	(CamelIMAPXServer *is);
 
 /* states for the connection? */
 enum {
@@ -453,105 +348,64 @@ enum {
 	IMAPX_SELECTED
 };
 
-struct _refresh_info {
-	gchar *uid;
-	gboolean exists;
-	guint32 server_flags;
-	CamelFlag *server_user_flags;
-};
-
-enum {
-	IMAPX_JOB_GET_MESSAGE = 1 << 0,
-	IMAPX_JOB_APPEND_MESSAGE = 1 << 1,
-	IMAPX_JOB_COPY_MESSAGE = 1 << 2,
-	IMAPX_JOB_FETCH_NEW_MESSAGES = 1 << 3,
-	IMAPX_JOB_REFRESH_INFO = 1 << 4,
-	IMAPX_JOB_SYNC_CHANGES = 1 << 5,
-	IMAPX_JOB_EXPUNGE = 1 << 6,
-	IMAPX_JOB_NOOP = 1 << 7,
-	IMAPX_JOB_IDLE = 1 << 8,
-	IMAPX_JOB_LIST = 1 << 9,
-	IMAPX_JOB_CREATE_MAILBOX = 1 << 10,
-	IMAPX_JOB_DELETE_MAILBOX = 1 << 11,
-	IMAPX_JOB_RENAME_MAILBOX = 1 << 12,
-	IMAPX_JOB_SUBSCRIBE_MAILBOX = 1 << 13,
-	IMAPX_JOB_UNSUBSCRIBE_MAILBOX = 1 << 14,
-	IMAPX_JOB_UPDATE_QUOTA_INFO = 1 << 15,
-	IMAPX_JOB_UID_SEARCH = 1 << 16
-};
-
-/* Mailbox management operations have highest priority
- * since we know for sure that they are user triggered. */
-enum {
-	IMAPX_PRIORITY_MAILBOX_MGMT = 200,
-	IMAPX_PRIORITY_SYNC_CHANGES = 150,
-	IMAPX_PRIORITY_EXPUNGE = 150,
-	IMAPX_PRIORITY_SEARCH = 150,
-	IMAPX_PRIORITY_GET_MESSAGE = 100,
-	IMAPX_PRIORITY_REFRESH_INFO = 0,
-	IMAPX_PRIORITY_NOOP = 0,
-	IMAPX_PRIORITY_NEW_MESSAGES = 0,
-	IMAPX_PRIORITY_APPEND_MESSAGE = -60,
-	IMAPX_PRIORITY_COPY_MESSAGE = -60,
-	IMAPX_PRIORITY_LIST = -80,
-	IMAPX_PRIORITY_IDLE = -100,
-	IMAPX_PRIORITY_SYNC_MESSAGE = -120,
-	IMAPX_PRIORITY_UPDATE_QUOTA_INFO = -80
-};
-
 struct _imapx_flag_change {
 	GPtrArray *infos;
 	gchar *name;
 };
 
-static CamelIMAPXJob *
-		imapx_match_active_job		(CamelIMAPXServer *is,
-						 guint32 type,
-						 const gchar *uid);
-static gboolean	imapx_job_fetch_new_messages_start
-						(CamelIMAPXJob *job,
-						 CamelIMAPXServer *is,
-						 GCancellable *cancellable,
-						 GError **error);
 static gint	imapx_refresh_info_uid_cmp	(gconstpointer ap,
 						 gconstpointer bp,
 						 gboolean ascending);
 static gint	imapx_uids_array_cmp		(gconstpointer ap,
 						 gconstpointer bp);
-static gboolean	imapx_server_sync_changes	(CamelIMAPXServer *is,
-						 CamelIMAPXMailbox *mailbox,
-						 guint32 job_type,
-						 gint pri,
-						 GCancellable *cancellable,
-						 GError **error);
 static void	imapx_sync_free_user		(GArray *user_set);
 
-static gboolean	imapx_command_copy_messages_step_start
-						(CamelIMAPXServer *is,
-						 CamelIMAPXJob *job,
-						 gint index,
-						 GError **error);
-static gboolean	imapx_job_noop_start		(CamelIMAPXJob *job,
-						 CamelIMAPXServer *is,
-						 GCancellable *cancellable,
-						 GError **error);
+G_DEFINE_TYPE (CamelIMAPXServer, camel_imapx_server, G_TYPE_OBJECT)
 
-static gboolean	imapx_in_idle			(CamelIMAPXServer *is);
-static gboolean	imapx_use_idle		(CamelIMAPXServer *is);
-static void	imapx_start_idle		(CamelIMAPXServer *is);
-static CamelIMAPXIdleStopResult
-		imapx_stop_idle			(CamelIMAPXServer *is,
-						 GError **error);
-static gboolean	camel_imapx_server_idle		(CamelIMAPXServer *is,
-						 CamelIMAPXMailbox *mailbox,
-						 GCancellable *cancellable,
-						 GError **error);
+/**
+ * camel_binding_bind_property:
+ *
+ * Thread safe variant of g_object_bind_property(). See its documentation
+ * for more information on arguments and return value.
+ *
+ * Returns: (transfer none):
+ *
+ * Since: 3.16
+ **/
+static GBinding *
+camel_binding_bind_property (gpointer source,
+			     const gchar *source_property,
+			     gpointer target,
+			     const gchar *target_property,
+			     GBindingFlags flags)
+{
+	static GRecMutex camel_binding_lock;
+	GBinding *binding;
 
-static void	imapx_maybe_select		(CamelIMAPXServer *is,
-						 CamelIMAPXJob *job,
-						 CamelIMAPXMailbox *mailbox);
+	g_rec_mutex_lock (&camel_binding_lock);
 
-G_DEFINE_TYPE (CamelIMAPXServer, camel_imapx_server, G_TYPE_OBJECT)
+	binding = g_object_bind_property (source, source_property, target, target_property, flags);
+
+	g_rec_mutex_unlock (&camel_binding_lock);
+
+	return binding;
+}
+
+typedef struct _FetchChangesInfo {
+	guint32 server_flags;
+	CamelFlag *server_user_flags;
+} FetchChangesInfo;
+
+static void
+fetch_changes_info_free (gpointer ptr)
+{
+	FetchChangesInfo *nfo = ptr;
+
+	if (nfo) {
+		camel_flag_list_free (&nfo->server_user_flags);
+		g_free (nfo);
+	}
+}
 
 static GWeakRef *
 imapx_weak_ref_new (gpointer object)
@@ -579,169 +433,6 @@ imapx_weak_ref_free (GWeakRef *weak_ref)
 	g_slice_free (GWeakRef, weak_ref);
 }
 
-static void
-imapx_server_set_shutdown_error (CamelIMAPXServer *imapx_server,
-				 const GError *error)
-{
-	g_mutex_lock (&imapx_server->priv->shutdown_error_lock);
-
-	if (error != imapx_server->priv->shutdown_error) {
-		g_clear_error (&imapx_server->priv->shutdown_error);
-		if (error)
-			imapx_server->priv->shutdown_error = g_error_copy (error);
-	}
-
-	g_mutex_unlock (&imapx_server->priv->shutdown_error_lock);
-}
-
-static GError *
-imapx_server_dup_shutdown_error (CamelIMAPXServer *imapx_server)
-{
-	GError *error = NULL;
-
-	g_mutex_lock (&imapx_server->priv->shutdown_error_lock);
-
-	if (imapx_server->priv->shutdown_error)
-		error = g_error_copy (imapx_server->priv->shutdown_error);
-
-	g_mutex_unlock (&imapx_server->priv->shutdown_error_lock);
-
-	return error;
-}
-
-static void
-imapx_server_command_added (CamelIMAPXServer *imapx_server,
-			    CamelIMAPXCommand *command)
-{
-	CamelIMAPXJob *job;
-
-	g_return_if_fail (command != NULL);
-
-	g_mutex_lock (&imapx_server->priv->jobs_prop_lock);
-
-	job = camel_imapx_command_get_job (command);
-
-	if (job) {
-		/* without IDLE commands */
-		if (!(job->type & IMAPX_JOB_IDLE))
-			imapx_server->priv->jobs_prop_command_count++;
-
-		if ((job->type & (IMAPX_JOB_FETCH_NEW_MESSAGES | IMAPX_JOB_REFRESH_INFO)) != 0)
-			imapx_server->priv->jobs_prop_expensive_command_count++;
-	}
-
-	g_mutex_unlock (&imapx_server->priv->jobs_prop_lock);
-}
-
-static void
-imapx_server_command_removed (CamelIMAPXServer *imapx_server,
-			      CamelIMAPXCommand *command)
-{
-	CamelIMAPXJob *job;
-
-	g_return_if_fail (command != NULL);
-
-	g_mutex_lock (&imapx_server->priv->jobs_prop_lock);
-
-	job = camel_imapx_command_get_job (command);
-
-	if (job) {
-		/* without IDLE commands */
-		if (!(job->type & IMAPX_JOB_IDLE)) {
-			imapx_server->priv->jobs_prop_command_count--;
-			g_warn_if_fail (imapx_server->priv->jobs_prop_command_count >= 0);
-		}
-
-		if ((job->type & (IMAPX_JOB_FETCH_NEW_MESSAGES | IMAPX_JOB_REFRESH_INFO)) != 0) {
-			imapx_server->priv->jobs_prop_expensive_command_count--;
-			g_warn_if_fail (imapx_server->priv->jobs_prop_expensive_command_count >= 0);
-		}
-	}
-
-	g_mutex_unlock (&imapx_server->priv->jobs_prop_lock);
-}
-
-static void
-imapx_server_add_job_mailbox (CamelIMAPXServer *imapx_server,
-			      CamelIMAPXMailbox *mailbox)
-{
-	gchar *folder_path;
-	gint n_stored;
-
-	g_return_if_fail (mailbox != NULL);
-
-	g_mutex_lock (&imapx_server->priv->jobs_prop_lock);
-
-	folder_path = camel_imapx_mailbox_dup_folder_path (mailbox);
-
-	n_stored = GPOINTER_TO_INT (g_hash_table_lookup (imapx_server->priv->jobs_prop_folder_paths, folder_path));
-	/* takes ownership of folder_path */
-	g_hash_table_insert (imapx_server->priv->jobs_prop_folder_paths, folder_path, GINT_TO_POINTER (n_stored + 1));
-
-	g_mutex_unlock (&imapx_server->priv->jobs_prop_lock);
-}
-
-static void
-imapx_server_remove_job_mailbox (CamelIMAPXServer *imapx_server,
-				 CamelIMAPXMailbox *mailbox)
-{
-	gchar *folder_path;
-	gint n_stored;
-
-	g_return_if_fail (mailbox != NULL);
-
-	g_mutex_lock (&imapx_server->priv->jobs_prop_lock);
-
-	folder_path = camel_imapx_mailbox_dup_folder_path (mailbox);
-
-	n_stored = GPOINTER_TO_INT (g_hash_table_lookup (imapx_server->priv->jobs_prop_folder_paths, folder_path));
-	if (!camel_imapx_mailbox_is_inbox (camel_imapx_mailbox_get_name (mailbox)))
-		g_warn_if_fail (n_stored >= 1);
-
-	n_stored--;
-	if (n_stored > 0) {
-		/* takes ownership of folder_path */
-		g_hash_table_insert (imapx_server->priv->jobs_prop_folder_paths, folder_path, GINT_TO_POINTER (n_stored));
-	} else {
-		g_hash_table_remove (imapx_server->priv->jobs_prop_folder_paths, folder_path);
-		g_free (folder_path);
-	}
-
-	g_mutex_unlock (&imapx_server->priv->jobs_prop_lock);
-}
-
-static void
-imapx_server_job_added (CamelIMAPXServer *imapx_server,
-			CamelIMAPXJob *job)
-{
-	CamelIMAPXMailbox *mailbox;
-
-	g_return_if_fail (job != NULL);
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-
-	if (mailbox != NULL) {
-		imapx_server_add_job_mailbox (imapx_server, mailbox);
-		g_object_unref (mailbox);
-	}
-}
-
-static void
-imapx_server_job_removed (CamelIMAPXServer *imapx_server,
-			  CamelIMAPXJob *job)
-{
-	CamelIMAPXMailbox *mailbox;
-
-	g_return_if_fail (job != NULL);
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-
-	if (mailbox != NULL) {
-		imapx_server_remove_job_mailbox (imapx_server, mailbox);
-		g_object_unref (mailbox);
-	}
-}
-
 static const CamelIMAPXUntaggedRespHandlerDesc *
 replace_untagged_descriptor (GHashTable *untagged_handlers,
                              const gchar *key,
@@ -799,125 +490,19 @@ create_initial_untagged_handler_table (v
 	return uh;
 }
 
-static void
-get_message_data_free (GetMessageData *data)
-{
-	g_free (data->uid);
-
-	g_clear_object (&data->message_cache);
-	g_clear_object (&data->stream);
-
-	g_slice_free (GetMessageData, data);
-}
-
-static void
-refresh_info_data_infos_free (RefreshInfoData *data)
-{
-	gint ii;
-
-	if (!data || !data->infos)
-		return;
-
-	for (ii = 0; ii < data->infos->len; ii++) {
-		struct _refresh_info *r = &g_array_index (data->infos, struct _refresh_info, ii);
-
-		camel_flag_list_free (&r->server_user_flags);
-		g_free (r->uid);
-	}
-
-	g_array_free (data->infos, TRUE);
-	data->infos = NULL;
-}
-
-static void
-refresh_info_data_free (RefreshInfoData *data)
-{
-	if (data->changes != NULL)
-		camel_folder_change_info_free (data->changes);
-
-	refresh_info_data_infos_free (data);
-
-	g_slice_free (RefreshInfoData, data);
-}
-
-static void
-sync_changes_data_free (SyncChangesData *data)
-{
-	if (data->folder != NULL) {
-		if (!data->own_allocated_changed_uids)
-			camel_folder_free_uids (data->folder, data->changed_uids);
-		g_object_unref (data->folder);
-	}
-
-	if (data->own_allocated_changed_uids && data->changed_uids) {
-		g_ptr_array_foreach (data->changed_uids, (GFunc) camel_pstring_free, NULL);
-		g_ptr_array_free (data->changed_uids, TRUE);
-	}
-
-	imapx_sync_free_user (data->on_user);
-	imapx_sync_free_user (data->off_user);
-
-	g_slice_free (SyncChangesData, data);
-}
-
-static void
-append_message_data_free (AppendMessageData *data)
-{
-	g_free (data->path);
-	g_free (data->appended_uid);
-
-	camel_message_info_unref (data->info);
-
-	g_slice_free (AppendMessageData, data);
-}
-
-static void
-copy_messages_data_free (CopyMessagesData *data)
-{
-	g_clear_object (&data->destination);
-
-	if (data->uids != NULL) {
-		g_ptr_array_foreach (data->uids, (GFunc) g_free, NULL);
-		g_ptr_array_free (data->uids, TRUE);
-	}
-
-	g_slice_free (CopyMessagesData, data);
-}
-
-static void
-list_data_free (ListData *data)
-{
-	g_free (data->pattern);
-
-	g_slice_free (ListData, data);
-}
-
-static void
-mailbox_data_free (MailboxData *data)
-{
-	g_clear_object (&data->mailbox);
-	g_free (data->mailbox_name);
-
-	g_slice_free (MailboxData, data);
-}
-
-static void
-search_data_free (SearchData *data)
-{
-	g_free (data->criteria);
-
-	if (data->results != NULL)
-		g_array_unref (data->results);
-
-	g_slice_free (SearchData, data);
-}
+struct _uidset_state {
+	gint entries, uids;
+	gint total, limit;
+	guint32 start;
+	guint32 last;
+};
 
 /*
   this creates a uid (or sequence number) set directly into a command,
   if total is set, then we break it up into total uids. (i.e. command time)
   if limit is set, then we break it up into limit entries (i.e. command length)
 */
-void
+static void
 imapx_uidset_init (struct _uidset_state *ss,
                    gint total,
                    gint limit)
@@ -930,14 +515,19 @@ imapx_uidset_init (struct _uidset_state
 	ss->limit = limit;
 }
 
-gboolean
+static gboolean
 imapx_uidset_done (struct _uidset_state *ss,
                    CamelIMAPXCommand *ic)
 {
 	gint ret = FALSE;
 
-	if (ss->last != 0 && ss->last != ss->start) {
-		camel_imapx_command_add (ic, ":%d", ss->last);
+	if (ss->last != 0) {
+		if (ss->entries > 0)
+			camel_imapx_command_add (ic, ",");
+		if (ss->last == ss->start)
+			camel_imapx_command_add (ic, "%d", ss->last);
+		else
+			camel_imapx_command_add (ic, "%d:%d", ss->start, ss->last);
 	}
 
 	ret = ss->last != 0;
@@ -950,7 +540,7 @@ imapx_uidset_done (struct _uidset_state
 	return ret;
 }
 
-gint
+static gint
 imapx_uidset_add (struct _uidset_state *ss,
                   CamelIMAPXCommand *ic,
                   const gchar *uid)
@@ -963,33 +553,41 @@ imapx_uidset_add (struct _uidset_state *
 
 	ss->uids++;
 
-	e (ic->is->tagprefix, "uidset add '%s'\n", uid);
+	e (ic->is->priv->tagprefix, "uidset add '%s'\n", uid);
 
 	if (ss->last == 0) {
-		e (ic->is->tagprefix, " start\n");
-		camel_imapx_command_add (ic, "%d", uidn);
-		ss->entries++;
+		e (ic->is->priv->tagprefix, " start\n");
 		ss->start = uidn;
+		ss->last = uidn;
 	} else {
-		if (ss->last != uidn - 1) {
-			if (ss->last == ss->start) {
-				e (ic->is->tagprefix, " ,next\n");
-				camel_imapx_command_add (ic, ",%d", uidn);
-				ss->entries++;
-			} else {
-				e (ic->is->tagprefix, " :range\n");
-				camel_imapx_command_add (ic, ":%d,%d", ss->last, uidn);
-				ss->entries+=2;
-			}
+		if (ss->start - 1 == uidn) {
 			ss->start = uidn;
+		} else {
+			if (ss->last != uidn - 1) {
+				if (ss->last == ss->start) {
+					e (ic->is->priv->tagprefix, " ,next\n");
+					if (ss->entries > 0)
+						camel_imapx_command_add (ic, ",");
+					camel_imapx_command_add (ic, "%d", ss->start);
+					ss->entries++;
+				} else {
+					e (ic->is->priv->tagprefix, " :range\n");
+					if (ss->entries > 0)
+						camel_imapx_command_add (ic, ",");
+					camel_imapx_command_add (ic, "%d:%d", ss->start, ss->last);
+					ss->entries += 2;
+				}
+				ss->start = uidn;
+			}
+
+			ss->last = uidn;
 		}
 	}
 
-	ss->last = uidn;
-
 	if ((ss->limit && ss->entries >= ss->limit)
+	    || (ss->limit && ss->uids >= ss->limit)
 	    || (ss->total && ss->uids >= ss->total)) {
-		e (ic->is->tagprefix, " done, %d entries, %d uids\n", ss->entries, ss->uids);
+		e (ic->is->priv->tagprefix, " done, %d entries, %d uids\n", ss->entries, ss->uids);
 		if (!imapx_uidset_done (ss, ic))
 			return -1;
 		return 1;
@@ -998,74 +596,9 @@ imapx_uidset_add (struct _uidset_state *
 	return 0;
 }
 
-static gboolean
-imapx_register_job (CamelIMAPXServer *is,
-                    CamelIMAPXJob *job,
-                    GError **error)
-{
-	if (is->state >= IMAPX_INITIALISED) {
-		QUEUE_LOCK (is);
-		g_queue_push_head (&is->jobs, camel_imapx_job_ref (job));
-		imapx_server_job_added (is, job);
-		QUEUE_UNLOCK (is);
-
-	} else if (is->state <= IMAPX_SHUTDOWN) {
-		e (is->tagprefix, "Server is shutdown/disconnected, try reconnect.");
-		g_set_error (error,
-			CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT,
-			_("Not authenticated"));
-		return FALSE;
-	} else {
-		e (is->tagprefix, "Not connected yet, maybe user cancelled jobs earlier?");
-		g_set_error (
-			error, CAMEL_SERVICE_ERROR,
-			CAMEL_SERVICE_ERROR_NOT_CONNECTED,
-			_("Not authenticated"));
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static void
-imapx_unregister_job (CamelIMAPXServer *is,
-                      CamelIMAPXJob *job)
-{
-	camel_imapx_job_done (job);
-
-	QUEUE_LOCK (is);
-
-	if (g_queue_remove (&is->jobs, job)) {
-		imapx_server_job_removed (is, job);
-		camel_imapx_job_unref (job);
-	}
-
-	imapx_command_start_next (is);
-
-	QUEUE_UNLOCK (is);
-}
-
-static gboolean
-imapx_submit_job (CamelIMAPXServer *is,
-                  CamelIMAPXJob *job,
-                  GError **error)
-{
-	gboolean success;
-
-	if (!imapx_register_job (is, job, error))
-		return FALSE;
-
-	success = camel_imapx_job_run (job, is, error);
-
-	if (!success)
-		imapx_unregister_job (is, job);
-
-	return success;
-}
-
-static CamelFolder *
-imapx_server_ref_folder (CamelIMAPXServer *is,
-                         CamelIMAPXMailbox *mailbox)
+static CamelFolder *
+imapx_server_ref_folder (CamelIMAPXServer *is,
+                         CamelIMAPXMailbox *mailbox)
 {
 	CamelFolder *folder;
 	CamelIMAPXStore *store;
@@ -1106,19 +639,19 @@ imapx_server_stash_command_arguments (Ca
 	/* Stash some reusable capability-based command arguments. */
 
 	buffer = g_string_new ("MESSAGES UNSEEN UIDVALIDITY UIDNEXT");
-	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, CONDSTORE))
+	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, CONDSTORE))
 		g_string_append (buffer, " HIGHESTMODSEQ");
 	g_free (is->priv->status_data_items);
 	is->priv->status_data_items = g_string_free (buffer, FALSE);
 
 	g_free (is->priv->list_return_opts);
-	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, LIST_EXTENDED)) {
+	if (!is->priv->is_cyrus && CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, LIST_EXTENDED)) {
 		buffer = g_string_new ("CHILDREN SUBSCRIBED");
-		if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, LIST_STATUS))
+		if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, LIST_STATUS))
 			g_string_append_printf (
 				buffer, " STATUS (%s)",
 				is->priv->status_data_items);
-		if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, SPECIAL_USE))
+		if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, SPECIAL_USE))
 			g_string_append_printf (buffer, " SPECIAL-USE");
 		is->priv->list_return_opts = g_string_free (buffer, FALSE);
 	} else {
@@ -1126,73 +659,57 @@ imapx_server_stash_command_arguments (Ca
 	}
 }
 
-static gboolean
-imapx_server_inactivity_timeout_cb (gpointer data)
+static gpointer
+imapx_server_inactivity_thread (gpointer user_data)
 {
-	CamelIMAPXServer *is;
-	gboolean result = G_SOURCE_REMOVE;
-
-	is = g_weak_ref_get (data);
-
-	if (is == NULL)
-		return result;
-
-	/* IDLE command may still be active, and any other active
-	 * commands would have reset this timeout.  So just check
-	 * for any queued-but-not-yet-active commands. */
-
-	if (!camel_imapx_command_queue_is_empty (is->queue)) {
-		/* Do nothing. */
+	CamelIMAPXServer *is = user_data;
+	GError *local_error = NULL;
 
-	} else if (imapx_in_idle (is)) {
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
 
+	if (camel_imapx_server_is_in_idle (is)) {
 		/* Stop and restart the IDLE command. */
-		switch (imapx_stop_idle (is, NULL)) {
-			case IMAPX_IDLE_STOP_SUCCESS:
-				imapx_start_idle (is);
-				result = G_SOURCE_CONTINUE;
-				break;
-
-			case IMAPX_IDLE_STOP_WAIT_DONE:
-			case IMAPX_IDLE_STOP_NOOP:
-				result = G_SOURCE_CONTINUE;
-				break;
-
-			default:
-				break;
-		}
-
+		if (!camel_imapx_server_schedule_idle_sync (is, NULL, is->priv->cancellable, &local_error) &&
+		    !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+			camel_imapx_debug (io, camel_imapx_server_get_tagprefix (is),
+				"%s: Failed to restart IDLE: %s\n", G_STRFUNC, local_error ? local_error->message : "Unknown error");
 	} else {
-		CamelIMAPXJob *job;
-		GCancellable *cancellable;
-		GError *local_error = NULL;
-
-		/* Submit a NOOP job but indicate we don't need a
-		 * reply when finished.  So this should NOT block. */
+		if (!camel_imapx_server_noop_sync (is, NULL, is->priv->cancellable, &local_error) &&
+		    !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+			camel_imapx_debug (io, camel_imapx_server_get_tagprefix (is),
+				"%s: Failed to issue NOOP: %s\n", G_STRFUNC, local_error ? local_error->message : "Unknown error");
+	}
 
-		cancellable = g_weak_ref_get (&is->priv->parser_cancellable);
+	g_clear_error (&local_error);
+	g_object_unref (is);
 
-		job = camel_imapx_job_new (cancellable);
-		job->type = IMAPX_JOB_NOOP;
-		job->start = imapx_job_noop_start;
-		job->pri = IMAPX_PRIORITY_NOOP;
-		job->noreply = TRUE;
+	return NULL;
+}
 
-		imapx_submit_job (is, job, &local_error);
+static gboolean
+imapx_server_inactivity_timeout_cb (gpointer data)
+{
+	CamelIMAPXServer *is;
+	GThread *thread;
+	GError *local_error = NULL;
 
-		if (local_error != NULL) {
-			g_warning ("%s: %s", G_STRFUNC, local_error->message);
-			g_error_free (local_error);
-		}
+	is = g_weak_ref_get (data);
 
-		camel_imapx_job_unref (job);
+	if (is == NULL)
+		return G_SOURCE_REMOVE;
 
-		g_clear_object (&cancellable);
+	thread = g_thread_try_new (NULL, imapx_server_inactivity_thread, g_object_ref (is), &local_error);
+	if (!thread) {
+		g_warning ("%s: Failed to start inactivity thread: %s", G_STRFUNC, local_error ? local_error->message : "Unknown error");
+		g_object_unref (is);
+	} else {
+		g_thread_unref (thread);
 	}
 
+	g_clear_error (&local_error);
 	g_object_unref (is);
 
-	return result;
+	return G_SOURCE_REMOVE;
 }
 
 static void
@@ -1212,9 +729,7 @@ imapx_server_reset_inactivity_timer (Cam
 		imapx_server_inactivity_timeout_cb,
 		imapx_weak_ref_new (is),
 		(GDestroyNotify) imapx_weak_ref_free);
-	g_source_attach (
-		is->priv->inactivity_timeout,
-		is->priv->parser_main_context);
+	g_source_attach (is->priv->inactivity_timeout, NULL);
 
 	g_mutex_unlock (&is->priv->inactivity_timeout_lock);
 }
@@ -1226,8 +741,21 @@ imapx_server_set_connection_timeout (GIO
 	GSocket *socket;
 	gint previous_timeout = -1;
 
-	if (!G_IS_SOCKET_CONNECTION (connection))
+	if (G_IS_TLS_CONNECTION (connection)) {
+		GIOStream *base_io_stream = NULL;
+
+		g_object_get (G_OBJECT (connection), "base-io-stream", &base_io_stream, NULL);
+
+		connection = base_io_stream;
+	} else if (connection) {
+		/* Connection can be NULL, when a custom command (GSubProcess) is used instead */
+		g_object_ref (connection);
+	}
+
+	if (!G_IS_SOCKET_CONNECTION (connection)) {
+		g_clear_object (&connection);
 		return previous_timeout;
+	}
 
 	socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (connection));
 	if (socket) {
@@ -1235,758 +763,170 @@ imapx_server_set_connection_timeout (GIO
 		g_socket_set_timeout (socket, timeout_seconds);
 	}
 
+	g_clear_object (&connection);
+
 	return previous_timeout;
 }
 
-/* Must hold QUEUE_LOCK */
 static void
-imapx_command_start (CamelIMAPXServer *is,
-                     CamelIMAPXCommand *ic)
+imapx_expunge_uid_from_summary (CamelIMAPXServer *is,
+                                const gchar *uid,
+                                gboolean unsolicited)
 {
-	CamelIMAPXCommandPart *cp;
-	CamelIMAPXJob *job;
-	GInputStream *input_stream = NULL;
-	GOutputStream *output_stream = NULL;
-	GCancellable *cancellable = NULL;
-	gboolean cp_continuation;
-	gboolean cp_literal_plus;
-	gboolean success;
-	GList *head;
-	gchar *string;
-	GError *local_error = NULL;
-
-	camel_imapx_command_close (ic);
-
-	head = g_queue_peek_head_link (&ic->parts);
-	g_return_if_fail (head != NULL);
-	cp = (CamelIMAPXCommandPart *) head->data;
-	ic->current_part = head;
-
-	cp_continuation = ((cp->type & CAMEL_IMAPX_COMMAND_CONTINUATION) != 0);
-	cp_literal_plus = ((cp->type & CAMEL_IMAPX_COMMAND_LITERAL_PLUS) != 0);
-
-	/* TODO: If we support literal+ we should be able to write the whole command out
-	 * at this point .... >here< */
+	CamelFolder *folder;
+	CamelIMAPXMailbox *mailbox;
+	guint32 messages;
 
-	if (cp_continuation || cp_literal_plus)
-		is->literal = ic;
+	mailbox = camel_imapx_server_ref_pending_or_selected (is);
 
-	camel_imapx_command_queue_push_tail (is->active, ic);
-	imapx_server_command_added (is, ic);
+	g_return_if_fail (mailbox != NULL);
 
-	job = camel_imapx_command_get_job (ic);
-	if (job && g_cancellable_set_error_if_cancelled (camel_imapx_job_get_cancellable (job), &local_error)) {
-		camel_imapx_job_take_error (job, local_error);
-		local_error = NULL;
+	folder = imapx_server_ref_folder (is, mailbox);
+	g_return_if_fail (folder != NULL);
 
-		camel_imapx_command_queue_remove (is->active, ic);
-		imapx_server_command_removed (is, ic);
+	messages = camel_imapx_mailbox_get_messages (mailbox);
 
-		if (ic->complete != NULL)
-			ic->complete (is, ic);
+	if (unsolicited && messages > 0)
+		camel_imapx_mailbox_set_messages (mailbox, messages - 1);
 
-		if (is->literal == ic)
-			is->literal = NULL;
+	g_return_if_fail (is->priv->changes != NULL);
 
-		goto exit;
-	}
+	camel_folder_summary_remove_uid (folder->summary, uid);
 
+	g_mutex_lock (&is->priv->changes_lock);
 
-	input_stream = camel_imapx_server_ref_input_stream (is);
-	output_stream = camel_imapx_server_ref_output_stream (is);
-	cancellable = g_weak_ref_get (&is->priv->parser_cancellable);
+	camel_folder_change_info_remove_uid (is->priv->changes, uid);
 
-	if (output_stream == NULL) {
-		local_error = g_error_new_literal (
-			CAMEL_IMAPX_ERROR, 1,
-			"Cannot issue command, no stream available");
-		goto fail;
-	}
+	if (camel_imapx_server_is_in_idle (is)) {
+		CamelFolderChangeInfo *changes;
 
-	c (
-		is->tagprefix,
-		"Starting command (active=%d,%s) %c%05u %s\r\n",
-		camel_imapx_command_queue_get_length (is->active),
-		is->literal ? " literal" : "",
-		is->tagprefix,
-		ic->tag,
-		cp->data && g_str_has_prefix (cp->data, "LOGIN") ?
-			"LOGIN..." : cp->data);
+		changes = is->priv->changes;
+		is->priv->changes = camel_folder_change_info_new ();
 
-	string = g_strdup_printf (
-		"%c%05u %s\r\n", is->tagprefix, ic->tag, cp->data);
-	g_mutex_lock (&is->priv->stream_lock);
-	success = g_output_stream_write_all (
-		output_stream, string, strlen (string),
-		NULL, cancellable, &local_error);
-	g_mutex_unlock (&is->priv->stream_lock);
-	g_free (string);
+		g_mutex_unlock (&is->priv->changes_lock);
 
-	if (local_error != NULL || !success)
-		goto fail;
+		camel_folder_summary_save_to_db (folder->summary, NULL);
+		imapx_update_store_summary (folder);
+		camel_folder_changed (folder, changes);
 
-	while (is->literal == ic && cp_literal_plus) {
-		/* Sent LITERAL+ continuation immediately */
-		imapx_continuation (
-			is, input_stream, output_stream,
-			TRUE, cancellable, &local_error);
-		if (local_error != NULL)
-			goto fail;
+		camel_folder_change_info_free (changes);
+	} else {
+		g_mutex_unlock (&is->priv->changes_lock);
 	}
 
-	imapx_server_reset_inactivity_timer (is);
+	g_object_unref (folder);
+	g_object_unref (mailbox);
+}
 
-	goto exit;
+/* untagged response handler functions */
 
-fail:
-	camel_imapx_command_queue_remove (is->active, ic);
-	imapx_server_command_removed (is, ic);
-
-	/* Break the parser thread out of its loop so it disconnects. */
-	g_main_loop_quit (is->priv->parser_main_loop);
-	g_cancellable_cancel (cancellable);
+static gboolean
+imapx_untagged_capability (CamelIMAPXServer *is,
+                           GInputStream *input_stream,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+	struct _capability_info *cinfo;
 
-	/* Hand the error off to the command that we failed to start. */
-	camel_imapx_command_failed (ic, local_error);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
 
-	if (ic->complete != NULL)
-		ic->complete (is, ic);
+	g_mutex_lock (&is->priv->stream_lock);
 
-	g_clear_error (&local_error);
+	if (is->priv->cinfo != NULL) {
+		imapx_free_capability (is->priv->cinfo);
+		is->priv->cinfo = NULL;
+	}
 
-exit:
-	g_clear_object (&input_stream);
-	g_clear_object (&output_stream);
-	g_clear_object (&cancellable);
-}
+	g_mutex_unlock (&is->priv->stream_lock);
 
-static gboolean
-imapx_is_duplicate_fetch_or_refresh (CamelIMAPXServer *is,
-                                     CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	guint32 job_types;
+	cinfo = imapx_parse_capability (CAMEL_IMAPX_INPUT_STREAM (input_stream), cancellable, error);
 
-	/* Job types to match. */
-	job_types =
-		IMAPX_JOB_FETCH_NEW_MESSAGES |
-		IMAPX_JOB_REFRESH_INFO;
+	if (!cinfo)
+		return FALSE;
 
-	job = camel_imapx_command_get_job (ic);
+	g_mutex_lock (&is->priv->stream_lock);
 
-	if (job == NULL)
-		return FALSE;
+	if (is->priv->cinfo != NULL)
+		imapx_free_capability (is->priv->cinfo);
+	is->priv->cinfo = cinfo;
 
-	if ((job->type & job_types) == 0)
-		return FALSE;
+	c (is->priv->tagprefix, "got capability flags %08x\n", is->priv->cinfo->capa);
 
-	if (imapx_match_active_job (is, job_types, NULL) == NULL)
-		return FALSE;
+	imapx_server_stash_command_arguments (is);
 
-	c (is->tagprefix, "Not yet sending duplicate fetch/refresh %s command\n", ic->name);
+	g_mutex_unlock (&is->priv->stream_lock);
 
 	return TRUE;
 }
 
-/* See if we can start another task yet.
- *
- * If we're waiting for a literal, we cannot proceed.
- *
- * If we're about to change the folder we're
- * looking at from user-direction, we dont proceed.
- *
- * If we have a folder selected, first see if any
- * jobs are waiting on it, but only if they are
- * at least as high priority as anything we
- * have running.
- *
- * If we dont, select the first folder required,
- * then queue all the outstanding jobs on it, that
- * are at least as high priority as the first.
- *
- * must have QUEUE lock */
-
-static void
-imapx_command_start_next (CamelIMAPXServer *is)
+static gboolean
+imapx_untagged_expunge (CamelIMAPXServer *is,
+                        GInputStream *input_stream,
+                        GCancellable *cancellable,
+                        GError **error)
 {
-	CamelIMAPXCommand *first_ic;
 	CamelIMAPXMailbox *mailbox;
-	gint min_pri = -128;
-
-	c (is->tagprefix, "** Starting next command\n");
-	if (is->literal) {
-		c (
-			is->tagprefix,
-			"* no; waiting for literal '%s'\n",
-			is->literal->name);
-		return;
-	}
-
-	g_mutex_lock (&is->priv->select_lock);
-	mailbox = g_weak_ref_get (&is->priv->select_pending);
-	g_mutex_unlock (&is->priv->select_lock);
-	if (mailbox != NULL) {
-		CamelIMAPXCommand *start_ic = NULL;
-		GList *head, *link;
-
-		c (
-			is->tagprefix,
-			"-- Checking job queue for non-mailbox jobs\n");
-
-		head = camel_imapx_command_queue_peek_head_link (is->queue);
+	gulong expunge = 0;
 
-		/* Tag which commands in the queue to start. */
-		for (link = head; link != NULL && !start_ic; link = g_list_next (link)) {
-			CamelIMAPXCommand *ic = link->data;
-			CamelIMAPXMailbox *ic_mailbox;
-
-			if (ic->pri < min_pri)
-				break;
-
-			c (
-				is->tagprefix,
-				"-- %3d '%s'?\n",
-				(gint) ic->pri, ic->name);
-
-			ic_mailbox = camel_imapx_command_ref_mailbox (ic);
-
-			if (ic_mailbox == NULL) {
-				c (
-					is->tagprefix,
-					"--> starting '%s'\n",
-					ic->name);
-				min_pri = ic->pri;
-
-				/* Each command must be removed from 'is->queue' before
-				 * starting it, so we temporarily reference the command
-				 * to avoid accidentally finalizing it. */
-				start_ic = camel_imapx_command_ref (ic);
-			}
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
 
-			g_clear_object (&ic_mailbox);
-		}
+	expunge = is->priv->context->id;
 
-		if (!start_ic)
-			c (
-				is->tagprefix,
-				"* no, waiting for pending select '%s'\n",
-				camel_imapx_mailbox_get_name (mailbox));
-
-		/* Start the tagged command */
-		if (start_ic) {
-			camel_imapx_command_queue_remove (is->queue, start_ic);
-			imapx_server_command_removed (is, start_ic);
-			imapx_command_start (is, start_ic);
-			camel_imapx_command_unref (start_ic);
-		}
+	COMMAND_LOCK (is);
 
-		g_clear_object (&mailbox);
+	/* Ignore EXPUNGE responses when not running a COPY(MOVE)_MESSAGE job */
+	if (!is->priv->current_command || (is->priv->current_command->job_kind != CAMEL_IMAPX_JOB_COPY_MESSAGE &&
+	    is->priv->current_command->job_kind != CAMEL_IMAPX_JOB_MOVE_MESSAGE)) {
+		COMMAND_UNLOCK (is);
 
-		return;
+		c (is->priv->tagprefix, "ignoring untagged expunge: %lu\n", expunge);
+		return TRUE;
 	}
 
-	if (is->state == IMAPX_SELECTED) {
-		gboolean stop_idle;
-		gboolean start_idle;
-
-		stop_idle =
-			imapx_in_idle (is) &&
-			!camel_imapx_command_queue_is_empty (is->queue);
-
-		start_idle =
-			imapx_use_idle (is) &&
-			!imapx_in_idle (is) &&
-			imapx_is_command_queue_empty (is);
-
-		if (stop_idle) {
-			switch (imapx_stop_idle (is, NULL)) {
-				/* Proceed with the next queued command. */
-				case IMAPX_IDLE_STOP_NOOP:
-					break;
-
-				case IMAPX_IDLE_STOP_WAIT_DONE:
-				case IMAPX_IDLE_STOP_SUCCESS:
-					c (
-						is->tagprefix,
-						"waiting for idle to stop \n");
-					/* if there are more pending commands,
-					 * then they should be processed too */
-					return;
+	COMMAND_UNLOCK (is);
 
-				case IMAPX_IDLE_STOP_ERROR:
-					return;
-			}
-
-		} else if (start_idle) {
-			imapx_start_idle (is);
-			c (is->tagprefix, "starting idle \n");
-			return;
-		}
-	}
+	c (is->priv->tagprefix, "expunged: %lu\n", expunge);
 
-	if (camel_imapx_command_queue_is_empty (is->queue)) {
-		c (is->tagprefix, "* no, no jobs\n");
-		return;
-	}
+	mailbox = camel_imapx_server_ref_pending_or_selected (is);
 
-	/* See if any queued jobs on this select first */
-	g_mutex_lock (&is->priv->select_lock);
-	mailbox = g_weak_ref_get (&is->priv->select_mailbox);
-	g_mutex_unlock (&is->priv->select_lock);
 	if (mailbox != NULL) {
-		CamelIMAPXCommand *start_ic = NULL;
-		GList *head, *link;
+		CamelFolder *folder;
+		gchar *uid;
 
-		c (
-			is->tagprefix,
-			"- we're selected on '%s', current jobs?\n",
-			camel_imapx_mailbox_get_name (mailbox));
+		folder = imapx_server_ref_folder (is, mailbox);
+		g_return_val_if_fail (folder != NULL, FALSE);
 
-		head = camel_imapx_command_queue_peek_head_link (is->active);
+		uid = camel_imapx_dup_uid_from_summary_index (folder, expunge - 1);
 
-		/* Find the highest priority in the active queue. */
-		for (link = head; link != NULL; link = g_list_next (link)) {
-			CamelIMAPXCommand *ic = link->data;
+		if (uid != NULL)
+			imapx_expunge_uid_from_summary (is, uid, TRUE);
 
-			min_pri = MAX (min_pri, ic->pri);
-			c (
-				is->tagprefix,
-				"-  %3d '%s'\n",
-				(gint) ic->pri, ic->name);
-		}
+		g_object_unref (folder);
+		g_free (uid);
+	}
 
-		if (camel_imapx_command_queue_get_length (is->active) >= MAX_COMMANDS) {
-			c (
-				is->tagprefix,
-				"** too many jobs busy, "
-				"waiting for results for now\n");
-			g_object_unref (mailbox);
-			return;
-		}
+	g_clear_object (&mailbox);
 
-		c (is->tagprefix, "-- Checking job queue\n");
+	return TRUE;
+}
 
-		head = camel_imapx_command_queue_peek_head_link (is->queue);
+static gboolean
+imapx_untagged_vanished (CamelIMAPXServer *is,
+                         GInputStream *input_stream,
+                         GCancellable *cancellable,
+                         GError **error)
+{
+	CamelFolder *folder;
+	CamelIMAPXMailbox *mailbox;
+	GArray *uids;
+	GList *uid_list = NULL;
+	gboolean unsolicited = TRUE;
+	guint ii = 0;
+	guint len = 0;
+	guchar *token = NULL;
+	gint tok = 0;
 
-		/* Tag which commands in the queue to start. */
-		for (link = head; link != NULL && !start_ic; link = g_list_next (link)) {
-			CamelIMAPXCommand *ic = link->data;
-			CamelIMAPXMailbox *ic_mailbox;
-			gboolean okay_to_start;
-
-			if (is->literal != NULL)
-				break;
-
-			if (ic->pri < min_pri)
-				break;
-
-			c (
-				is->tagprefix,
-				"-- %3d '%s'?\n",
-				(gint) ic->pri, ic->name);
-
-			ic_mailbox = camel_imapx_command_ref_mailbox (ic);
-
-			okay_to_start =
-				(ic_mailbox == NULL) ||
-				(ic_mailbox == mailbox &&
-				!imapx_is_duplicate_fetch_or_refresh (is, ic));
-
-			if (okay_to_start) {
-				c (
-					is->tagprefix,
-					"--> starting '%s'\n",
-					ic->name);
-				min_pri = ic->pri;
-				/* Each command must be removed from 'is->queue' before
-				 * starting it, so we temporarily reference the command
-				 * to avoid accidentally finalizing it. */
-				start_ic = camel_imapx_command_ref (ic);
-			} else {
-				/* This job isn't for the selected mailbox,
-				 * but we don't want to consider jobs with
-				 * lower priority than this, even if they
-				 * are for the selected mailbox. */
-				min_pri = ic->pri;
-			}
-
-			g_clear_object (&ic_mailbox);
-		}
-
-		g_clear_object (&mailbox);
-
-		/* Start the tagged command */
-		if (start_ic) {
-			camel_imapx_command_queue_remove (is->queue, start_ic);
-			imapx_server_command_removed (is, start_ic);
-			imapx_command_start (is, start_ic);
-			camel_imapx_command_unref (start_ic);
-
-			return;
-		}
-	}
-
-	/* This won't be NULL because we checked for an empty queue above. */
-	first_ic = camel_imapx_command_queue_peek_head (is->queue);
-
-	/* If we need to select a mailbox for the first command, do
-	 * so now.  It will re-call us if it completes successfully. */
-	mailbox = camel_imapx_command_ref_mailbox (first_ic);
-	if (mailbox != NULL) {
-		CamelIMAPXJob *job;
-
-		c (
-			is->tagprefix,
-			"Selecting mailbox '%s' for command '%s'(%p)\n",
-			camel_imapx_mailbox_get_name (mailbox),
-			first_ic->name, first_ic);
-
-		/* Associate the SELECT command with the CamelIMAPXJob
-		 * that triggered it.  Then if the SELECT command fails
-		 * we have some destination to propagate the GError to. */
-		job = camel_imapx_command_get_job (first_ic);
-		imapx_maybe_select (is, job, mailbox);
-
-		g_clear_object (&mailbox);
-
-	} else {
-		CamelIMAPXCommand *start_ic = NULL;
-		GList *head, *link;
-
-		min_pri = first_ic->pri;
-
-		g_mutex_lock (&is->priv->select_lock);
-		mailbox = g_weak_ref_get (&is->priv->select_mailbox);
-		g_mutex_unlock (&is->priv->select_lock);
-
-		head = camel_imapx_command_queue_peek_head_link (is->queue);
-
-		/* Tag which commands in the queue to start. */
-		for (link = head; link != NULL && !start_ic; link = g_list_next (link)) {
-			CamelIMAPXCommand *ic = link->data;
-			CamelIMAPXMailbox *ic_mailbox;
-			gboolean okay_to_start;
-
-			if (is->literal != NULL)
-				break;
-
-			if (ic->pri < min_pri)
-				break;
-
-			ic_mailbox = camel_imapx_command_ref_mailbox (ic);
-
-			okay_to_start =
-				(ic_mailbox == NULL) ||
-				(ic_mailbox == mailbox &&
-				!imapx_is_duplicate_fetch_or_refresh (is, ic));
-
-			if (okay_to_start) {
-				c (
-					is->tagprefix,
-					"* queueing job %3d '%s'\n",
-					(gint) ic->pri, ic->name);
-				min_pri = ic->pri;
-				/* Each command must be removed from 'is->queue' before
-				 * starting it, so we temporarily reference the command
-				 * to avoid accidentally finalizing it. */
-				start_ic = camel_imapx_command_ref (ic);
-			}
-
-			g_clear_object (&ic_mailbox);
-		}
-
-		g_clear_object (&mailbox);
-
-		/* Start the tagged command */
-		if (start_ic) {
-			camel_imapx_command_queue_remove (is->queue, start_ic);
-			imapx_server_command_removed (is, start_ic);
-			imapx_command_start (is, start_ic);
-			camel_imapx_command_unref (start_ic);
-		}
-	}
-}
-
-static gboolean
-imapx_is_command_queue_empty (CamelIMAPXServer *is)
-{
-	if (!camel_imapx_command_queue_is_empty (is->queue))
-		return FALSE;
-
-	if (!camel_imapx_command_queue_is_empty (is->active))
-		return FALSE;
-
-	return TRUE;
-}
-
-static void
-imapx_command_queue (CamelIMAPXServer *is,
-                     CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-
-	/* We enqueue in priority order, new messages have
-	 * higher priority than older messages with the same priority */
-
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
-
-	camel_imapx_command_close (ic);
-
-	c (
-		is->tagprefix,
-		"enqueue job '%.*s'\n",
-		((CamelIMAPXCommandPart *) ic->parts.head->data)->data_size,
-		((CamelIMAPXCommandPart *) ic->parts.head->data)->data);
-
-	QUEUE_LOCK (is);
-
-	if (is->state == IMAPX_SHUTDOWN) {
-		GError *local_error = NULL;
-
-		c (is->tagprefix, "refuse to queue job on disconnected server\n");
-
-		local_error = g_error_new (
-			CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT,
-			"%s", _("Server disconnected"));
-		camel_imapx_command_failed (ic, local_error);
-		g_error_free (local_error);
-
-		QUEUE_UNLOCK (is);
-
-		if (ic->complete != NULL)
-			ic->complete (is, ic);
-
-		return;
-	}
-
-	camel_imapx_command_queue_insert_sorted (is->queue, ic);
-	imapx_server_command_added (is, ic);
-
-	imapx_command_start_next (is);
-
-	QUEUE_UNLOCK (is);
-}
-
-/* Must not have QUEUE lock */
-static CamelIMAPXJob *
-imapx_match_active_job (CamelIMAPXServer *is,
-                        guint32 type,
-                        const gchar *uid)
-{
-	CamelIMAPXJob *match = NULL;
-	GList *head, *link;
-
-	QUEUE_LOCK (is);
-
-	head = camel_imapx_command_queue_peek_head_link (is->active);
-
-	for (link = head; link != NULL; link = g_list_next (link)) {
-		CamelIMAPXCommand *ic = link->data;
-		CamelIMAPXMailbox *mailbox;
-		CamelIMAPXJob *job;
-		gboolean job_matches;
-
-		job = camel_imapx_command_get_job (ic);
-
-		if (job == NULL)
-			continue;
-
-		if (!(job->type & type))
-			continue;
-
-		g_mutex_lock (&is->priv->select_lock);
-		mailbox = g_weak_ref_get (&is->priv->select_mailbox);
-		g_mutex_unlock (&is->priv->select_lock);
-
-		job_matches = camel_imapx_job_matches (job, mailbox, uid);
-		g_clear_object (&mailbox);
-
-		if (job_matches) {
-			match = job;
-			break;
-		}
-	}
-
-	QUEUE_UNLOCK (is);
-
-	return match;
-}
-
-/* Do *not* call this when the queue_lock is held, it can cause
-   deadlock when searching between multiple servers */
-static CamelIMAPXJob *
-imapx_server_ref_job (CamelIMAPXServer *imapx_server,
-		      CamelIMAPXMailbox *mailbox,
-		      guint32 job_type,
-		      const gchar *uid)
-{
-	CamelIMAPXStore *imapx_store;
-	CamelIMAPXJob *job;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), NULL);
-
-	/* first try its own queue */
-	job = camel_imapx_server_ref_job (imapx_server, mailbox, job_type, uid);
-	if (job)
-		return job;
-
-	/* then try queue for all the opened servers */
-	imapx_store = camel_imapx_server_ref_store (imapx_server);
-	if (!imapx_store)
-		return NULL;
-
-	job = camel_imapx_store_ref_job (imapx_store, mailbox, job_type, uid);
-
-	g_object_unref (imapx_store);
-
-	return job;
-}
-
-static void
-imapx_expunge_uid_from_summary (CamelIMAPXServer *is,
-                                gchar *uid,
-                                gboolean unsolicited)
-{
-	CamelFolder *folder;
-	CamelIMAPXMailbox *mailbox;
-	CamelMessageInfo *mi;
-	guint32 messages;
-
-	g_mutex_lock (&is->priv->select_lock);
-	mailbox = g_weak_ref_get (&is->priv->select_mailbox);
-	g_mutex_unlock (&is->priv->select_lock);
-
-	g_return_if_fail (mailbox != NULL);
-
-	folder = imapx_server_ref_folder (is, mailbox);
-	g_return_if_fail (folder != NULL);
-
-	messages = camel_imapx_mailbox_get_messages (mailbox);
-
-	if (unsolicited && messages > 0)
-		camel_imapx_mailbox_set_messages (mailbox, messages - 1);
-
-	if (is->priv->changes == NULL)
-		is->priv->changes = camel_folder_change_info_new ();
-
-	mi = camel_folder_summary_peek_loaded (folder->summary, uid);
-	if (mi) {
-		camel_folder_summary_remove (folder->summary, mi);
-		camel_message_info_unref (mi);
-	} else {
-		camel_folder_summary_remove_uid (folder->summary, uid);
-	}
-
-	camel_folder_change_info_remove_uid (is->priv->changes, uid);
-
-	if (imapx_in_idle (is)) {
-		camel_folder_summary_save_to_db (folder->summary, NULL);
-		imapx_update_store_summary (folder);
-		camel_folder_changed (folder, is->priv->changes);
-
-		camel_folder_change_info_clear (is->priv->changes);
-	}
-
-	g_object_unref (folder);
-	g_object_unref (mailbox);
-}
-
-/* untagged response handler functions */
-
-static gboolean
-imapx_untagged_capability (CamelIMAPXServer *is,
-                           GInputStream *input_stream,
-                           GCancellable *cancellable,
-                           GError **error)
-{
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-
-	if (is->cinfo != NULL)
-		imapx_free_capability (is->cinfo);
-
-	is->cinfo = imapx_parse_capability (
-		CAMEL_IMAPX_INPUT_STREAM (input_stream), cancellable, error);
-
-	if (is->cinfo == NULL)
-		return FALSE;
-
-	c (is->tagprefix, "got capability flags %08x\n", is->cinfo->capa);
-
-	imapx_server_stash_command_arguments (is);
-
-	return TRUE;
-}
-
-static gboolean
-imapx_untagged_expunge (CamelIMAPXServer *is,
-                        GInputStream *input_stream,
-                        GCancellable *cancellable,
-                        GError **error)
-{
-	CamelIMAPXMailbox *mailbox;
-	CamelIMAPXJob *job = NULL;
-	guint32 expunge = 0;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-
-	expunge = is->priv->context->id;
-	job = imapx_match_active_job (is, IMAPX_JOB_EXPUNGE, NULL);
-
-	/* If there is a job running, let it handle the deletion */
-	if (job != NULL)
-		return TRUE;
-
-	job = imapx_match_active_job (is, IMAPX_JOB_COPY_MESSAGE, NULL);
-	/* Ignore EXPUNGE responses when not running a COPY(MOVE)_MESSAGE job */
-	if (!job) {
-		c (is->tagprefix, "ignoring untagged expunge: %lu\n", is->priv->context->id);
-		return TRUE;
-	}
-
-	c (is->tagprefix, "expunged: %lu\n", is->priv->context->id);
-
-	g_mutex_lock (&is->priv->select_lock);
-	mailbox = g_weak_ref_get (&is->priv->select_mailbox);
-	g_mutex_unlock (&is->priv->select_lock);
-
-	if (mailbox != NULL) {
-		CamelFolder *folder;
-		gchar *uid;
-
-		folder = imapx_server_ref_folder (is, mailbox);
-		g_return_val_if_fail (folder != NULL, FALSE);
-
-		uid = camel_imapx_dup_uid_from_summary_index (
-			folder, expunge - 1);
-
-		if (uid != NULL)
-			imapx_expunge_uid_from_summary (is, uid, TRUE);
-
-		g_object_unref (folder);
-		g_object_unref (mailbox);
-	}
-
-	return TRUE;
-}
-
-static gboolean
-imapx_untagged_vanished (CamelIMAPXServer *is,
-                         GInputStream *input_stream,
-                         GCancellable *cancellable,
-                         GError **error)
-{
-	CamelFolder *folder;
-	CamelIMAPXMailbox *mailbox;
-	GArray *uids;
-	GList *uid_list = NULL;
-	gboolean unsolicited = TRUE;
-	guint ii = 0;
-	guint len = 0;
-	guchar *token = NULL;
-	gint tok = 0;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
 
 	tok = camel_imapx_input_stream_token (
 		CAMEL_IMAPX_INPUT_STREAM (input_stream),
@@ -2014,9 +954,7 @@ imapx_untagged_vanished (CamelIMAPXServe
 	if (uids == NULL)
 		return FALSE;
 
-	g_mutex_lock (&is->priv->select_lock);
-	mailbox = g_weak_ref_get (&is->priv->select_mailbox);
-	g_mutex_unlock (&is->priv->select_lock);
+	mailbox = camel_imapx_server_ref_pending_or_selected (is);
 
 	g_return_val_if_fail (mailbox != NULL, FALSE);
 
@@ -2030,7 +968,7 @@ imapx_untagged_vanished (CamelIMAPXServe
 
 		if (messages < uids->len) {
 			c (
-				is->tagprefix,
+				is->priv->tagprefix,
 				"Error: mailbox messages (%u) is "
 				"fewer than vanished %u\n",
 				messages, uids->len);
@@ -2042,8 +980,9 @@ imapx_untagged_vanished (CamelIMAPXServe
 		camel_imapx_mailbox_set_messages (mailbox, messages);
 	}
 
-	if (is->priv->changes == NULL)
-		is->priv->changes = camel_folder_change_info_new ();
+	g_return_val_if_fail (is->priv->changes != NULL, FALSE);
+
+	g_mutex_lock (&is->priv->changes_lock);
 
 	for (ii = 0; ii < uids->len; ii++) {
 		guint32 uid;
@@ -2051,23 +990,44 @@ imapx_untagged_vanished (CamelIMAPXServe
 
 		uid = g_array_index (uids, guint32, ii);
 
-		e (is->tagprefix, "vanished: %u\n", uid);
+		e (is->priv->tagprefix, "vanished: %u\n", uid);
 
 		str = g_strdup_printf ("%u", uid);
 		uid_list = g_list_prepend (uid_list, str);
 		camel_folder_change_info_remove_uid (is->priv->changes, str);
 	}
 
+	g_mutex_unlock (&is->priv->changes_lock);
+
 	uid_list = g_list_reverse (uid_list);
 	camel_folder_summary_remove_uids (folder->summary, uid_list);
 
 	/* If the response is truly unsolicited (e.g. via NOTIFY)
 	 * then go ahead and emit the change notification now. */
-	if (camel_imapx_command_queue_is_empty (is->queue)) {
-		camel_folder_summary_save_to_db (folder->summary, NULL);
-		imapx_update_store_summary (folder);
-		camel_folder_changed (folder, is->priv->changes);
-		camel_folder_change_info_clear (is->priv->changes);
+	COMMAND_LOCK (is);
+	if (!is->priv->current_command) {
+		COMMAND_UNLOCK (is);
+
+		g_mutex_lock (&is->priv->changes_lock);
+		if (is->priv->changes->uid_removed &&
+		    is->priv->changes->uid_removed->len >= 100) {
+			CamelFolderChangeInfo *changes;
+
+			changes = is->priv->changes;
+			is->priv->changes = camel_folder_change_info_new ();
+
+			g_mutex_unlock (&is->priv->changes_lock);
+
+			camel_folder_summary_save_to_db (folder->summary, NULL);
+			imapx_update_store_summary (folder);
+
+			camel_folder_changed (folder, changes);
+			camel_folder_change_info_free (changes);
+		} else {
+			g_mutex_unlock (&is->priv->changes_lock);
+		}
+	} else {
+		COMMAND_UNLOCK (is);
 	}
 
 	g_list_free_full (uid_list, (GDestroyNotify) g_free);
@@ -2118,7 +1078,7 @@ imapx_untagged_exists (CamelIMAPXServer
 
 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
 
-	mailbox = camel_imapx_server_ref_selected (is);
+	mailbox = camel_imapx_server_ref_pending_or_selected (is);
 
 	if (mailbox == NULL) {
 		g_warning ("%s: No mailbox available", G_STRFUNC);
@@ -2127,21 +1087,22 @@ imapx_untagged_exists (CamelIMAPXServer
 
 	exists = (guint32) is->priv->context->id;
 
+	c (is->priv->tagprefix, "%s: updating mailbox '%s' messages: %d ~> %d\n", G_STRFUNC,
+		camel_imapx_mailbox_get_name (mailbox),
+		camel_imapx_mailbox_get_messages (mailbox),
+		exists);
+
 	camel_imapx_mailbox_set_messages (mailbox, exists);
 
 	folder = imapx_server_ref_folder (is, mailbox);
 	g_return_val_if_fail (folder != NULL, FALSE);
 
-	if (imapx_in_idle (is)) {
+	if (camel_imapx_server_is_in_idle (is)) {
 		guint count;
 
 		count = camel_folder_summary_count (folder->summary);
-		if (count < exists) {
-			CamelIMAPXIdleStopResult stop_result;
-
-			stop_result = imapx_stop_idle (is, error);
-			success = (stop_result != IMAPX_IDLE_STOP_ERROR);
-		}
+		if (count < exists)
+			g_signal_emit (is, signals[REFRESH_MAILBOX], 0, mailbox);
 	}
 
 	g_object_unref (folder);
@@ -2165,7 +1126,7 @@ imapx_untagged_flags (CamelIMAPXServer *
 		CAMEL_IMAPX_INPUT_STREAM (input_stream),
 		&flags, NULL, cancellable, error);
 
-	c (is->tagprefix, "flags: %08x\n", flags);
+	c (is->priv->tagprefix, "flags: %08x\n", flags);
 
 	return success;
 }
@@ -2204,72 +1165,50 @@ imapx_untagged_fetch (CamelIMAPXServer *
 	}
 
 	if ((finfo->got & (FETCH_BODY | FETCH_UID)) == (FETCH_BODY | FETCH_UID)) {
-		CamelIMAPXJob *job;
-		GetMessageData *data;
-
-		job = imapx_match_active_job (
-			is, IMAPX_JOB_GET_MESSAGE, finfo->uid);
-		if (job == NULL) {
-			g_warn_if_reached ();
-			return FALSE;
-		}
-
-		data = camel_imapx_job_get_data (job);
-		g_return_val_if_fail (data != NULL, FALSE);
+		GOutputStream *output_stream;
+		gconstpointer body_data;
+		gsize body_size;
 
-		/* This must've been a get-message request,
-		 * fill out the body stream, in the right spot. */
+		g_return_val_if_fail (is->priv->get_message_stream != NULL, FALSE);
 
-		if (job != NULL) {
-			GOutputStream *output_stream;
-			gconstpointer body_data;
-			gsize body_size;
+		/* Fill out the body stream, in the right spot. */
 
-			if (data->use_multi_fetch) {
-				data->body_offset = finfo->offset;
-				g_seekable_seek (
-					G_SEEKABLE (data->stream),
-					finfo->offset, G_SEEK_SET,
-					NULL, NULL);
-			}
+		g_seekable_seek (
+			G_SEEKABLE (is->priv->get_message_stream),
+			finfo->offset, G_SEEK_SET,
+			NULL, NULL);
 
-			output_stream =
-				g_io_stream_get_output_stream (data->stream);
+		output_stream = g_io_stream_get_output_stream (is->priv->get_message_stream);
 
-			body_data = g_bytes_get_data (finfo->body, &body_size);
+		body_data = g_bytes_get_data (finfo->body, &body_size);
 
-			/* Sometimes the server, like Microsoft Exchange, reports larger message
-			   size than it actually is, which results in no data being read from
-			   the server for that particular offset. */
-			if (body_size) {
-				g_mutex_lock (&is->priv->stream_lock);
-				if (!g_output_stream_write_all (
-					output_stream, body_data, body_size,
-					NULL, cancellable, error)) {
-					g_mutex_unlock (&is->priv->stream_lock);
-					g_prefix_error (
-						error, "%s: ",
-						_("Error writing to cache stream"));
-					return FALSE;
-				}
+		/* Sometimes the server, like Microsoft Exchange, reports larger message
+		   size than it actually is, which results in no data being read from
+		   the server for that particular offset. */
+		if (body_size) {
+			g_mutex_lock (&is->priv->stream_lock);
+			if (!g_output_stream_write_all (
+				output_stream, body_data, body_size,
+				NULL, cancellable, error)) {
 				g_mutex_unlock (&is->priv->stream_lock);
+				g_prefix_error (
+					error, "%s: ",
+					_("Error writing to cache stream"));
+				imapx_free_fetch (finfo);
+				return FALSE;
 			}
+			g_mutex_unlock (&is->priv->stream_lock);
 		}
 	}
 
 	if ((finfo->got & FETCH_FLAGS) && !(finfo->got & FETCH_HEADER)) {
-		CamelIMAPXJob *job;
 		CamelIMAPXMailbox *select_mailbox;
 		CamelIMAPXMailbox *select_pending;
-		RefreshInfoData *data = NULL;
 
-		job = imapx_match_active_job (
-			is, IMAPX_JOB_FETCH_NEW_MESSAGES |
-			IMAPX_JOB_REFRESH_INFO, NULL);
-
-		if (job != NULL) {
-			data = camel_imapx_job_get_data (job);
-			g_return_val_if_fail (data != NULL, FALSE);
+		if (is->priv->fetch_changes_mailbox) {
+			g_return_val_if_fail (is->priv->fetch_changes_mailbox != NULL, FALSE);
+			g_return_val_if_fail (is->priv->fetch_changes_folder != NULL, FALSE);
+			g_return_val_if_fail (is->priv->fetch_changes_infos != NULL, FALSE);
 		}
 
 		g_mutex_lock (&is->priv->select_lock);
@@ -2280,27 +1219,49 @@ imapx_untagged_fetch (CamelIMAPXServer *
 		/* This is either a refresh_info job, check to see if it is
 		 * and update if so, otherwise it must've been an unsolicited
 		 * response, so update the summary to match. */
-		if (data && (finfo->got & FETCH_UID) && data->scan_changes) {
-			struct _refresh_info r;
+		if ((finfo->got & FETCH_UID) != 0 && is->priv->fetch_changes_folder && is->priv->fetch_changes_infos) {
+			FetchChangesInfo *nfo;
+			gint64 monotonic_time;
+			gint n_messages;
+
+			nfo = g_hash_table_lookup (is->priv->fetch_changes_infos, finfo->uid);
+			if (!nfo) {
+				nfo = g_new0 (FetchChangesInfo, 1);
+
+				g_hash_table_insert (is->priv->fetch_changes_infos, (gpointer) camel_pstring_strdup (finfo->uid), nfo);
+			}
 
-			r.uid = finfo->uid;
-			finfo->uid = NULL;
-			r.server_flags = finfo->flags;
-			r.server_user_flags = finfo->user_flags;
+			nfo->server_flags = finfo->flags;
+			nfo->server_user_flags = finfo->user_flags;
 			finfo->user_flags = NULL;
-			r.exists = FALSE;
-			g_array_append_val (data->infos, r);
 
+			monotonic_time = g_get_monotonic_time ();
+			n_messages = camel_imapx_mailbox_get_messages (is->priv->fetch_changes_mailbox);
+
+			if (n_messages > 0 && is->priv->fetch_changes_last_progress + G_USEC_PER_SEC / 2 < monotonic_time &&
+			    is->priv->context && is->priv->context->id <= n_messages) {
+				COMMAND_LOCK (is);
+
+				if (is->priv->current_command) {
+					COMMAND_UNLOCK (is);
+
+					is->priv->fetch_changes_last_progress = monotonic_time;
+
+					camel_operation_progress (cancellable, 100 * is->priv->context->id
+						/ camel_imapx_mailbox_get_messages (is->priv->fetch_changes_mailbox));
+				} else {
+					COMMAND_UNLOCK (is);
+				}
+			}
 		} else if (select_mailbox != NULL) {
 			CamelFolder *select_folder;
 			CamelMessageInfo *mi = NULL;
 			gboolean changed = FALSE;
 			gchar *uid = NULL;
 
-			c (is->tagprefix, "flag changed: %lu\n", is->priv->context->id);
+			c (is->priv->tagprefix, "flag changed: %lu\n", is->priv->context->id);
 
-			select_folder =
-				imapx_server_ref_folder (is, select_mailbox);
+			select_folder = imapx_server_ref_folder (is, select_mailbox);
 			g_return_val_if_fail (select_folder != NULL, FALSE);
 
 			if (finfo->got & FETCH_UID) {
@@ -2313,8 +1274,7 @@ imapx_untagged_fetch (CamelIMAPXServer *
 			}
 
 			if (uid) {
-				mi = camel_folder_summary_get (
-					select_folder->summary, uid);
+				mi = camel_folder_summary_get (select_folder->summary, uid);
 				if (mi) {
 					/* It's unsolicited _unless_ select_pending (i.e. during
 					 * a QRESYNC SELECT */
@@ -2327,29 +1287,30 @@ imapx_untagged_fetch (CamelIMAPXServer *
 				} else {
 					/* This (UID + FLAGS for previously unknown message) might
 					 * happen during a SELECT (QRESYNC). We should use it. */
-					c (is->tagprefix, "flags changed for unknown uid %s\n.", uid);
+					c (is->priv->tagprefix, "flags changed for unknown uid %s\n.", uid);
 				}
 				finfo->user_flags = NULL;
 			}
 
 			if (changed) {
-				if (is->priv->changes == NULL)
-					is->priv->changes =
-						camel_folder_change_info_new ();
+				g_return_val_if_fail (is->priv->changes != NULL, FALSE);
 
-				camel_folder_change_info_change_uid (
-					is->priv->changes, uid);
+				g_mutex_lock (&is->priv->changes_lock);
+				camel_folder_change_info_change_uid (is->priv->changes, uid);
+				g_mutex_unlock (&is->priv->changes_lock);
 			}
 			g_free (uid);
 
-			if (changed && imapx_in_idle (is)) {
-				camel_folder_summary_save_to_db (
-					select_folder->summary, NULL);
+			if (changed && camel_imapx_server_is_in_idle (is)) {
+				camel_folder_summary_save_to_db (select_folder->summary, NULL);
 				imapx_update_store_summary (select_folder);
-				camel_folder_changed (
-					select_folder, is->priv->changes);
-				camel_folder_change_info_clear (
-					is->priv->changes);
+
+				g_mutex_lock (&is->priv->changes_lock);
+
+				camel_folder_changed (select_folder, is->priv->changes);
+				camel_folder_change_info_clear (is->priv->changes);
+
+				g_mutex_unlock (&is->priv->changes_lock);
 			}
 
 			if (mi)
@@ -2363,145 +1324,123 @@ imapx_untagged_fetch (CamelIMAPXServer *
 	}
 
 	if ((finfo->got & (FETCH_HEADER | FETCH_UID)) == (FETCH_HEADER | FETCH_UID)) {
-		CamelIMAPXJob *job;
+		CamelIMAPXMailbox *mailbox;
+		CamelFolder *folder;
+		CamelMimeParser *mp;
+		CamelMessageInfo *mi;
+		guint32 messages;
+		guint32 unseen;
+		guint32 uidnext;
 
 		/* This must be a refresh info job as well, but it has
 		 * asked for new messages to be added to the index. */
 
-		job = imapx_match_active_job (
-			is, IMAPX_JOB_FETCH_NEW_MESSAGES |
-			IMAPX_JOB_REFRESH_INFO, NULL);
-
-		if (job != NULL) {
-			CamelIMAPXMailbox *mailbox;
-			CamelFolder *folder;
-			CamelMimeParser *mp;
-			CamelMessageInfo *mi;
-			guint32 messages;
-			guint32 unseen;
-			guint32 uidnext;
+		if (is->priv->fetch_changes_mailbox) {
+			g_return_val_if_fail (is->priv->fetch_changes_mailbox != NULL, FALSE);
+			g_return_val_if_fail (is->priv->fetch_changes_folder != NULL, FALSE);
+			g_return_val_if_fail (is->priv->fetch_changes_infos != NULL, FALSE);
+
+			folder = g_object_ref (is->priv->fetch_changes_folder);
+			mailbox = g_object_ref (is->priv->fetch_changes_mailbox);
+		} else {
+			mailbox = camel_imapx_server_ref_selected (is);
+			folder = mailbox ? imapx_server_ref_folder (is, mailbox) : NULL;
+		}
 
-			mailbox = camel_imapx_job_ref_mailbox (job);
-			g_return_val_if_fail (mailbox != NULL, FALSE);
+		if (!mailbox || !folder || (!(finfo->got & FETCH_FLAGS) && !is->priv->fetch_changes_infos)) {
+			g_clear_object (&mailbox);
+			g_clear_object (&folder);
+			imapx_free_fetch (finfo);
 
-			folder = imapx_server_ref_folder (is, mailbox);
-			g_return_val_if_fail (folder != NULL, FALSE);
+			return TRUE;
+		}
+
+		messages = camel_imapx_mailbox_get_messages (mailbox);
+		unseen = camel_imapx_mailbox_get_unseen (mailbox);
+		uidnext = camel_imapx_mailbox_get_uidnext (mailbox);
+
+		/* Do we want to save these headers for later too?  Do we care? */
+
+		mp = camel_mime_parser_new ();
+		camel_mime_parser_init_with_bytes (mp, finfo->header);
+		mi = camel_folder_summary_info_new_from_parser (folder->summary, mp);
+		g_object_unref (mp);
+
+		if (mi != NULL) {
+			guint32 server_flags;
+			CamelFlag *server_user_flags;
+			CamelMessageInfoBase *binfo;
+			gboolean free_user_flags = FALSE;
 
-			messages = camel_imapx_mailbox_get_messages (mailbox);
-			unseen = camel_imapx_mailbox_get_unseen (mailbox);
-			uidnext = camel_imapx_mailbox_get_uidnext (mailbox);
-
-			/* Do we want to save these headers for later too?  Do we care? */
-
-			mp = camel_mime_parser_new ();
-			camel_mime_parser_init_with_bytes (mp, finfo->header);
-			mi = camel_folder_summary_info_new_from_parser (folder->summary, mp);
-			g_object_unref (mp);
-
-			if (mi != NULL) {
-				guint32 server_flags;
-				CamelFlag *server_user_flags;
-				CamelMessageInfoBase *binfo;
-				gboolean free_user_flags = FALSE;
-
-				mi->uid = camel_pstring_strdup (finfo->uid);
-
-				if (!(finfo->got & FETCH_FLAGS)) {
-					RefreshInfoData *data;
-					struct _refresh_info *r = NULL;
-					gint min, max, mid;
-					gboolean found = FALSE;
-
-					data = camel_imapx_job_get_data (job);
-					g_return_val_if_fail (data != NULL, FALSE);
-
-					min = data->last_index;
-					max = data->index - 1;
-
-					/* array is sorted, so use a binary search */
-					do {
-						gint cmp = 0;
-
-						mid = (min + max) / 2;
-						r = &g_array_index (data->infos, struct _refresh_info, mid);
-						cmp = imapx_refresh_info_uid_cmp (
-							finfo->uid,
-							r->uid,
-							is->priv->context->fetch_order == CAMEL_SORT_ASCENDING);
-
-						if (cmp > 0)
-							min = mid + 1;
-						else if (cmp < 0)
-							max = mid - 1;
-						else
-							found = TRUE;
+			mi->uid = camel_pstring_strdup (finfo->uid);
 
-					} while (!found && min <= max);
+			if (!(finfo->got & FETCH_FLAGS) && is->priv->fetch_changes_infos) {
+				FetchChangesInfo *nfo;
 
-					g_return_val_if_fail (found, FALSE);
+				nfo = g_hash_table_lookup (is->priv->fetch_changes_infos, finfo->uid);
+				g_return_val_if_fail (nfo != NULL, FALSE);
 
-					server_flags = r->server_flags;
-					server_user_flags = r->server_user_flags;
+				server_flags = nfo->server_flags;
+				server_user_flags = nfo->server_user_flags;
+			} else {
+				server_flags = finfo->flags;
+				server_user_flags = finfo->user_flags;
+				/* free user_flags ? */
+				finfo->user_flags = NULL;
+				free_user_flags = TRUE;
+			}
+
+			/* If the message is a really new one -- equal or higher than what
+			 * we know as UIDNEXT for the folder, then it came in since we last
+			 * fetched UIDNEXT and UNREAD count. We'll update UIDNEXT in the
+			 * command completion, but update UNREAD count now according to the
+			 * message SEEN flag */
+			if (!(server_flags & CAMEL_MESSAGE_SEEN)) {
+				guint64 uidl;
+
+				uidl = strtoull (mi->uid, NULL, 10);
+
+				if (uidl >= uidnext) {
+					c (is->priv->tagprefix, "Updating unseen count for new message %s\n", mi->uid);
+					camel_imapx_mailbox_set_unseen (mailbox, unseen + 1);
 				} else {
-					server_flags = finfo->flags;
-					server_user_flags = finfo->user_flags;
-					/* free user_flags ? */
-					finfo->user_flags = NULL;
-					free_user_flags = TRUE;
+					c (is->priv->tagprefix, "Not updating unseen count for new message %s\n", mi->uid);
 				}
+			}
 
-				/* If the message is a really new one -- equal or higher than what
-				 * we know as UIDNEXT for the folder, then it came in since we last
-				 * fetched UIDNEXT and UNREAD count. We'll update UIDNEXT in the
-				 * command completion, but update UNREAD count now according to the
-				 * message SEEN flag */
-				if (!(server_flags & CAMEL_MESSAGE_SEEN)) {
-					guint64 uidl;
-
-					uidl = strtoull (mi->uid, NULL, 10);
-
-					if (uidl >= uidnext) {
-						c (is->tagprefix, "Updating unseen count for new message %s\n", mi->uid);
-						camel_imapx_mailbox_set_unseen (mailbox, unseen + 1);
-					} else {
-						c (is->tagprefix, "Not updating unseen count for new message %s\n", mi->uid);
-					}
-				}
+			binfo = (CamelMessageInfoBase *) mi;
+			binfo->size = finfo->size;
 
-				binfo = (CamelMessageInfoBase *) mi;
-				binfo->size = finfo->size;
+			camel_folder_summary_lock (folder->summary);
 
-				camel_folder_summary_lock (folder->summary);
+			if (!camel_folder_summary_check_uid (folder->summary, mi->uid)) {
+				imapx_set_message_info_flags_for_new_message (mi, server_flags, server_user_flags, FALSE, NULL, camel_imapx_mailbox_get_permanentflags (mailbox));
+				camel_folder_summary_add (folder->summary, mi);
 
-				if (!camel_folder_summary_check_uid (folder->summary, mi->uid)) {
-					RefreshInfoData *data;
+				g_mutex_lock (&is->priv->changes_lock);
 
-					data = camel_imapx_job_get_data (job);
-					g_return_val_if_fail (data != NULL, FALSE);
+				camel_folder_change_info_add_uid (is->priv->changes, mi->uid);
+				camel_folder_change_info_recent_uid (is->priv->changes, mi->uid);
 
-					imapx_set_message_info_flags_for_new_message (mi, server_flags, server_user_flags, FALSE, NULL, camel_imapx_mailbox_get_permanentflags (mailbox));
-					camel_folder_summary_add (folder->summary, mi);
-					camel_folder_change_info_add_uid (data->changes, mi->uid);
+				g_mutex_unlock (&is->priv->changes_lock);
 
-					camel_folder_change_info_recent_uid (data->changes, mi->uid);
+				if (messages > 0) {
+					gint cnt = (camel_folder_summary_count (folder->summary) * 100) / messages;
 
-					if (messages > 0) {
-						gint cnt = (camel_folder_summary_count (folder->summary) * 100) / messages;
-						camel_operation_progress (cancellable, cnt ? cnt : 1);
-					}
-				} else {
-					camel_message_info_unref (mi);
+					camel_operation_progress (cancellable, cnt ? cnt : 1);
 				}
-
-				camel_folder_summary_unlock (folder->summary);
-
-				if (free_user_flags && server_user_flags)
-					camel_flag_list_free (&server_user_flags);
+			} else {
+				camel_message_info_unref (mi);
 			}
 
-			g_object_unref (folder);
-			g_object_unref (mailbox);
+			camel_folder_summary_unlock (folder->summary);
+
+			if (free_user_flags && server_user_flags)
+				camel_flag_list_free (&server_user_flags);
 		}
+
+		g_clear_object (&mailbox);
+		g_clear_object (&folder);
 	}
 
 	imapx_free_fetch (finfo);
@@ -2670,7 +1609,7 @@ imapx_untagged_recent (CamelIMAPXServer
 
 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
 
-	mailbox = camel_imapx_server_ref_selected (is);
+	mailbox = camel_imapx_server_ref_pending_or_selected (is);
 
 	if (mailbox == NULL) {
 		g_warning ("%s: No mailbox available", G_STRFUNC);
@@ -2679,6 +1618,11 @@ imapx_untagged_recent (CamelIMAPXServer
 
 	recent = (guint32) is->priv->context->id;
 
+	c (is->priv->tagprefix, "%s: updating mailbox '%s' recent: %d ~> %d\n", G_STRFUNC,
+		camel_imapx_mailbox_get_name (mailbox),
+		camel_imapx_mailbox_get_recent (mailbox),
+		recent);
+
 	camel_imapx_mailbox_set_recent (mailbox, recent);
 
 	g_object_unref (mailbox);
@@ -2798,7 +1742,9 @@ imapx_untagged_bye (CamelIMAPXServer *is
 	/* XXX It's weird to be setting an error on success,
 	 *     but it's to indicate the server hung up on us. */
 	if (success) {
-		c (is->tagprefix, "BYE: %s\n", token);
+		g_strstrip ((gchar *) token);
+
+		c (is->priv->tagprefix, "BYE: %s\n", token);
 		g_set_error (
 			error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT,
 			"IMAP server said BYE: %s", token);
@@ -2806,7 +1752,7 @@ imapx_untagged_bye (CamelIMAPXServer *is
 
 	g_free (token);
 
-	is->state = IMAPX_SHUTDOWN;
+	is->priv->state = IMAPX_SHUTDOWN;
 
 	return FALSE;
 }
@@ -2819,9 +1765,9 @@ imapx_untagged_preauth (CamelIMAPXServer
 {
 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
 
-	c (is->tagprefix, "preauthenticated\n");
-	if (is->state < IMAPX_AUTHENTICATED)
-		is->state = IMAPX_AUTHENTICATED;
+	c (is->priv->tagprefix, "preauthenticated\n");
+	if (is->priv->state < IMAPX_AUTHENTICATED)
+		is->priv->state = IMAPX_AUTHENTICATED;
 
 	return TRUE;
 }
@@ -2851,15 +1797,7 @@ imapx_untagged_ok_no_bad (CamelIMAPXServ
 	   overwritten with a value from a different mailbox, thus the offline
 	   cache will persist, instead of being vanished.
 	*/
-	g_mutex_lock (&is->priv->select_lock);
-
-	mailbox = g_weak_ref_get (&is->priv->select_pending);
-	if (!mailbox)
-		mailbox = g_weak_ref_get (&is->priv->select_mailbox);
-	if (!mailbox)
-		mailbox = g_weak_ref_get (&is->priv->select_closing);
-
-	g_mutex_unlock (&is->priv->select_lock);
+	mailbox = camel_imapx_server_ref_pending_or_selected (is);
 
 	is->priv->context->sinfo = imapx_parse_status (
 		CAMEL_IMAPX_INPUT_STREAM (input_stream),
@@ -2873,43 +1811,34 @@ imapx_untagged_ok_no_bad (CamelIMAPXServ
 	switch (is->priv->context->sinfo->condition) {
 	case IMAPX_CLOSED:
 		c (
-			is->tagprefix,
+			is->priv->tagprefix,
 			"previously selected mailbox is now closed\n");
 		{
 			CamelIMAPXMailbox *select_mailbox;
-			CamelIMAPXMailbox *select_closing;
 			CamelIMAPXMailbox *select_pending;
 
 			g_mutex_lock (&is->priv->select_lock);
 
-			select_mailbox =
-				g_weak_ref_get (&is->priv->select_mailbox);
-			select_closing =
-				g_weak_ref_get (&is->priv->select_closing);
-			select_pending =
-				g_weak_ref_get (&is->priv->select_pending);
-
-			if (select_mailbox == NULL)
-				g_weak_ref_set (
-					&is->priv->select_mailbox,
-					select_pending);
+			select_mailbox = g_weak_ref_get (&is->priv->select_mailbox);
+			select_pending = g_weak_ref_get (&is->priv->select_pending);
 
-			g_weak_ref_set (&is->priv->select_closing, NULL);
+			if (select_mailbox == NULL) {
+				g_weak_ref_set (&is->priv->select_mailbox, select_pending);
 
-			g_mutex_unlock (&is->priv->select_lock);
+				if (select_pending)
+					is->priv->last_selected_mailbox_change_stamp = camel_imapx_mailbox_get_change_stamp (select_pending);
+				else
+					is->priv->last_selected_mailbox_change_stamp = 0;
+			}
 
-			if (select_closing != NULL)
-				g_signal_emit (
-					is, signals[MAILBOX_CLOSED], 0,
-					select_closing);
+			g_mutex_unlock (&is->priv->select_lock);
 
 			g_clear_object (&select_mailbox);
-			g_clear_object (&select_closing);
 			g_clear_object (&select_pending);
 		}
 		break;
 	case IMAPX_ALERT:
-		c (is->tagprefix, "ALERT!: %s\n", is->priv->context->sinfo->text);
+		c (is->priv->tagprefix, "ALERT!: %s\n", is->priv->context->sinfo->text);
 		{
 			const gchar *alert_message;
 			gboolean emit_alert = FALSE;
@@ -2938,12 +1867,15 @@ imapx_untagged_ok_no_bad (CamelIMAPXServ
 				service = CAMEL_SERVICE (store);
 				session = camel_service_ref_session (service);
 
-				camel_session_user_alert (
-					session, service,
-					CAMEL_SESSION_ALERT_WARNING,
-					alert_message);
+				if (session) {
+					camel_session_user_alert (
+						session, service,
+						CAMEL_SESSION_ALERT_WARNING,
+						alert_message);
+
+					g_object_unref (session);
+				}
 
-				g_object_unref (session);
 				g_object_unref (store);
 			}
 
@@ -2951,24 +1883,48 @@ imapx_untagged_ok_no_bad (CamelIMAPXServ
 		}
 		break;
 	case IMAPX_PARSE:
-		c (is->tagprefix, "PARSE: %s\n", is->priv->context->sinfo->text);
+		c (is->priv->tagprefix, "PARSE: %s\n", is->priv->context->sinfo->text);
 		break;
 	case IMAPX_CAPABILITY:
 		if (is->priv->context->sinfo->u.cinfo) {
-			struct _capability_info *cinfo = is->cinfo;
-			is->cinfo = is->priv->context->sinfo->u.cinfo;
+			struct _capability_info *cinfo;
+
+			g_mutex_lock (&is->priv->stream_lock);
+
+			cinfo = is->priv->cinfo;
+			is->priv->cinfo = is->priv->context->sinfo->u.cinfo;
 			is->priv->context->sinfo->u.cinfo = NULL;
 			if (cinfo)
 				imapx_free_capability (cinfo);
-			c (is->tagprefix, "got capability flags %08x\n", is->cinfo ? is->cinfo->capa : 0xFFFFFFFF);
+			c (is->priv->tagprefix, "got capability flags %08x\n", is->priv->cinfo ? is->priv->cinfo->capa : 0xFFFFFFFF);
+
+			if (is->priv->context->sinfo->text) {
+				guint32 list_extended = imapx_lookup_capability ("LIST-EXTENDED");
+
+				is->priv->is_cyrus = is->priv->is_cyrus || camel_strstrcase (is->priv->context->sinfo->text, "cyrus");
+				if (is->priv->is_cyrus && is->priv->cinfo && (is->priv->cinfo->capa & list_extended) != 0) {
+					/* Disable LIST-EXTENDED for cyrus servers */
+					c (is->priv->tagprefix, "Disabling LIST-EXTENDED extension for a Cyrus server\n");
+					is->priv->cinfo->capa &= ~list_extended;
+				}
+			}
+
 			imapx_server_stash_command_arguments (is);
+
+			g_mutex_unlock (&is->priv->stream_lock);
 		}
 		break;
+	case IMAPX_COPYUID:
+		imapx_free_status (is->priv->copyuid_status);
+		is->priv->copyuid_status = is->priv->context->sinfo;
+		is->priv->context->sinfo = NULL;
+		break;
 	default:
 		break;
 	}
 
 	imapx_free_status (is->priv->context->sinfo);
+	is->priv->context->sinfo = NULL;
 
 	return TRUE;
 }
@@ -3001,7 +1957,7 @@ imapx_untagged (CamelIMAPXServer *is,
 	is->priv->context->lsub = FALSE;
 	is->priv->context->fetch_order = fetch_order;
 
-	e (is->tagprefix, "got untagged response\n");
+	e (is->priv->tagprefix, "got untagged response\n");
 	is->priv->context->id = 0;
 	is->priv->context->tok = camel_imapx_input_stream_token (
 		CAMEL_IMAPX_INPUT_STREAM (input_stream),
@@ -3025,12 +1981,12 @@ imapx_untagged (CamelIMAPXServer *is,
 
 	if (is->priv->context->tok == '\n') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"truncated server response");
 		goto exit;
 	}
 
-	e (is->tagprefix, "Have token '%s' id %lu\n", is->priv->context->token, is->priv->context->id);
+	e (is->priv->tagprefix, "Have token '%s' id %lu\n", is->priv->context->token, is->priv->context->id);
 	p = is->priv->context->token;
 	while ((c = *p))
 		*p++ = g_ascii_toupper ((gchar) c);
@@ -3042,12 +1998,12 @@ imapx_untagged (CamelIMAPXServer *is,
 		desc = g_hash_table_lookup (is->priv->untagged_handlers, token);
 		if (desc == NULL) {
 			/* unknown response, just ignore it */
-			c (is->tagprefix, "unknown token: %s\n", is->priv->context->token);
+			c (is->priv->tagprefix, "unknown token: %s\n", is->priv->context->token);
 			break;
 		}
 		if (desc->handler == NULL) {
 			/* no handler function, ignore token */
-			c (is->tagprefix, "no handler for token: %s\n", is->priv->context->token);
+			c (is->priv->tagprefix, "no handler for token: %s\n", is->priv->context->token);
 			break;
 		}
 
@@ -3085,6 +2041,61 @@ exit:
 	return success;
 }
 
+static gssize
+imapx_server_write_file_with_progress (GOutputStream *output_stream,
+				       GInputStream *input_stream,
+				       goffset file_size,
+				       GCancellable *cancellable,
+				       GError **error)
+{
+	gssize n_read;
+	gsize bytes_copied, n_written;
+	gchar buffer[8192];
+	goffset file_offset;
+	gboolean res;
+
+	g_return_val_if_fail (G_IS_OUTPUT_STREAM (output_stream), -1);
+	g_return_val_if_fail (G_IS_INPUT_STREAM (input_stream), -1);
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, error))
+		return FALSE;
+
+	file_offset = 0;
+	bytes_copied = 0;
+	res = TRUE;
+	do {
+		n_read = g_input_stream_read (input_stream, buffer, sizeof (buffer), cancellable, error);
+		if (n_read == -1) {
+			res = FALSE;
+			break;
+		}
+
+		if (n_read == 0)
+			break;
+
+		if (!g_output_stream_write_all (output_stream, buffer, n_read, &n_written, cancellable, error) || n_written == -1) {
+			res = FALSE;
+			break;
+		}
+
+		file_offset += n_read;
+
+		if (file_size > 0) {
+			gdouble divd = (gdouble) file_offset / (gdouble) file_size;
+			camel_operation_progress (cancellable, (gint) (100 * divd));
+		}
+
+		bytes_copied += n_written;
+		if (bytes_copied > G_MAXSSIZE)
+			bytes_copied = G_MAXSSIZE;
+	} while (res);
+
+	if (res)
+		return bytes_copied;
+
+	return -1;
+}
+
 /* handle any continuation requests
  * either data continuations, or auth continuation */
 static gboolean
@@ -3095,7 +2106,7 @@ imapx_continuation (CamelIMAPXServer *is
                     GCancellable *cancellable,
                     GError **error)
 {
-	CamelIMAPXCommand *ic, *newliteral = NULL;
+	CamelIMAPXCommand *ic, *newic = NULL;
 	CamelIMAPXCommandPart *cp;
 	GList *link;
 	gssize n_bytes_written;
@@ -3105,7 +2116,7 @@ imapx_continuation (CamelIMAPXServer *is
 	 * can write while we have it ... so we dont need any
 	 * ohter lock here.  All other writes go through
 	 * queue-lock */
-	if (imapx_in_idle (is)) {
+	if (camel_imapx_server_is_in_idle (is)) {
 		success = camel_imapx_input_stream_skip (
 			CAMEL_IMAPX_INPUT_STREAM (input_stream),
 			cancellable, error);
@@ -3113,57 +2124,38 @@ imapx_continuation (CamelIMAPXServer *is
 		if (!success)
 			return FALSE;
 
-		c (is->tagprefix, "Got continuation response for IDLE \n");
-		g_rec_mutex_lock (&is->priv->idle_lock);
-		/* We might have actually sent the DONE already! */
-		if (is->priv->idle_state == IMAPX_IDLE_ISSUED) {
-			is->priv->idle_state = IMAPX_IDLE_STARTED;
-		} else if (is->priv->idle_state == IMAPX_IDLE_CANCEL) {
-			/* IDLE got cancelled after we sent the command, while
-			 * we were waiting for this continuation. Send DONE
-			 * immediately. */
-			if (!imapx_command_idle_stop (is, error)) {
-				g_rec_mutex_unlock (&is->priv->idle_lock);
-				return FALSE;
-			}
-			is->priv->idle_state = IMAPX_IDLE_WAIT_DONE;
-		} else if (is->priv->idle_state == IMAPX_IDLE_WAIT_DONE) {
-			/* Do nothing, just wait */
-		} else {
-			c (
-				is->tagprefix, "idle starts in wrong state %d\n",
-				is->priv->idle_state);
-		}
-		g_rec_mutex_unlock (&is->priv->idle_lock);
+		c (is->priv->tagprefix, "Got continuation response for IDLE \n");
 
-		QUEUE_LOCK (is);
-		is->literal = NULL;
-		imapx_command_start_next (is);
-		QUEUE_UNLOCK (is);
+		g_mutex_lock (&is->priv->idle_lock);
+		is->priv->idle_state = IMAPX_IDLE_STATE_RUNNING;
+		g_cond_broadcast (&is->priv->idle_cond);
+		g_mutex_unlock (&is->priv->idle_lock);
 
 		return TRUE;
 	}
 
-	ic = is->literal;
+	ic = is->priv->continuation_command;
 	if (!litplus) {
 		if (ic == NULL) {
-			c (is->tagprefix, "got continuation response with no outstanding continuation requests?\n");
+			c (is->priv->tagprefix, "got continuation response with no outstanding continuation requests?\n");
 			return camel_imapx_input_stream_skip (
 				CAMEL_IMAPX_INPUT_STREAM (input_stream),
 				cancellable, error);
 		}
-		c (is->tagprefix, "got continuation response for data\n");
+		c (is->priv->tagprefix, "got continuation response for data\n");
 	} else {
-		c (is->tagprefix, "sending LITERAL+ continuation\n");
+		c (is->priv->tagprefix, "sending LITERAL+ continuation\n");
+		g_return_val_if_fail (ic != NULL, FALSE);
 	}
 
-	link = ic->current_part;
+	/* coverity[deadcode] */
+	link = ic ? ic->current_part : NULL;
 	g_return_val_if_fail (link != NULL, FALSE);
 	cp = (CamelIMAPXCommandPart *) link->data;
 
 	switch (cp->type & CAMEL_IMAPX_COMMAND_MASK) {
 	case CAMEL_IMAPX_COMMAND_DATAWRAPPER:
-		c (is->tagprefix, "writing data wrapper to literal\n");
+		c (is->priv->tagprefix, "writing data wrapper to literal\n");
 		n_bytes_written =
 			camel_data_wrapper_write_to_output_stream_sync (
 				CAMEL_DATA_WRAPPER (cp->ob),
@@ -3188,7 +2180,7 @@ imapx_continuation (CamelIMAPXServer *is
 		g_free (token);
 		if (resp == NULL)
 			return FALSE;
-		c (is->tagprefix, "got auth continuation, feeding token '%s' back to auth mech\n", resp);
+		c (is->priv->tagprefix, "got auth continuation, feeding token '%s' back to auth mech\n", resp);
 
 		g_mutex_lock (&is->priv->stream_lock);
 		n_bytes_written = g_output_stream_write_all (
@@ -3202,15 +2194,17 @@ imapx_continuation (CamelIMAPXServer *is
 
 		/* we want to keep getting called until we get a status reponse from the server
 		 * ignore what sasl tells us */
-		newliteral = ic;
+		newic = ic;
 		/* We already ate the end of the input stream line */
 		goto noskip;
 		break; }
 	case CAMEL_IMAPX_COMMAND_FILE: {
 		GFile *file;
+		GFileInfo *file_info;
 		GFileInputStream *file_input_stream;
+		goffset file_size = 0;
 
-		c (is->tagprefix, "writing file '%s' to literal\n", (gchar *) cp->ob);
+		c (is->priv->tagprefix, "writing file '%s' to literal\n", (gchar *) cp->ob);
 
 		file = g_file_new_for_path (cp->ob);
 		file_input_stream = g_file_read (file, cancellable, error);
@@ -3219,14 +2213,22 @@ imapx_continuation (CamelIMAPXServer *is
 		if (file_input_stream == NULL)
 			return FALSE;
 
+		file_info = g_file_input_stream_query_info (file_input_stream,
+			G_FILE_ATTRIBUTE_STANDARD_SIZE, cancellable, NULL);
+		if (file_info) {
+			file_size = g_file_info_get_size (file_info);
+			g_object_unref (file_info);
+		}
+
 		g_mutex_lock (&is->priv->stream_lock);
-		n_bytes_written = g_output_stream_splice (
-			output_stream,
-			G_INPUT_STREAM (file_input_stream),
-			G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
-			cancellable, error);
+
+		n_bytes_written = imapx_server_write_file_with_progress (
+			output_stream, G_INPUT_STREAM (file_input_stream),
+			file_size, cancellable, error);
+
 		g_mutex_unlock (&is->priv->stream_lock);
 
+		g_input_stream_close (G_INPUT_STREAM (file_input_stream), cancellable, NULL);
 		g_object_unref (file_input_stream);
 
 		if (n_bytes_written < 0)
@@ -3244,9 +2246,8 @@ imapx_continuation (CamelIMAPXServer *is
 		break;
 	default:
 		/* should we just ignore? */
-		is->literal = NULL;
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"continuation response for non-continuation request");
 		return FALSE;
 	}
@@ -3266,7 +2267,7 @@ noskip:
 		ic->current_part = link;
 		cp = (CamelIMAPXCommandPart *) link->data;
 
-		c (is->tagprefix, "next part of command \"%c%05u: %s\"\n", is->tagprefix, ic->tag, cp->data);
+		c (is->priv->tagprefix, "next part of command \"%c%05u: %s\"\n", is->priv->tagprefix, ic->tag, cp->data);
 
 		g_mutex_lock (&is->priv->stream_lock);
 		n_bytes_written = g_output_stream_write_all (
@@ -3277,12 +2278,12 @@ noskip:
 			return FALSE;
 
 		if (cp->type & (CAMEL_IMAPX_COMMAND_CONTINUATION | CAMEL_IMAPX_COMMAND_LITERAL_PLUS)) {
-			newliteral = ic;
+			newic = ic;
 		} else {
-			g_assert (g_list_next (link) == NULL);
+			g_warn_if_fail (g_list_next (link) == NULL);
 		}
 	} else {
-		c (is->tagprefix, "%p: queueing continuation\n", ic);
+		c (is->priv->tagprefix, "%p: queueing continuation\n", ic);
 	}
 
 	g_mutex_lock (&is->priv->stream_lock);
@@ -3292,12 +2293,7 @@ noskip:
 	if (n_bytes_written < 0)
 		return FALSE;
 
-	QUEUE_LOCK (is);
-	is->literal = newliteral;
-
-	if (!litplus)
-		imapx_command_start_next (is);
-	QUEUE_UNLOCK (is);
+	is->priv->continuation_command = newic;
 
 	return TRUE;
 }
@@ -3318,80 +2314,74 @@ imapx_completion (CamelIMAPXServer *is,
 
 	/* Given "A0001 ...", 'A' = tag prefix, '0001' = tag. */
 
-	if (token[0] != is->tagprefix) {
+	if (token[0] != is->priv->tagprefix) {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"Server sent unexpected response: %s", token);
 		return FALSE;
 	}
 
 	tag = strtoul ((gchar *) token + 1, NULL, 10);
 
-	QUEUE_LOCK (is);
+	COMMAND_LOCK (is);
 
-	if (is->literal != NULL && is->literal->tag == tag)
-		ic = camel_imapx_command_ref (is->literal);
+	if (is->priv->current_command != NULL && is->priv->current_command->tag == tag)
+		ic = camel_imapx_command_ref (is->priv->current_command);
 	else
-		ic = camel_imapx_command_queue_ref_by_tag (is->active, tag);
+		ic = NULL;
 
-	QUEUE_UNLOCK (is);
+	COMMAND_UNLOCK (is);
 
 	if (ic == NULL) {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"got response tag unexpectedly: %s", token);
 		return FALSE;
 	}
 
-	c (is->tagprefix, "Got completion response for command %05u '%s'\n", ic->tag, ic->name);
+	c (is->priv->tagprefix, "Got completion response for command %05u '%s'\n", ic->tag, camel_imapx_job_get_kind_name (ic->job_kind));
+
+	g_mutex_lock (&is->priv->changes_lock);
 
 	if (camel_folder_change_info_changed (is->priv->changes)) {
-		CamelFolder *folder;
+		CamelFolder *folder = NULL;
 		CamelIMAPXMailbox *mailbox;
+		CamelFolderChangeInfo *changes;
 
-		g_mutex_lock (&is->priv->select_lock);
-		mailbox = g_weak_ref_get (&is->priv->select_mailbox);
-		g_mutex_unlock (&is->priv->select_lock);
+		changes = is->priv->changes;
+		is->priv->changes = camel_folder_change_info_new ();
 
-		g_return_val_if_fail (mailbox != NULL, FALSE);
+		g_mutex_unlock (&is->priv->changes_lock);
 
-		folder = imapx_server_ref_folder (is, mailbox);
-		g_return_val_if_fail (folder != NULL, FALSE);
+		mailbox = camel_imapx_server_ref_selected (is);
 
-		camel_folder_summary_save_to_db (folder->summary, NULL);
+		g_warn_if_fail (mailbox != NULL);
 
-		imapx_update_store_summary (folder);
-		camel_folder_changed (folder, is->priv->changes);
-		camel_folder_change_info_clear (is->priv->changes);
+		if (mailbox) {
+			folder = imapx_server_ref_folder (is, mailbox);
+			g_return_val_if_fail (folder != NULL, FALSE);
 
-		g_object_unref (folder);
-		g_object_unref (mailbox);
-	}
+			camel_folder_summary_save_to_db (folder->summary, NULL);
 
-	QUEUE_LOCK (is);
+			imapx_update_store_summary (folder);
+			camel_folder_changed (folder, changes);
+		}
 
-	/* Move the command from the active queue to the done queue.
-	 * We're holding our own reference to the command so there's
-	 * no risk of accidentally finalizing it here. */
-	camel_imapx_command_queue_remove (is->active, ic);
-	imapx_server_command_removed (is, ic);
-	camel_imapx_command_queue_push_tail (is->done, ic);
+		camel_folder_change_info_free (changes);
 
-	if (is->literal == ic)
-		is->literal = NULL;
+		g_clear_object (&folder);
+		g_clear_object (&mailbox);
+	} else {
+		g_mutex_unlock (&is->priv->changes_lock);
+	}
 
 	if (g_list_next (ic->current_part) != NULL) {
-		QUEUE_UNLOCK (is);
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
-			"command still has unsent parts? %s", ic->name);
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
+			"command still has unsent parts? %s", camel_imapx_job_get_kind_name (ic->job_kind));
 		goto exit;
 	}
 
-	camel_imapx_command_queue_remove (is->done, ic);
-
-	QUEUE_UNLOCK (is);
-
 	mailbox = camel_imapx_server_ref_selected (is);
 
 	ic->status = imapx_parse_status (
@@ -3403,16 +2393,22 @@ imapx_completion (CamelIMAPXServer *is,
 	if (ic->status == NULL)
 		goto exit;
 
-	if (ic->complete != NULL)
-		ic->complete (is, ic);
+	if (ic->status->condition == IMAPX_CAPABILITY) {
+		guint32 list_extended = imapx_lookup_capability ("LIST-EXTENDED");
+
+		is->priv->is_cyrus = is->priv->is_cyrus || (ic->status->text && camel_strstrcase (ic->status->text, "cyrus"));
+		if (is->priv->is_cyrus && ic->status->u.cinfo && (ic->status->u.cinfo->capa & list_extended) != 0) {
+			/* Disable LIST-EXTENDED for cyrus servers */
+			c (is->priv->tagprefix, "Disabling LIST-EXTENDED extension for a Cyrus server\n");
+			ic->status->u.cinfo->capa &= ~list_extended;
+		}
+	}
 
 	success = TRUE;
 
 exit:
-	QUEUE_LOCK (is);
-	imapx_command_start_next (is);
-	QUEUE_UNLOCK (is);
 
+	ic->completed = TRUE;
 	camel_imapx_command_unref (ic);
 
 	return success;
@@ -3421,23 +2417,19 @@ exit:
 static gboolean
 imapx_step (CamelIMAPXServer *is,
             GInputStream *input_stream,
+	    GOutputStream *output_stream,
             GCancellable *cancellable,
             GError **error)
 {
-	GOutputStream *output_stream;
 	guint len;
 	guchar *token;
 	gint tok;
 	gboolean success = FALSE;
 
-	// poll ? wait for other stuff? loop?
 	tok = camel_imapx_input_stream_token (
 		CAMEL_IMAPX_INPUT_STREAM (input_stream),
 		&token, &len, cancellable, error);
 
-	output_stream = camel_imapx_server_ref_output_stream (is);
-	g_return_val_if_fail (output_stream != NULL, FALSE);
-
 	switch (tok) {
 		case IMAPX_TOK_ERROR:
 			/* GError is already set. */
@@ -3458,6171 +2450,4070 @@ imapx_step (CamelIMAPXServer *is,
 			break;
 		default:
 			g_set_error (
-				error, CAMEL_IMAPX_ERROR, 1,
+				error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 				"unexpected server response:");
 			break;
 	}
 
-	g_clear_object (&output_stream);
-
 	return success;
 }
 
-/* Used to run 1 command synchronously,
- * use for capa, login, and namespaces only. */
-static gboolean
-imapx_command_run (CamelIMAPXServer *is,
-                   CamelIMAPXCommand *ic,
-                   GCancellable *cancellable,
-                   GError **error)
+static void
+imapx_server_set_streams (CamelIMAPXServer *is,
+                          GInputStream *input_stream,
+                          GOutputStream *output_stream)
 {
-	GInputStream *input_stream;
-	gboolean success = TRUE;
-
-	input_stream = camel_imapx_server_ref_input_stream (is);
-	g_return_val_if_fail (input_stream != NULL, FALSE);
-
-	camel_imapx_command_close (ic);
-
-	QUEUE_LOCK (is);
-	imapx_command_start (is, ic);
-	QUEUE_UNLOCK (is);
-
-	while (success && ic->status == NULL)
-		success = imapx_step (is, input_stream, cancellable, error);
-
-	if (is->literal == ic)
-		is->literal = NULL;
-
-	QUEUE_LOCK (is);
-	camel_imapx_command_queue_remove (is->active, ic);
-	imapx_server_command_removed (is, ic);
-	QUEUE_UNLOCK (is);
-
-	g_object_unref (input_stream);
+	GConverter *logger;
 
-	return success;
-}
+	if (input_stream != NULL) {
+		GInputStream *temp_stream;
 
-static void
-imapx_command_complete (CamelIMAPXServer *is,
-                        CamelIMAPXCommand *ic)
-{
-	camel_imapx_command_done (ic);
-	camel_imapx_command_unref (ic);
-}
+		/* The logger produces debugging output. */
+		logger = camel_imapx_logger_new (is->priv->tagprefix);
+		input_stream = g_converter_input_stream_new (
+			input_stream, logger);
+		g_clear_object (&logger);
 
-static void
-imapx_command_cancelled (GCancellable *cancellable,
-                         CamelIMAPXCommand *ic)
-{
-	/* Unblock imapx_command_run_sync() immediately.
-	 *
-	 * If camel_imapx_command_done() is called sometime later,
-	 * the GCond will broadcast but no one will be listening. */
+		/* Buffer the input stream for parsing. */
+		temp_stream = camel_imapx_input_stream_new (input_stream);
+		camel_binding_bind_property (
+			temp_stream, "close-base-stream",
+			input_stream, "close-base-stream",
+			G_BINDING_SYNC_CREATE);
+		g_object_unref (input_stream);
+		input_stream = temp_stream;
+	}
 
-	camel_imapx_command_done (ic);
-}
+	if (output_stream != NULL) {
+		/* The logger produces debugging output. */
+		logger = camel_imapx_logger_new (is->priv->tagprefix);
+		output_stream = g_converter_output_stream_new (
+			output_stream, logger);
+		g_clear_object (&logger);
+	}
 
-/* The caller should free the command as well */
-static gboolean
-imapx_command_run_sync (CamelIMAPXServer *is,
-                        CamelIMAPXCommand *ic,
-                        GCancellable *cancellable,
-                        GError **error)
-{
-	guint cancel_id = 0;
-	gboolean success = TRUE;
+	g_mutex_lock (&is->priv->stream_lock);
 
-	/* FIXME The only caller of this function currently does not set
-	 *       a "complete" callback function, so we can get away with
-	 *       referencing the command here and dropping the reference
-	 *       in imapx_command_complete().  The queueing/dequeueing
-	 *       of these things is too complex for my little mind, so
-	 *       we may have to revisit the reference counting if this
-	 *       function gets another caller. */
-
-	g_warn_if_fail (ic->complete == NULL);
-	ic->complete = imapx_command_complete;
-
-	if (G_IS_CANCELLABLE (cancellable))
-		cancel_id = g_cancellable_connect (
-			cancellable,
-			G_CALLBACK (imapx_command_cancelled),
-			camel_imapx_command_ref (ic),
-			(GDestroyNotify) camel_imapx_command_unref);
-
-	/* Unref'ed in imapx_command_complete(). */
-	camel_imapx_command_ref (ic);
+	/* Don't close the base streams so STARTTLS works correctly. */
 
-	imapx_command_queue (is, ic);
+	if (G_IS_FILTER_INPUT_STREAM (is->priv->input_stream)) {
+		g_filter_input_stream_set_close_base_stream (
+			G_FILTER_INPUT_STREAM (is->priv->input_stream),
+			FALSE);
+	}
 
-	camel_imapx_command_wait (ic);
+	if (G_IS_FILTER_OUTPUT_STREAM (is->priv->output_stream)) {
+		g_filter_output_stream_set_close_base_stream (
+			G_FILTER_OUTPUT_STREAM (is->priv->output_stream),
+			FALSE);
+	}
 
-	if (cancel_id > 0)
-		g_cancellable_disconnect (cancellable, cancel_id);
+	g_clear_object (&is->priv->input_stream);
+	is->priv->input_stream = input_stream;
 
-	if (camel_imapx_command_set_error_if_failed (ic, error))
-		return FALSE;
+	g_clear_object (&is->priv->output_stream);
+	is->priv->output_stream = output_stream;
 
-	return success;
+	g_mutex_unlock (&is->priv->stream_lock);
 }
 
-static gboolean
-imapx_ensure_mailbox_permanentflags (CamelIMAPXServer *is,
-				     CamelIMAPXMailbox *mailbox,
-				     GCancellable *cancellable,
-				     GError **error)
+#ifdef G_OS_UNIX
+static void
+imapx_server_child_process_setup (gpointer user_data)
 {
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+#ifdef TIOCNOTTY
+	gint fd;
+#endif
 
-	if (camel_imapx_mailbox_get_permanentflags (mailbox) != ~0)
-		return TRUE;
+	setsid ();
 
-	/* This will also invoke SELECT command, which updates PERMANENTFLAGS
-	   for the mailbox. There might be possible to use EXAMINE for it,
-	   but some servers do not return the same set of flags as with SELECT.
-	   It's a little hack on top of the IMAPx implementation. */
-	return camel_imapx_server_noop (is, mailbox, cancellable, error);
+#ifdef TIOCNOTTY
+	/* Detach from the controlling tty if we have one.  Otherwise,
+	 * SSH might do something stupid like trying to use it instead
+	 * of running $SSH_ASKPASS. */
+	if ((fd = open ("/dev/tty", O_RDONLY)) != -1) {
+		ioctl (fd, TIOCNOTTY, NULL);
+		close (fd);
+	}
+#endif /* TIOCNOTTY */
 }
+#endif /* G_OS_UNIX */
 
-/* ********************************************************************** */
-// IDLE support
-
-/*TODO handle negative cases sanely */
 static gboolean
-imapx_command_idle_stop (CamelIMAPXServer *is,
-                         GError **error)
-{
-	GOutputStream *output_stream;
-	GCancellable *cancellable;
-	gboolean success;
+connect_to_server_process (CamelIMAPXServer *is,
+                           const gchar *cmd,
+                           GError **error)
+{
+	GSubprocessLauncher *launcher;
+	GSubprocess *subprocess = NULL;
+	CamelNetworkSettings *network_settings;
+	CamelProvider *provider;
+	CamelSettings *settings;
+	CamelIMAPXStore *store;
+	CamelURL url;
+	gchar **argv = NULL;
+	gchar *buf;
+	gchar *cmd_copy;
+	gchar *full_cmd;
+	const gchar *password;
+	gchar *host;
+	gchar *user;
+	guint16 port;
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	memset (&url, 0, sizeof (CamelURL));
 
-	output_stream = camel_imapx_server_ref_output_stream (is);
-	g_return_val_if_fail (output_stream != NULL, FALSE);
+	launcher = g_subprocess_launcher_new (
+		G_SUBPROCESS_FLAGS_STDIN_PIPE |
+		G_SUBPROCESS_FLAGS_STDOUT_PIPE |
+		G_SUBPROCESS_FLAGS_STDERR_SILENCE);
 
-	cancellable = g_weak_ref_get (&is->priv->parser_cancellable);
+#ifdef G_OS_UNIX
+	g_subprocess_launcher_set_child_setup (
+		launcher, imapx_server_child_process_setup,
+		NULL, (GDestroyNotify) NULL);
+#endif
 
-	g_mutex_lock (&is->priv->stream_lock);
-	success = g_output_stream_write_all (
-		output_stream, "DONE\r\n", 6, NULL, cancellable, error);
-	g_mutex_unlock (&is->priv->stream_lock);
+	store = camel_imapx_server_ref_store (is);
 
-	if (!success) {
-		g_prefix_error (error, "Unable to issue DONE: ");
-		c (is->tagprefix, "Failed to issue DONE to terminate IDLE\n");
-		is->state = IMAPX_SHUTDOWN;
-		g_main_loop_quit (is->priv->parser_main_loop);
-	}
+	password = camel_service_get_password (CAMEL_SERVICE (store));
+	provider = camel_service_get_provider (CAMEL_SERVICE (store));
+	settings = camel_service_ref_settings (CAMEL_SERVICE (store));
 
-	g_clear_object (&cancellable);
-	g_clear_object (&output_stream);
+	network_settings = CAMEL_NETWORK_SETTINGS (settings);
+	host = camel_network_settings_dup_host (network_settings);
+	port = camel_network_settings_get_port (network_settings);
+	user = camel_network_settings_dup_user (network_settings);
 
-	return success;
-}
+	/* Put full details in the environment, in case the connection
+	 * program needs them */
+	camel_url_set_protocol (&url, provider->protocol);
+	camel_url_set_host (&url, host);
+	camel_url_set_port (&url, port);
+	camel_url_set_user (&url, user);
+	buf = camel_url_to_string (&url, 0);
 
-static void
-imapx_command_idle_done (CamelIMAPXServer *is,
-                         CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	GError *local_error = NULL;
+	g_subprocess_launcher_setenv (launcher, "URL", buf, TRUE);
+	g_subprocess_launcher_setenv (launcher, "URLHOST", host, TRUE);
 
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+	if (port > 0) {
+		gchar *port_string;
 
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error performing IDLE"));
-		camel_imapx_job_take_error (job, local_error);
+		port_string = g_strdup_printf ("%u", port);
+		g_subprocess_launcher_setenv (
+			launcher, "URLPORT", port_string, TRUE);
+		g_free (port_string);
 	}
 
-	g_rec_mutex_lock (&is->priv->idle_lock);
-	is->priv->idle_state = IMAPX_IDLE_OFF;
-	g_rec_mutex_unlock (&is->priv->idle_lock);
+	if (user != NULL) {
+		g_subprocess_launcher_setenv (
+			launcher, "URLPORT", user, TRUE);
+	}
 
-	imapx_unregister_job (is, job);
-}
+	if (password != NULL) {
+		g_subprocess_launcher_setenv (
+			launcher, "URLPASSWD", password, TRUE);
+	}
 
-static gboolean
-imapx_job_idle_start (CamelIMAPXJob *job,
-                      CamelIMAPXServer *is,
-                      GCancellable *cancellable,
-                      GError **error)
-{
-	CamelIMAPXCommand *ic;
-	CamelIMAPXCommandPart *cp;
-	CamelIMAPXMailbox *mailbox;
-	gboolean success = TRUE;
+	g_free (buf);
 
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_val_if_fail (mailbox != NULL, FALSE);
+	g_object_unref (settings);
+	g_object_unref (store);
 
-	ic = camel_imapx_command_new (
-		is, "IDLE", mailbox, "IDLE");
-	camel_imapx_command_set_job (ic, job);
-	ic->pri = job->pri;
-	ic->complete = imapx_command_idle_done;
+	/* Now do %h, %u, etc. substitution in cmd */
+	buf = cmd_copy = g_strdup (cmd);
 
-	camel_imapx_command_close (ic);
-	cp = g_queue_peek_head (&ic->parts);
-	cp->type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
+	full_cmd = g_strdup ("");
 
-	QUEUE_LOCK (is);
-	g_rec_mutex_lock (&is->priv->idle_lock);
-	/* Don't issue it if the idle was cancelled already */
-	if (is->priv->idle_state == IMAPX_IDLE_PENDING) {
-		is->priv->idle_state = IMAPX_IDLE_ISSUED;
+	for (;;) {
+		gchar *pc;
+		gchar *tmp;
+		const gchar *var;
+		gint len;
 
-		if (camel_imapx_command_queue_is_empty (is->active)) {
-			imapx_command_start (is, ic);
-		} else {
-			c (is->tagprefix, "finally cancelling IDLE, other command was quicker\n");
-			is->priv->idle_state = IMAPX_IDLE_OFF;
-			imapx_unregister_job (is, job);
+		pc = strchr (buf, '%');
+	ignore:
+		if (!pc) {
+			tmp = g_strdup_printf ("%s%s", full_cmd, buf);
+			g_free (full_cmd);
+			full_cmd = tmp;
+			break;
 		}
-	} else {
-		imapx_unregister_job (is, job);
-	}
-	g_rec_mutex_unlock (&is->priv->idle_lock);
-	QUEUE_UNLOCK (is);
 
-	camel_imapx_command_unref (ic);
+		len = pc - buf;
 
-	g_object_unref (mailbox);
+		var = NULL;
 
-	return success;
-}
+		switch (pc[1]) {
+		case 'h':
+			var = host;
+			break;
+		case 'u':
+			var = user;
+			break;
+		}
+		if (!var) {
+			/* If there wasn't a valid %-code, with an actual
+			 * variable to insert, pretend we didn't see the % */
+			pc = strchr (pc + 1, '%');
+			goto ignore;
+		}
+		tmp = g_strdup_printf ("%s%.*s%s", full_cmd, len, buf, var);
+		g_free (full_cmd);
+		full_cmd = tmp;
+		buf = pc + 2;
+	}
 
-static gboolean
-camel_imapx_server_idle (CamelIMAPXServer *is,
-                         CamelIMAPXMailbox *mailbox,
-                         GCancellable *cancellable,
-                         GError **error)
-{
-	CamelIMAPXJob *job;
-	gint previous_connection_timeout;
-	gboolean success;
+	g_free (cmd_copy);
 
-	job = camel_imapx_job_new (cancellable);
-	job->type = IMAPX_JOB_IDLE;
-	job->start = imapx_job_idle_start;
+	g_free (host);
+	g_free (user);
 
-	camel_imapx_job_set_mailbox (job, mailbox);
+	if (g_shell_parse_argv (full_cmd, NULL, &argv, error)) {
+		subprocess = g_subprocess_launcher_spawnv (
+			launcher, (const gchar * const *) argv, error);
+		g_strfreev (argv);
+	}
 
-	previous_connection_timeout = imapx_server_set_connection_timeout (is->priv->connection, 0);
+	g_free (full_cmd);
+	g_object_unref (launcher);
 
-	success = imapx_submit_job (is, job, error);
+	if (subprocess != NULL) {
+		GInputStream *input_stream;
+		GOutputStream *output_stream;
 
-	if (previous_connection_timeout >= 0)
-		imapx_server_set_connection_timeout (is->priv->connection, previous_connection_timeout);
+		g_mutex_lock (&is->priv->stream_lock);
+		g_warn_if_fail (is->priv->subprocess == NULL);
+		is->priv->subprocess = g_object_ref (subprocess);
+		g_mutex_unlock (&is->priv->stream_lock);
 
-	camel_imapx_job_unref (job);
+		input_stream = g_subprocess_get_stdout_pipe (subprocess);
+		output_stream = g_subprocess_get_stdin_pipe (subprocess);
 
-	return success;
-}
+		imapx_server_set_streams (is, input_stream, output_stream);
 
-static gboolean
-imapx_job_fetch_new_messages_matches (CamelIMAPXJob *job,
-                                      CamelIMAPXMailbox *mailbox,
-                                      const gchar *uid)
-{
-	return camel_imapx_job_has_mailbox (job, mailbox);
+		g_object_unref (subprocess);
+	}
+
+	return TRUE;
 }
 
 static gboolean
-imapx_server_fetch_new_messages (CamelIMAPXServer *is,
-                                 CamelIMAPXMailbox *mailbox,
-                                 gboolean async,
-                                 gboolean update_unseen,
-                                 GCancellable *cancellable,
-                                 GError **error)
+imapx_connect_to_server (CamelIMAPXServer *is,
+                         GCancellable *cancellable,
+                         GError **error)
 {
-	CamelIMAPXJob *job;
-	RefreshInfoData *data;
-	gboolean success;
+	CamelNetworkSettings *network_settings;
+	CamelNetworkSecurityMethod method;
+	CamelIMAPXStore *store;
+	CamelSettings *settings;
+	GIOStream *connection = NULL;
+	GIOStream *tls_stream;
+	GSocket *socket;
+	guint len;
+	guchar *token;
+	gint tok;
+	CamelIMAPXCommand *ic;
+	gchar *shell_command = NULL;
+	gboolean use_shell_command;
+	gboolean success = TRUE;
+	gchar *host;
 
-	data = g_slice_new0 (RefreshInfoData);
-	data->changes = camel_folder_change_info_new ();
-	data->update_unseen = update_unseen;
+	store = camel_imapx_server_ref_store (is);
 
-	job = camel_imapx_job_new (cancellable);
-	job->type = IMAPX_JOB_FETCH_NEW_MESSAGES;
-	job->start = imapx_job_fetch_new_messages_start;
-	job->matches = imapx_job_fetch_new_messages_matches;
-	job->noreply = async;
+	settings = camel_service_ref_settings (CAMEL_SERVICE (store));
 
-	camel_imapx_job_set_mailbox (job, mailbox);
+	network_settings = CAMEL_NETWORK_SETTINGS (settings);
+	host = camel_network_settings_dup_host (network_settings);
+	method = camel_network_settings_get_security_method (network_settings);
 
-	camel_imapx_job_set_data (
-		job, data, (GDestroyNotify) refresh_info_data_free);
+	use_shell_command = camel_imapx_settings_get_use_shell_command (
+		CAMEL_IMAPX_SETTINGS (settings));
 
-	success = imapx_submit_job (is, job, error);
+	if (use_shell_command)
+		shell_command = camel_imapx_settings_dup_shell_command (
+			CAMEL_IMAPX_SETTINGS (settings));
 
-	camel_imapx_job_unref (job);
+	g_object_unref (settings);
 
-	return success;
-}
+	if (shell_command != NULL) {
+		success = connect_to_server_process (is, shell_command, error);
 
-static gboolean
-imapx_call_idle (gpointer data)
-{
-	CamelFolder *folder;
-	CamelIMAPXServer *is;
-	CamelIMAPXMailbox *mailbox;
-	GCancellable *cancellable;
-	GError *local_error = NULL;
+		g_free (shell_command);
 
-	is = g_weak_ref_get (data);
+		if (success)
+			goto connected;
+		else
+			goto exit;
+	}
 
-	if (is == NULL)
-		goto exit;
+	connection = camel_network_service_connect_sync (
+		CAMEL_NETWORK_SERVICE (store), cancellable, error);
 
-	/* XXX Rename to 'pending_lock'? */
-	g_rec_mutex_lock (&is->priv->idle_lock);
-	g_source_unref (is->priv->idle_pending);
-	is->priv->idle_pending = NULL;
+	if (connection != NULL) {
+		GInputStream *input_stream;
+		GOutputStream *output_stream;
+		GError *local_error = NULL;
 
-	if (is->priv->idle_state != IMAPX_IDLE_PENDING) {
-		g_rec_mutex_unlock (&is->priv->idle_lock);
-		goto exit;
-	}
+		/* Disable the Nagle algorithm with TCP_NODELAY, since IMAP
+		 * commands should be issued immediately even we've not yet
+		 * received a response to a previous command. */
+		socket = g_socket_connection_get_socket (
+			G_SOCKET_CONNECTION (connection));
+		g_socket_set_option (
+			socket, IPPROTO_TCP, TCP_NODELAY, 1, &local_error);
+		if (local_error != NULL) {
+			/* Failure to set the socket option is non-fatal. */
+			g_warning ("%s: %s", G_STRFUNC, local_error->message);
+			g_clear_error (&local_error);
+		}
 
-	g_rec_mutex_unlock (&is->priv->idle_lock);
+		g_mutex_lock (&is->priv->stream_lock);
+		g_warn_if_fail (is->priv->connection == NULL);
+		is->priv->connection = g_object_ref (connection);
+		g_mutex_unlock (&is->priv->stream_lock);
 
-	g_mutex_lock (&is->priv->select_lock);
-	mailbox = g_weak_ref_get (&is->priv->select_mailbox);
-	g_mutex_unlock (&is->priv->select_lock);
+		input_stream = g_io_stream_get_input_stream (connection);
+		output_stream = g_io_stream_get_output_stream (connection);
 
-	if (mailbox == NULL)
-		goto exit;
+		imapx_server_set_streams (is, input_stream, output_stream);
 
-	folder = imapx_server_ref_folder (is, mailbox);
-	if (folder == NULL)
+		/* Hang on to the connection reference in case we need to
+		 * issue STARTTLS below. */
+	} else {
+		success = FALSE;
 		goto exit;
+	}
 
-	cancellable = g_weak_ref_get (&is->priv->parser_cancellable);
+connected:
+	while (1) {
+		GInputStream *input_stream;
 
-	/* We block here until the IDLE command completes. */
-	camel_imapx_server_idle (is, mailbox, cancellable, &local_error);
-
-	if (local_error == NULL) {
-		gboolean have_new_messages;
-		gboolean fetch_new_messages;
+		input_stream = camel_imapx_server_ref_input_stream (is);
 
-		have_new_messages =
-			camel_imapx_mailbox_get_messages (mailbox) >
-			camel_folder_summary_count (folder->summary);
+		token = NULL;
+		tok = camel_imapx_input_stream_token (
+			CAMEL_IMAPX_INPUT_STREAM (input_stream),
+			&token, &len, cancellable, error);
 
-		fetch_new_messages =
-			have_new_messages &&
-			imapx_is_command_queue_empty (is);
+		if (tok < 0) {
+			success = FALSE;
 
-		if (fetch_new_messages)
-			imapx_server_fetch_new_messages (
-				is, mailbox, TRUE, TRUE,
-				cancellable, &local_error);
-	}
+		} else if (tok == '*') {
+			success = imapx_untagged (
+				is, input_stream, cancellable, error);
 
-	/* XXX Need a better way to propagate IDLE errors. */
-	if (local_error != NULL) {
-		if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
-		    is->state != IMAPX_SHUTDOWN)
-			g_warning ("%s: %s", G_STRFUNC, local_error->message);
-		g_clear_error (&local_error);
-	}
+			if (success) {
+				g_object_unref (input_stream);
+				break;
+			}
 
-	g_clear_object (&folder);
-	g_clear_object (&cancellable);
+		} else {
+			camel_imapx_input_stream_ungettoken (
+				CAMEL_IMAPX_INPUT_STREAM (input_stream),
+				tok, token, len);
 
-exit:
-	g_clear_object (&is);
+			success = camel_imapx_input_stream_text (
+				CAMEL_IMAPX_INPUT_STREAM (input_stream),
+				&token, cancellable, error);
 
-	return G_SOURCE_REMOVE;
-}
+			g_free (token);
+		}
 
-static gpointer
-imapx_idle_thread (gpointer data)
-{
-	CamelIMAPXServer *is = (CamelIMAPXServer *) data;
-	GSource *pending;
+		g_object_unref (input_stream);
 
-	g_main_context_push_thread_default (is->priv->idle_main_context);
+		if (!success)
+			goto exit;
+	}
 
-	/* Schedule the first IDLE command after a brief "dwell"
-	 * delay so any other pending commands get priority.
-	 *
-	 * XXX Don't fully understand why this is necessary, but
-	 *     for now just adapting old code and hoping to avoid
-	 *     regressions.
-	 */
+	g_mutex_lock (&is->priv->stream_lock);
 
-	g_rec_mutex_lock (&is->priv->idle_lock);
+	if (!is->priv->cinfo) {
+		g_mutex_unlock (&is->priv->stream_lock);
 
-	g_warn_if_fail (is->priv->idle_pending == NULL);
-	pending = g_timeout_source_new_seconds (IMAPX_IDLE_DWELL_TIME);
-	g_source_set_name (pending, "imapx_call_idle");
-	g_source_set_callback (
-		pending, imapx_call_idle,
-		imapx_weak_ref_new (is),
-		(GDestroyNotify) imapx_weak_ref_free);
-	g_source_attach (pending, is->priv->idle_main_context);
-	is->priv->idle_pending = g_source_ref (pending);
-	g_source_unref (pending);
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_CAPABILITY, "CAPABILITY");
 
-	g_rec_mutex_unlock (&is->priv->idle_lock);
+		success = camel_imapx_server_process_command_sync (is, ic, _("Failed to get capabilities"), cancellable, error);
 
-	g_main_loop_run (is->priv->idle_main_loop);
+		camel_imapx_command_unref (ic);
 
-	g_main_context_pop_thread_default (is->priv->idle_main_context);
+		if (!success)
+			goto exit;
+	} else {
+		g_mutex_unlock (&is->priv->stream_lock);
+	}
 
-	g_object_unref (is);
+	if (method == CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT) {
 
-	return NULL;
-}
+		g_mutex_lock (&is->priv->stream_lock);
 
-static CamelIMAPXIdleStopResult
-imapx_stop_idle (CamelIMAPXServer *is,
-                 GError **error)
-{
-	CamelIMAPXIdleStopResult result = IMAPX_IDLE_STOP_NOOP;
-	time_t now;
+		if (CAMEL_IMAPX_LACK_CAPABILITY (is->priv->cinfo, STARTTLS)) {
+			g_mutex_unlock (&is->priv->stream_lock);
+			g_set_error (
+				error, CAMEL_ERROR,
+				CAMEL_ERROR_GENERIC,
+				_("Failed to connect to IMAP server %s in secure mode: %s"),
+				host, _("STARTTLS not supported"));
+			success = FALSE;
+			goto exit;
+		} else {
+			g_mutex_unlock (&is->priv->stream_lock);
+		}
 
-	time (&now);
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_STARTTLS, "STARTTLS");
 
-	g_rec_mutex_lock (&is->priv->idle_lock);
+		success = camel_imapx_server_process_command_sync (is, ic, _("Failed to issue STARTTLS"), cancellable, error);
 
-	switch (is->priv->idle_state) {
-		case IMAPX_IDLE_ISSUED:
-			is->priv->idle_state = IMAPX_IDLE_CANCEL;
-			result = IMAPX_IDLE_STOP_SUCCESS;
-			break;
+		if (success) {
+			g_mutex_lock (&is->priv->stream_lock);
 
-		case IMAPX_IDLE_CANCEL:
-			result = IMAPX_IDLE_STOP_SUCCESS;
-			break;
+			/* See if we got new capabilities
+			 * in the STARTTLS response. */
+			imapx_free_capability (is->priv->cinfo);
+			is->priv->cinfo = NULL;
+			if (ic->status->condition == IMAPX_CAPABILITY) {
+				is->priv->cinfo = ic->status->u.cinfo;
+				ic->status->u.cinfo = NULL;
+				c (is->priv->tagprefix, "got capability flags %08x\n", is->priv->cinfo ? is->priv->cinfo->capa : 0xFFFFFFFF);
+				imapx_server_stash_command_arguments (is);
+			}
 
-		case IMAPX_IDLE_WAIT_DONE:
-			result = IMAPX_IDLE_STOP_WAIT_DONE;
-			break;
+			g_mutex_unlock (&is->priv->stream_lock);
+		}
 
-		case IMAPX_IDLE_STARTED:
-			if (imapx_command_idle_stop (is, error)) {
-				result = IMAPX_IDLE_STOP_WAIT_DONE;
-				is->priv->idle_state = IMAPX_IDLE_WAIT_DONE;
-			} else {
-				result = IMAPX_IDLE_STOP_ERROR;
-				is->priv->idle_state = IMAPX_IDLE_OFF;
-				goto exit;
-			}
-			break;
+		camel_imapx_command_unref (ic);
 
-		case IMAPX_IDLE_PENDING:
-			is->priv->idle_state = IMAPX_IDLE_OFF;
-			break;
+		if (!success)
+			goto exit;
 
-		case IMAPX_IDLE_OFF:
-			break;
-	}
+		tls_stream = camel_network_service_starttls (
+			CAMEL_NETWORK_SERVICE (store), connection, error);
 
-exit:
-	g_rec_mutex_unlock (&is->priv->idle_lock);
+		if (tls_stream != NULL) {
+			GInputStream *input_stream;
+			GOutputStream *output_stream;
 
-	return result;
-}
+			g_mutex_lock (&is->priv->stream_lock);
+			g_object_unref (is->priv->connection);
+			is->priv->connection = g_object_ref (tls_stream);
+			g_mutex_unlock (&is->priv->stream_lock);
 
-static void
-imapx_start_idle (CamelIMAPXServer *is)
-{
-	if (camel_application_is_exiting)
-		return;
+			input_stream =
+				g_io_stream_get_input_stream (tls_stream);
+			output_stream =
+				g_io_stream_get_output_stream (tls_stream);
 
-	g_rec_mutex_lock (&is->priv->idle_lock);
+			imapx_server_set_streams (
+				is, input_stream, output_stream);
 
-	if (is->priv->idle_state != IMAPX_IDLE_OFF) {
-		g_warn_if_fail (is->priv->idle_state == IMAPX_IDLE_OFF);
-		g_rec_mutex_unlock (&is->priv->idle_lock);
-		return;
-	}
+			g_object_unref (tls_stream);
+		} else {
+			g_prefix_error (
+				error,
+				_("Failed to connect to IMAP server %s in secure mode: "),
+				host);
+			success = FALSE;
+			goto exit;
+		}
 
-	is->priv->idle_state = IMAPX_IDLE_PENDING;
+		/* Get new capabilities if they weren't already given */
+		g_mutex_lock (&is->priv->stream_lock);
+		if (is->priv->cinfo == NULL) {
+			g_mutex_unlock (&is->priv->stream_lock);
+			ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_CAPABILITY, "CAPABILITY");
+			success = camel_imapx_server_process_command_sync (is, ic, _("Failed to get capabilities"), cancellable, error);
+			camel_imapx_command_unref (ic);
 
-	if (is->priv->idle_thread == NULL) {
-		is->priv->idle_thread = g_thread_new (
-			NULL, imapx_idle_thread, g_object_ref (is));
-
-	} else if (is->priv->idle_pending == NULL) {
-		GSource *pending;
-
-		pending = g_idle_source_new ();
-		g_source_set_name (pending, "imapx_call_idle");
-		g_source_set_callback (
-			pending, imapx_call_idle,
-			imapx_weak_ref_new (is),
-			(GDestroyNotify) imapx_weak_ref_free);
-		g_source_attach (pending, is->priv->idle_main_context);
-		is->priv->idle_pending = g_source_ref (pending);
-		g_source_unref (pending);
+			if (!success)
+				goto exit;
+		} else {
+			g_mutex_unlock (&is->priv->stream_lock);
+		}
 	}
 
-	g_rec_mutex_unlock (&is->priv->idle_lock);
-}
+exit:
+	if (!success) {
+		g_mutex_lock (&is->priv->stream_lock);
 
-static gboolean
-imapx_in_idle (CamelIMAPXServer *is)
-{
-	gboolean in_idle = FALSE;
+		g_clear_object (&is->priv->input_stream);
+		g_clear_object (&is->priv->output_stream);
+		g_clear_object (&is->priv->connection);
+		g_clear_object (&is->priv->subprocess);
+
+		if (is->priv->cinfo != NULL) {
+			imapx_free_capability (is->priv->cinfo);
+			is->priv->cinfo = NULL;
+		}
 
-	g_rec_mutex_lock (&is->priv->idle_lock);
+		g_mutex_unlock (&is->priv->stream_lock);
+	}
 
-	if (is->priv->idle_thread != NULL)
-		in_idle = (is->priv->idle_state > IMAPX_IDLE_OFF);
+	g_free (host);
 
-	g_rec_mutex_unlock (&is->priv->idle_lock);
+	g_clear_object (&connection);
+	g_clear_object (&store);
 
-	return in_idle;
+	return success;
 }
 
-static gboolean
-imapx_use_idle (CamelIMAPXServer *is)
+gboolean
+camel_imapx_server_is_connected (CamelIMAPXServer *imapx_server)
 {
-	gboolean use_idle = FALSE;
-
-	/* No need for IDLE if the server supports NOTIFY. */
-	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, NOTIFY))
-		return FALSE;
-
-	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, IDLE)) {
-		CamelIMAPXSettings *settings;
-
-		settings = camel_imapx_server_ref_settings (is);
-		use_idle = camel_imapx_settings_get_use_idle (settings);
-		g_object_unref (settings);
-	}
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), FALSE);
 
-	return use_idle;
+	return imapx_server->priv->state >= IMAPX_CONNECTED;
 }
 
-// end IDLE
-/* ********************************************************************** */
-static void
-imapx_command_select_done (CamelIMAPXServer *is,
-                           CamelIMAPXCommand *ic)
+CamelAuthenticationResult
+camel_imapx_server_authenticate_sync (CamelIMAPXServer *is,
+				      const gchar *mechanism,
+				      GCancellable *cancellable,
+				      GError **error)
 {
-	CamelIMAPXJob *job;
-	CamelIMAPXMailbox *select_closing;
-	CamelIMAPXMailbox *select_pending;
-	GError *local_error = NULL;
-
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
-
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		CamelIMAPXCommandQueue *failed;
-		GQueue trash = G_QUEUE_INIT;
-		GList *list, *link;
-		gboolean noperm_error;
-
-		c (is->tagprefix, "Select failed: %s\n", local_error ? local_error->message : "Unknown error");
+	CamelNetworkSettings *network_settings;
+	CamelIMAPXStore *store;
+	CamelService *service;
+	CamelSettings *settings;
+	CamelAuthenticationResult result;
+	CamelIMAPXCommand *ic;
+	CamelSasl *sasl = NULL;
+	gchar *host;
+	gchar *user;
 
-		g_mutex_lock (&is->priv->select_lock);
-		select_closing = g_weak_ref_get (&is->priv->select_closing);
-		select_pending = g_weak_ref_get (&is->priv->select_pending);
-		g_weak_ref_set (&is->priv->select_mailbox, NULL);
-		g_weak_ref_set (&is->priv->select_closing, NULL);
-		g_weak_ref_set (&is->priv->select_pending, NULL);
-		is->state = IMAPX_INITIALISED;
-		g_mutex_unlock (&is->priv->select_lock);
+	g_return_val_if_fail (
+		CAMEL_IS_IMAPX_SERVER (is),
+		CAMEL_AUTHENTICATION_ERROR);
 
-		failed = camel_imapx_command_queue_new ();
+	store = camel_imapx_server_ref_store (is);
 
-		QUEUE_LOCK (is);
+	service = CAMEL_SERVICE (store);
+	settings = camel_service_ref_settings (service);
 
-		noperm_error = select_pending != NULL && ic->status && ic->status->result == IMAPX_NO &&
-			(ic->status->condition == IMAPX_NOPERM || ic->status->condition == IMAPX_UNKNOWN);
+	network_settings = CAMEL_NETWORK_SETTINGS (settings);
+	host = camel_network_settings_dup_host (network_settings);
+	user = camel_network_settings_dup_user (network_settings);
 
-		if (select_pending != NULL) {
-			GList *head = camel_imapx_command_queue_peek_head_link (is->queue);
-
-			for (link = head; link != NULL; link = g_list_next (link)) {
-				CamelIMAPXCommand *cw = link->data;
-				CamelIMAPXMailbox *cw_mailbox;
-
-				cw_mailbox = camel_imapx_command_ref_mailbox (cw);
-
-				if (cw_mailbox == select_pending) {
-					c (
-						is->tagprefix,
-						"Cancelling command '%s'(%p) "
-						"for mailbox '%s'\n",
-						cw->name, cw,
-						camel_imapx_mailbox_get_name (cw_mailbox));
-					g_queue_push_tail (&trash, link);
-				}
+	g_object_unref (settings);
 
-				g_clear_object (&cw_mailbox);
-			}
-		}
+	if (mechanism != NULL) {
+		g_mutex_lock (&is->priv->stream_lock);
 
-		if (noperm_error) {
-			/* This avoids another SELECT try on this mailbox;
-			   the mailbox can be write-only in this case. */
-			if (camel_imapx_mailbox_get_permanentflags (select_pending) == ~0)
-				camel_imapx_mailbox_set_permanentflags (select_pending, 0);
+		if (is->priv->cinfo && !g_hash_table_lookup (is->priv->cinfo->auth_types, mechanism) && (
+		    !g_str_equal (mechanism, "Google") || !g_hash_table_lookup (is->priv->cinfo->auth_types, "XOAUTH2"))) {
+			g_mutex_unlock (&is->priv->stream_lock);
+			g_set_error (
+				error, CAMEL_SERVICE_ERROR,
+				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+				_("IMAP server %s does not support %s "
+				"authentication"), host, mechanism);
+			result = CAMEL_AUTHENTICATION_ERROR;
+			goto exit;
+		} else {
+			g_mutex_unlock (&is->priv->stream_lock);
 		}
 
-		while ((link = g_queue_pop_head (&trash)) != NULL) {
-			CamelIMAPXCommand *cw = link->data;
-			camel_imapx_command_ref (cw);
-			camel_imapx_command_queue_delete_link (is->queue, link);
-			imapx_server_command_removed (is, cw);
-			camel_imapx_command_queue_push_tail (failed, cw);
-			camel_imapx_command_unref (cw);
+		sasl = camel_sasl_new ("imap", mechanism, service);
+		if (sasl == NULL) {
+			g_set_error (
+				error, CAMEL_SERVICE_ERROR,
+				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+				_("No support for %s authentication"),
+				mechanism);
+			result = CAMEL_AUTHENTICATION_ERROR;
+			goto exit;
 		}
+	}
 
-		QUEUE_UNLOCK (is);
-
-		list = camel_imapx_command_queue_peek_head_link (failed);
-
-		for (link = list; link != NULL; link = g_list_next (link)) {
-			CamelIMAPXCommand *cw = link->data;
-			CamelIMAPXJob *failed_job;
-
-			failed_job = camel_imapx_command_get_job (cw);
-
-			if (!CAMEL_IS_IMAPX_JOB (failed_job)) {
-				g_warn_if_reached ();
-				continue;
-			}
+	if (sasl != NULL) {
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_AUTHENTICATE, "AUTHENTICATE %A", sasl);
+	} else {
+		const gchar *password;
 
-			if (!noperm_error)
-				camel_imapx_job_cancel (failed_job);
+		password = camel_service_get_password (service);
 
-			if (ic->status)
-				cw->status = imapx_copy_status (ic->status);
+		if (user == NULL) {
+			g_set_error_literal (
+				error, CAMEL_SERVICE_ERROR,
+				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+				_("Cannot authenticate without a username"));
+			result = CAMEL_AUTHENTICATION_ERROR;
+			goto exit;
+		}
 
-			cw->complete (is, cw);
+		if (password == NULL) {
+			g_set_error_literal (
+				error, CAMEL_SERVICE_ERROR,
+				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+				_("Authentication password not available"));
+			result = CAMEL_AUTHENTICATION_ERROR;
+			goto exit;
 		}
 
-		camel_imapx_command_queue_free (failed);
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LOGIN, "LOGIN %s %s", user, password);
+	}
 
-		camel_imapx_job_take_error (job, local_error);
-		imapx_unregister_job (is, job);
+	if (!camel_imapx_server_process_command_sync (is, ic, _("Failed to authenticate"), cancellable, error) && (
+	    !ic->status || ic->status->result != IMAPX_NO))
+		result = CAMEL_AUTHENTICATION_ERROR;
+	else if (ic->status->result == IMAPX_OK)
+		result = CAMEL_AUTHENTICATION_ACCEPTED;
+	else if (ic->status->result == IMAPX_NO) {
+		g_clear_error (error);
 
-	} else {
-		CamelFolder *folder;
-		CamelIMAPXSummary *imapx_summary;
-		guint32 uidnext;
-
-		c (is->tagprefix, "Select ok!\n");
-
-		g_mutex_lock (&is->priv->select_lock);
-		select_closing = g_weak_ref_get (&is->priv->select_closing);
-		select_pending = g_weak_ref_get (&is->priv->select_pending);
-		g_weak_ref_set (&is->priv->select_mailbox, select_pending);
-		g_weak_ref_set (&is->priv->select_closing, NULL);
-		g_weak_ref_set (&is->priv->select_pending, NULL);
-		is->state = IMAPX_SELECTED;
-		g_mutex_unlock (&is->priv->select_lock);
+		if (camel_imapx_store_is_connecting_concurrent_connection (store)) {
+			/* At least one connection succeeded, probably max connection limit
+			   set on the server had been reached, thus use special error code
+			   for it, to instruct the connection manager to decrease the limit
+			   and use already created connection. */
+			g_set_error_literal (
+				error, CAMEL_IMAPX_SERVER_ERROR,
+				CAMEL_IMAPX_SERVER_ERROR_CONCURRENT_CONNECT_FAILED,
+				ic->status->text ? ic->status->text : _("Unknown error"));
+			result = CAMEL_AUTHENTICATION_ERROR;
+		} else if (sasl) {
+			CamelSaslClass *sasl_class;
 
-		/* We should have a strong reference
-		 * on the newly-selected CamelFolder. */
-		folder = imapx_server_ref_folder (is, select_pending);
-		g_return_if_fail (folder != NULL);
-
-		uidnext = camel_imapx_mailbox_get_uidnext (select_pending);
-		imapx_summary = CAMEL_IMAPX_SUMMARY (folder->summary);
-
-		if (imapx_summary->uidnext < uidnext) {
-			/* We don't want to fetch new messages if the command we selected this
-			 * folder for is *already* fetching all messages (i.e. scan_changes).
-			 * Bug #667725. */
-			CamelIMAPXJob *job = imapx_server_ref_job (
-				is, select_pending,
-				IMAPX_JOB_REFRESH_INFO, NULL);
-			if (job) {
-				camel_imapx_job_unref (job);
-				c (
-					is->tagprefix,
-					"Will not fetch_new_messages when already refreshing information\n");
+			sasl_class = CAMEL_SASL_GET_CLASS (sasl);
+			if (sasl_class && sasl_class->auth_type && !sasl_class->auth_type->need_password) {
+				g_set_error_literal (
+					error, CAMEL_SERVICE_ERROR,
+					CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+					ic->status->text ? ic->status->text : _("Unknown error"));
+				result = CAMEL_AUTHENTICATION_ERROR;
 			} else {
-				imapx_server_fetch_new_messages (is, select_pending, TRUE, TRUE, NULL, NULL);
+				result = CAMEL_AUTHENTICATION_REJECTED;
 			}
+		} else {
+			result = CAMEL_AUTHENTICATION_REJECTED;
 		}
+	} else {
+		g_set_error_literal (
+			error, CAMEL_SERVICE_ERROR,
+			CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+			ic->status->text ? ic->status->text : _("Unknown error"));
+		result = CAMEL_AUTHENTICATION_ERROR;
+	}
 
-#if 0  /* see comment for disabled bits in imapx_job_refresh_info_start() */
-		/* This should trigger a new messages scan */
-		if (is->exists != folder->summary->root_view->total_count)
-			g_warning (
-				"exists is %d our summary is %d and summary exists is %d\n", is->exists,
-				folder->summary->root_view->total_count,
-				((CamelIMAPXSummary *) folder->summary)->exists);
-#endif
+	/* Forget old capabilities after login. */
+	if (result == CAMEL_AUTHENTICATION_ACCEPTED) {
+		g_mutex_lock (&is->priv->stream_lock);
 
-		g_clear_object (&folder);
+		if (is->priv->cinfo) {
+			imapx_free_capability (is->priv->cinfo);
+			is->priv->cinfo = NULL;
+		}
+
+		if (ic->status->condition == IMAPX_CAPABILITY) {
+			is->priv->cinfo = ic->status->u.cinfo;
+			ic->status->u.cinfo = NULL;
+			c (is->priv->tagprefix, "got capability flags %08x\n", is->priv->cinfo ? is->priv->cinfo->capa : 0xFFFFFFFF);
+			imapx_server_stash_command_arguments (is);
+		}
+
+		g_mutex_unlock (&is->priv->stream_lock);
 	}
 
-	if (select_closing != NULL)
-		g_signal_emit (is, signals[MAILBOX_CLOSED], 0, select_closing);
+	camel_imapx_command_unref (ic);
+
+	if (sasl != NULL)
+		g_object_unref (sasl);
+
+exit:
+	g_free (host);
+	g_free (user);
+
+	g_object_unref (store);
 
-	g_clear_object (&select_closing);
-	g_clear_object (&select_pending);
+	return result;
 }
 
-/* Should have a queue lock. TODO Change the way select is written */
-static void
-imapx_maybe_select (CamelIMAPXServer *is,
-                    CamelIMAPXJob *job,
-                    CamelIMAPXMailbox *mailbox)
+static gboolean
+imapx_reconnect (CamelIMAPXServer *is,
+                 GCancellable *cancellable,
+                 GError **error)
 {
 	CamelIMAPXCommand *ic;
-	CamelIMAPXMailbox *select_mailbox;
-	CamelIMAPXMailbox *select_pending;
-	gboolean nothing_to_do = FALSE;
-
-	/* Select is complicated by the fact we may have commands
-	 * active on the server for a different selection.
-	 *
-	 * So this waits for any commands to complete, selects the
-	 * new mailbox, and halts the queuing of any new commands.
-	 * It is assumed whomever called us is about to issue a
-	 * high-priority command anyway. */
+	CamelService *service;
+	CamelSession *session;
+	CamelIMAPXStore *store;
+	CamelSettings *settings;
+	gchar *mechanism;
+	gboolean use_qresync;
+	gboolean use_idle;
+	gboolean success = FALSE;
 
-	g_mutex_lock (&is->priv->select_lock);
+	store = camel_imapx_server_ref_store (is);
 
-	select_mailbox = g_weak_ref_get (&is->priv->select_mailbox);
-	select_pending = g_weak_ref_get (&is->priv->select_pending);
+	service = CAMEL_SERVICE (store);
+	session = camel_service_ref_session (service);
+	if (!session) {
+		g_set_error_literal (
+			error, CAMEL_SERVICE_ERROR,
+			CAMEL_SERVICE_ERROR_UNAVAILABLE,
+			_("You must be working online to complete this operation"));
+		g_object_unref (store);
+		return FALSE;
+	}
 
-	if (select_pending != NULL) {
-		nothing_to_do = TRUE;
-	} else if (select_mailbox == mailbox) {
-		nothing_to_do = TRUE;
-	} else if (!camel_imapx_command_queue_is_empty (is->active)) {
-		nothing_to_do = TRUE;
-	} else {
-		g_weak_ref_set (&is->priv->select_pending, mailbox);
+	settings = camel_service_ref_settings (service);
 
-		if (select_mailbox != NULL) {
-			g_weak_ref_set (&is->priv->select_mailbox, NULL);
-		} else {
-			/* If no mailbox was selected, we won't get a
-			 * [CLOSED] status so just point select_mailbox
-			 * at the newly-selected mailbox immediately. */
-			g_weak_ref_set (&is->priv->select_mailbox, mailbox);
-		}
+	mechanism = camel_network_settings_dup_auth_mechanism (
+		CAMEL_NETWORK_SETTINGS (settings));
 
-		g_weak_ref_set (&is->priv->select_closing, select_mailbox);
+	use_qresync = camel_imapx_settings_get_use_qresync (CAMEL_IMAPX_SETTINGS (settings));
+	use_idle = camel_imapx_settings_get_use_idle (CAMEL_IMAPX_SETTINGS (settings));
 
-		/* Hrm, what about reconnecting? */
-		is->state = IMAPX_INITIALISED;
-	}
+	g_object_unref (settings);
 
-	g_clear_object (&select_mailbox);
-	g_clear_object (&select_pending);
+	if (!imapx_connect_to_server (is, cancellable, error))
+		goto exception;
 
-	g_mutex_unlock (&is->priv->select_lock);
+	if (is->priv->state == IMAPX_AUTHENTICATED)
+		goto preauthed;
 
-	if (nothing_to_do)
-		return;
+	if (!camel_session_authenticate_sync (
+		session, service, mechanism, cancellable, error))
+		goto exception;
 
-	g_signal_emit (is, signals[MAILBOX_SELECT], 0, mailbox);
+	/* After login we re-capa unless the server already told us. */
+	g_mutex_lock (&is->priv->stream_lock);
+	if (is->priv->cinfo == NULL) {
+		GError *local_error = NULL;
 
-	ic = camel_imapx_command_new (
-		is, "SELECT", NULL, "SELECT %M", mailbox);
+		g_mutex_unlock (&is->priv->stream_lock);
 
-	if (is->use_qresync) {
-		CamelFolder *folder;
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_CAPABILITY, "CAPABILITY");
+		camel_imapx_server_process_command_sync (is, ic, _("Failed to get capabilities"), cancellable, &local_error);
+		camel_imapx_command_unref (ic);
 
-		folder = imapx_server_ref_folder (is, mailbox);
-		camel_imapx_command_add_qresync_parameter (ic, folder);
-		g_clear_object (&folder);
+		if (local_error != NULL) {
+			g_propagate_error (error, local_error);
+			goto exception;
+		}
+	} else {
+		g_mutex_unlock (&is->priv->stream_lock);
 	}
 
-	ic->complete = imapx_command_select_done;
-	camel_imapx_command_set_job (ic, job);
-
-	imapx_command_start (is, ic);
+	is->priv->state = IMAPX_AUTHENTICATED;
 
-	camel_imapx_command_unref (ic);
-}
+preauthed:
+	/* Fetch namespaces (if supported). */
+	g_mutex_lock (&is->priv->stream_lock);
+	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, NAMESPACE)) {
+		GError *local_error = NULL;
 
-static void
-imapx_server_set_streams (CamelIMAPXServer *is,
-                          GInputStream *input_stream,
-                          GOutputStream *output_stream)
-{
-	GConverter *logger;
+		g_mutex_unlock (&is->priv->stream_lock);
 
-	if (input_stream != NULL) {
-		GInputStream *temp_stream;
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NAMESPACE, "NAMESPACE");
+		camel_imapx_server_process_command_sync (is, ic, _("Failed to issue NAMESPACE"), cancellable, &local_error);
+		camel_imapx_command_unref (ic);
 
-		/* The logger produces debugging output. */
-		logger = camel_imapx_logger_new (is->tagprefix);
-		input_stream = g_converter_input_stream_new (
-			input_stream, logger);
-		g_clear_object (&logger);
+		if (local_error != NULL) {
+			g_propagate_error (error, local_error);
+			goto exception;
+		}
 
-		/* Buffer the input stream for parsing. */
-		temp_stream = camel_imapx_input_stream_new (input_stream);
-		g_object_bind_property (
-			temp_stream, "close-base-stream",
-			input_stream, "close-base-stream",
-			G_BINDING_SYNC_CREATE);
-		g_object_unref (input_stream);
-		input_stream = temp_stream;
+		g_mutex_lock (&is->priv->stream_lock);
 	}
 
-	if (output_stream != NULL) {
-		/* The logger produces debugging output. */
-		logger = camel_imapx_logger_new (is->tagprefix);
-		output_stream = g_converter_output_stream_new (
-			output_stream, logger);
-		g_clear_object (&logger);
-	}
+	/* Enable quick mailbox resynchronization (if supported). */
+	if (use_qresync && CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, QRESYNC)) {
+		GError *local_error = NULL;
 
-	g_mutex_lock (&is->priv->stream_lock);
+		g_mutex_unlock (&is->priv->stream_lock);
 
-	/* Don't close the base streams so STARTTLS works correctly. */
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_ENABLE, "ENABLE CONDSTORE QRESYNC");
+		camel_imapx_server_process_command_sync (is, ic, _("Failed to enable QResync"), cancellable, &local_error);
+		camel_imapx_command_unref (ic);
 
-	if (G_IS_FILTER_INPUT_STREAM (is->priv->input_stream)) {
-		g_filter_input_stream_set_close_base_stream (
-			G_FILTER_INPUT_STREAM (is->priv->input_stream),
-			FALSE);
-	}
+		if (local_error != NULL) {
+			g_propagate_error (error, local_error);
+			goto exception;
+		}
 
-	if (G_IS_FILTER_OUTPUT_STREAM (is->priv->output_stream)) {
-		g_filter_output_stream_set_close_base_stream (
-			G_FILTER_OUTPUT_STREAM (is->priv->output_stream),
-			FALSE);
-	}
+		g_mutex_lock (&is->priv->stream_lock);
 
-	g_clear_object (&is->priv->input_stream);
-	is->priv->input_stream = input_stream;
+		is->priv->use_qresync = TRUE;
+	} else {
+		is->priv->use_qresync = FALSE;
+	}
 
-	g_clear_object (&is->priv->output_stream);
-	is->priv->output_stream = output_stream;
+	/* Set NOTIFY options after enabling QRESYNC (if supported). */
+	if (use_idle && CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, NOTIFY)) {
+		GError *local_error = NULL;
 
-	g_mutex_unlock (&is->priv->stream_lock);
-}
+		g_mutex_unlock (&is->priv->stream_lock);
 
-#if GLIB_CHECK_VERSION(2,39,0)
-#ifdef G_OS_UNIX
-static void
-imapx_server_child_process_setup (gpointer user_data)
-{
-#ifdef TIOCNOTTY
-	gint fd;
-#endif /* TIOCNOTTY */
+		/* XXX The list of FETCH attributes is negotiable. */
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NOTIFY, "NOTIFY SET "
+			"(selected "
+			"(MessageNew (UID RFC822.SIZE RFC822.HEADER FLAGS)"
+			" MessageExpunge"
+			" FlagChange)) "
+			"(personal "
+			"(MessageNew"
+			" MessageExpunge"
+			" MailboxName"
+			" SubscriptionChange))");
+		camel_imapx_server_process_command_sync (is, ic, _("Failed to issue NOTIFY"), cancellable, &local_error);
+		camel_imapx_command_unref (ic);
 
-	setsid ();
+		if (local_error != NULL) {
+			g_propagate_error (error, local_error);
+			goto exception;
+		}
 
-#ifdef TIOCNOTTY
-	/* Detach from the controlling tty if we have one.  Otherwise,
-	 * SSH might do something stupid like trying to use it instead
-	 * of running $SSH_ASKPASS. */
-	if ((fd = open ("/dev/tty", O_RDONLY)) != -1) {
-		ioctl (fd, TIOCNOTTY, NULL);
-		close (fd);
+		g_mutex_lock (&is->priv->stream_lock);
 	}
-#endif /* TIOCNOTTY */
-}
-#endif /* G_OS_UNIX */
-#endif
 
-static gboolean
-connect_to_server_process (CamelIMAPXServer *is,
-                           const gchar *cmd,
-                           GError **error)
-{
-#if GLIB_CHECK_VERSION(2,39,0)
-	GSubprocessLauncher *launcher;
-	GSubprocess *subprocess = NULL;
-	CamelNetworkSettings *network_settings;
-	CamelProvider *provider;
-	CamelSettings *settings;
-	CamelIMAPXStore *store;
-	CamelURL url;
-	gchar **argv = NULL;
-	gchar *buf;
-	gchar *cmd_copy;
-	gchar *full_cmd;
-	const gchar *password;
-	gchar *host;
-	gchar *user;
-	guint16 port;
+	g_mutex_unlock (&is->priv->stream_lock);
 
-	memset (&url, 0, sizeof (CamelURL));
+	is->priv->state = IMAPX_INITIALISED;
 
-	launcher = g_subprocess_launcher_new (
-		G_SUBPROCESS_FLAGS_STDIN_PIPE |
-		G_SUBPROCESS_FLAGS_STDOUT_PIPE |
-		G_SUBPROCESS_FLAGS_STDERR_SILENCE);
+	success = TRUE;
 
-#ifdef G_OS_UNIX
-	g_subprocess_launcher_set_child_setup (
-		launcher, imapx_server_child_process_setup,
-		NULL, (GDestroyNotify) NULL);
-#endif
+	goto exit;
 
-	store = camel_imapx_server_ref_store (is);
+exception:
+	imapx_disconnect (is);
 
-	password = camel_service_get_password (CAMEL_SERVICE (store));
-	provider = camel_service_get_provider (CAMEL_SERVICE (store));
-	settings = camel_service_ref_settings (CAMEL_SERVICE (store));
+exit:
+	g_free (mechanism);
 
-	network_settings = CAMEL_NETWORK_SETTINGS (settings);
-	host = camel_network_settings_dup_host (network_settings);
-	port = camel_network_settings_get_port (network_settings);
-	user = camel_network_settings_dup_user (network_settings);
+	g_object_unref (session);
+	g_object_unref (store);
 
-	/* Put full details in the environment, in case the connection
-	 * program needs them */
-	camel_url_set_protocol (&url, provider->protocol);
-	camel_url_set_host (&url, host);
-	camel_url_set_port (&url, port);
-	camel_url_set_user (&url, user);
-	buf = camel_url_to_string (&url, 0);
-
-	g_subprocess_launcher_setenv (launcher, "URL", buf, TRUE);
-	g_subprocess_launcher_setenv (launcher, "URLHOST", host, TRUE);
-
-	if (port > 0) {
-		gchar *port_string;
-
-		port_string = g_strdup_printf ("%u", port);
-		g_subprocess_launcher_setenv (
-			launcher, "URLPORT", port_string, TRUE);
-		g_free (port_string);
-	}
-
-	if (user != NULL) {
-		g_subprocess_launcher_setenv (
-			launcher, "URLPORT", user, TRUE);
-	}
-
-	if (password != NULL) {
-		g_subprocess_launcher_setenv (
-			launcher, "URLPASSWD", password, TRUE);
-	}
-
-	g_free (buf);
-
-	g_object_unref (settings);
-	g_object_unref (store);
-
-	/* Now do %h, %u, etc. substitution in cmd */
-	buf = cmd_copy = g_strdup (cmd);
-
-	full_cmd = g_strdup ("");
-
-	for (;;) {
-		gchar *pc;
-		gchar *tmp;
-		const gchar *var;
-		gint len;
-
-		pc = strchr (buf, '%');
-	ignore:
-		if (!pc) {
-			tmp = g_strdup_printf ("%s%s", full_cmd, buf);
-			g_free (full_cmd);
-			full_cmd = tmp;
-			break;
-		}
-
-		len = pc - buf;
-
-		var = NULL;
-
-		switch (pc[1]) {
-		case 'h':
-			var = host;
-			break;
-		case 'u':
-			var = user;
-			break;
-		}
-		if (!var) {
-			/* If there wasn't a valid %-code, with an actual
-			 * variable to insert, pretend we didn't see the % */
-			pc = strchr (pc + 1, '%');
-			goto ignore;
-		}
-		tmp = g_strdup_printf ("%s%.*s%s", full_cmd, len, buf, var);
-		g_free (full_cmd);
-		full_cmd = tmp;
-		buf = pc + 2;
-	}
-
-	g_free (cmd_copy);
-
-	g_free (host);
-	g_free (user);
-
-	if (g_shell_parse_argv (full_cmd, NULL, &argv, error)) {
-		subprocess = g_subprocess_launcher_spawnv (
-			launcher, (const gchar * const *) argv, error);
-		g_strfreev (argv);
-	}
-
-	g_free (full_cmd);
-	g_object_unref (launcher);
-
-	if (subprocess != NULL) {
-		GInputStream *input_stream;
-		GOutputStream *output_stream;
-
-		g_mutex_lock (&is->priv->stream_lock);
-		g_warn_if_fail (is->priv->subprocess == NULL);
-		is->priv->subprocess = g_object_ref (subprocess);
-		g_mutex_unlock (&is->priv->stream_lock);
-
-		input_stream = g_subprocess_get_stdout_pipe (subprocess);
-		output_stream = g_subprocess_get_stdin_pipe (subprocess);
-
-		imapx_server_set_streams (is, input_stream, output_stream);
-
-		g_object_unref (subprocess);
-	}
-
-	return TRUE;
-
-#else /* GLIB_CHECK_VERSION(2,39,0) */
-
-	g_set_error_literal (
-		error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
-		"GLib 2.39 or later is required to connect "
-		"to an IMAP server through a shell command");
-
-	return FALSE;
-#endif
-}
-
-gboolean
-imapx_connect_to_server (CamelIMAPXServer *is,
-                         GCancellable *cancellable,
-                         GError **error)
-{
-	CamelNetworkSettings *network_settings;
-	CamelNetworkSecurityMethod method;
-	CamelIMAPXStore *store;
-	CamelSettings *settings;
-	GIOStream *connection = NULL;
-	GIOStream *tls_stream;
-	GSocket *socket;
-	guint len;
-	guchar *token;
-	gint tok;
-	CamelIMAPXCommand *ic;
-	gchar *shell_command = NULL;
-	gboolean use_shell_command;
-	gboolean success = TRUE;
-	gchar *host;
-	GError *local_error = NULL;
-
-	store = camel_imapx_server_ref_store (is);
-
-	settings = camel_service_ref_settings (CAMEL_SERVICE (store));
-
-	network_settings = CAMEL_NETWORK_SETTINGS (settings);
-	host = camel_network_settings_dup_host (network_settings);
-	method = camel_network_settings_get_security_method (network_settings);
-
-	use_shell_command = camel_imapx_settings_get_use_shell_command (
-		CAMEL_IMAPX_SETTINGS (settings));
-
-	if (use_shell_command)
-		shell_command = camel_imapx_settings_dup_shell_command (
-			CAMEL_IMAPX_SETTINGS (settings));
-
-	g_object_unref (settings);
-
-	if (shell_command != NULL) {
-		success = connect_to_server_process (is, shell_command, error);
-
-		g_free (shell_command);
-
-		if (success)
-			goto connected;
-		else
-			goto exit;
-	}
-
-	connection = camel_network_service_connect_sync (
-		CAMEL_NETWORK_SERVICE (store), cancellable, error);
-
-	if (connection != NULL) {
-		GInputStream *input_stream;
-		GOutputStream *output_stream;
-
-		/* Disable the Nagle algorithm with TCP_NODELAY, since IMAP
-		 * commands should be issued immediately even we've not yet
-		 * received a response to a previous command. */
-		socket = g_socket_connection_get_socket (
-			G_SOCKET_CONNECTION (connection));
-		g_socket_set_option (
-			socket, IPPROTO_TCP, TCP_NODELAY, 1, &local_error);
-		if (local_error != NULL) {
-			/* Failure to set the socket option is non-fatal. */
-			g_warning ("%s: %s", G_STRFUNC, local_error->message);
-			g_clear_error (&local_error);
-		}
-
-		g_mutex_lock (&is->priv->stream_lock);
-		g_warn_if_fail (is->priv->connection == NULL);
-		is->priv->connection = g_object_ref (connection);
-		g_mutex_unlock (&is->priv->stream_lock);
-
-		input_stream = g_io_stream_get_input_stream (connection);
-		output_stream = g_io_stream_get_output_stream (connection);
-
-		imapx_server_set_streams (is, input_stream, output_stream);
-
-		/* Hang on to the connection reference in case we need to
-		 * issue STARTTLS below. */
-	} else {
-		success = FALSE;
-		goto exit;
-	}
-
-connected:
-	while (1) {
-		GInputStream *input_stream;
-
-		input_stream = camel_imapx_server_ref_input_stream (is);
-
-		token = NULL;
-		tok = camel_imapx_input_stream_token (
-			CAMEL_IMAPX_INPUT_STREAM (input_stream),
-			&token, &len, cancellable, error);
-
-		if (tok < 0) {
-			success = FALSE;
-
-		} else if (tok == '*') {
-			success = imapx_untagged (
-				is, input_stream, cancellable, error);
-
-			if (success) {
-				g_object_unref (input_stream);
-				break;
-			}
-
-		} else {
-			camel_imapx_input_stream_ungettoken (
-				CAMEL_IMAPX_INPUT_STREAM (input_stream),
-				tok, token, len);
-
-			success = camel_imapx_input_stream_text (
-				CAMEL_IMAPX_INPUT_STREAM (input_stream),
-				&token, cancellable, error);
-
-			g_free (token);
-		}
-
-		g_object_unref (input_stream);
-
-		if (!success)
-			goto exit;
-	}
-
-	if (!is->cinfo) {
-		ic = camel_imapx_command_new (
-			is, "CAPABILITY", NULL, "CAPABILITY");
-
-		success = imapx_command_run (is, ic, cancellable, error);
-
-		/* Server reported error. */
-		if (success && ic->status->result != IMAPX_OK) {
-			g_set_error (
-				error, CAMEL_ERROR,
-				CAMEL_ERROR_GENERIC,
-				"%s", ic->status->text);
-			success = FALSE;
-		}
-
-		camel_imapx_command_unref (ic);
-
-		if (!success)
-			goto exit;
-	}
-
-	if (method == CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT) {
-
-		if (CAMEL_IMAPX_LACK_CAPABILITY (is->cinfo, STARTTLS)) {
-			g_set_error (
-				&local_error, CAMEL_ERROR,
-				CAMEL_ERROR_GENERIC,
-				_("Failed to connect to IMAP server %s in secure mode: %s"),
-				host, _("STARTTLS not supported"));
-			goto exit;
-		}
-
-		ic = camel_imapx_command_new (
-			is, "STARTTLS", NULL, "STARTTLS");
-
-		success = imapx_command_run (is, ic, cancellable, error);
-
-		/* Server reported error. */
-		if (success && ic->status->result != IMAPX_OK) {
-			g_set_error (
-				error, CAMEL_ERROR,
-				CAMEL_ERROR_GENERIC,
-				"%s", ic->status->text);
-			success = FALSE;
-		}
-
-		if (success) {
-			/* See if we got new capabilities
-			 * in the STARTTLS response. */
-			imapx_free_capability (is->cinfo);
-			is->cinfo = NULL;
-			if (ic->status->condition == IMAPX_CAPABILITY) {
-				is->cinfo = ic->status->u.cinfo;
-				ic->status->u.cinfo = NULL;
-				c (is->tagprefix, "got capability flags %08x\n", is->cinfo ? is->cinfo->capa : 0xFFFFFFFF);
-				imapx_server_stash_command_arguments (is);
-			}
-		}
-
-		camel_imapx_command_unref (ic);
-
-		if (!success)
-			goto exit;
-
-		tls_stream = camel_network_service_starttls (
-			CAMEL_NETWORK_SERVICE (store), connection, error);
-
-		if (tls_stream != NULL) {
-			GInputStream *input_stream;
-			GOutputStream *output_stream;
-
-			g_mutex_lock (&is->priv->stream_lock);
-			g_object_unref (is->priv->connection);
-			is->priv->connection = g_object_ref (tls_stream);
-			g_mutex_unlock (&is->priv->stream_lock);
-
-			input_stream =
-				g_io_stream_get_input_stream (tls_stream);
-			output_stream =
-				g_io_stream_get_output_stream (tls_stream);
-
-			imapx_server_set_streams (
-				is, input_stream, output_stream);
-
-			g_object_unref (tls_stream);
-		} else {
-			g_prefix_error (
-				error,
-				_("Failed to connect to IMAP server %s in secure mode: "),
-				host);
-			success = FALSE;
-			goto exit;
-		}
-
-		/* Get new capabilities if they weren't already given */
-		if (is->cinfo == NULL) {
-			ic = camel_imapx_command_new (
-				is, "CAPABILITY", NULL, "CAPABILITY");
-			success = imapx_command_run (is, ic, cancellable, error);
-			camel_imapx_command_unref (ic);
-
-			if (!success)
-				goto exit;
-		}
-	}
-
-exit:
-	if (!success) {
-		g_mutex_lock (&is->priv->stream_lock);
-
-		g_clear_object (&is->priv->input_stream);
-		g_clear_object (&is->priv->output_stream);
-		g_clear_object (&is->priv->connection);
-#if GLIB_CHECK_VERSION(2,39,0)
-		g_clear_object (&is->priv->subprocess);
-#endif
-
-		if (is->cinfo != NULL) {
-			imapx_free_capability (is->cinfo);
-			is->cinfo = NULL;
-		}
-
-		g_mutex_unlock (&is->priv->stream_lock);
-	}
-
-	g_free (host);
-
-	g_clear_object (&connection);
-	g_clear_object (&store);
-
-	return success;
-}
-
-gboolean
-camel_imapx_server_is_connected (CamelIMAPXServer *imapx_server)
-{
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), FALSE);
-
-	return imapx_server->state >= IMAPX_CONNECTED;
-}
-
-CamelAuthenticationResult
-camel_imapx_server_authenticate (CamelIMAPXServer *is,
-                                 const gchar *mechanism,
-                                 GCancellable *cancellable,
-                                 GError **error)
-{
-	CamelNetworkSettings *network_settings;
-	CamelIMAPXStore *store;
-	CamelService *service;
-	CamelSettings *settings;
-	CamelAuthenticationResult result;
-	CamelIMAPXCommand *ic;
-	CamelSasl *sasl = NULL;
-	gchar *host;
-	gchar *user;
-
-	g_return_val_if_fail (
-		CAMEL_IS_IMAPX_SERVER (is),
-		CAMEL_AUTHENTICATION_ERROR);
-
-	store = camel_imapx_server_ref_store (is);
-
-	service = CAMEL_SERVICE (store);
-	settings = camel_service_ref_settings (service);
-
-	network_settings = CAMEL_NETWORK_SETTINGS (settings);
-	host = camel_network_settings_dup_host (network_settings);
-	user = camel_network_settings_dup_user (network_settings);
-
-	g_object_unref (settings);
-
-	if (mechanism != NULL) {
-		if (is->cinfo && !g_hash_table_lookup (is->cinfo->auth_types, mechanism)) {
-			g_set_error (
-				error, CAMEL_SERVICE_ERROR,
-				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
-				_("IMAP server %s does not support %s "
-				"authentication"), host, mechanism);
-			result = CAMEL_AUTHENTICATION_ERROR;
-			goto exit;
-		}
-
-		sasl = camel_sasl_new ("imap", mechanism, service);
-		if (sasl == NULL) {
-			g_set_error (
-				error, CAMEL_SERVICE_ERROR,
-				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
-				_("No support for %s authentication"),
-				mechanism);
-			result = CAMEL_AUTHENTICATION_ERROR;
-			goto exit;
-		}
-	}
-
-	if (sasl != NULL) {
-		ic = camel_imapx_command_new (
-			is, "AUTHENTICATE", NULL, "AUTHENTICATE %A", sasl);
-	} else {
-		const gchar *password;
-
-		password = camel_service_get_password (service);
-
-		if (user == NULL) {
-			g_set_error_literal (
-				error, CAMEL_SERVICE_ERROR,
-				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
-				_("Cannot authenticate without a username"));
-			result = CAMEL_AUTHENTICATION_ERROR;
-			goto exit;
-		}
-
-		if (password == NULL) {
-			g_set_error_literal (
-				error, CAMEL_SERVICE_ERROR,
-				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
-				_("Authentication password not available"));
-			result = CAMEL_AUTHENTICATION_ERROR;
-			goto exit;
-		}
-
-		ic = camel_imapx_command_new (
-			is, "LOGIN", NULL, "LOGIN %s %s", user, password);
-	}
-
-	if (!imapx_command_run (is, ic, cancellable, error))
-		result = CAMEL_AUTHENTICATION_ERROR;
-	else if (ic->status->result == IMAPX_OK)
-		result = CAMEL_AUTHENTICATION_ACCEPTED;
-	else if (ic->status->result == IMAPX_NO) {
-		if (camel_imapx_store_is_connecting_concurrent_connection (store)) {
-			/* At least one connection succeeded, probably max connection limit
-			   set on the server had been reached, thus use special error code
-			   for it, to instruct the connection manager to decrease the limit
-			   and use already created connection. */
-			g_set_error_literal (
-				error, CAMEL_IMAPX_SERVER_ERROR,
-				CAMEL_IMAPX_SERVER_ERROR_CONCURRENT_CONNECT_FAILED,
-				ic->status->text ? ic->status->text : _("Unknown error"));
-			result = CAMEL_AUTHENTICATION_ERROR;
-		} else {
-			result = CAMEL_AUTHENTICATION_REJECTED;
-		}
-	} else {
-		g_set_error_literal (
-			error, CAMEL_SERVICE_ERROR,
-			CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
-			ic->status->text ? ic->status->text : _("Unknown error"));
-		result = CAMEL_AUTHENTICATION_ERROR;
-	}
-
-	/* Forget old capabilities after login. */
-	if (result == CAMEL_AUTHENTICATION_ACCEPTED) {
-		if (is->cinfo) {
-			imapx_free_capability (is->cinfo);
-			is->cinfo = NULL;
-		}
-
-		if (ic->status->condition == IMAPX_CAPABILITY) {
-			is->cinfo = ic->status->u.cinfo;
-			ic->status->u.cinfo = NULL;
-			c (is->tagprefix, "got capability flags %08x\n", is->cinfo ? is->cinfo->capa : 0xFFFFFFFF);
-			imapx_server_stash_command_arguments (is);
-		}
-	}
-
-	camel_imapx_command_unref (ic);
-
-	if (sasl != NULL)
-		g_object_unref (sasl);
-
-exit:
-	g_free (host);
-	g_free (user);
-
-	g_object_unref (store);
-
-	return result;
-}
-
-static gboolean
-imapx_reconnect (CamelIMAPXServer *is,
-                 GCancellable *cancellable,
-                 GError **error)
-{
-	CamelIMAPXCommand *ic;
-	CamelService *service;
-	CamelSession *session;
-	CamelIMAPXStore *store;
-	CamelSettings *settings;
-	gchar *mechanism;
-	gboolean use_qresync;
-	gboolean success = FALSE;
-
-	store = camel_imapx_server_ref_store (is);
-
-	service = CAMEL_SERVICE (store);
-	session = camel_service_ref_session (service);
-
-	settings = camel_service_ref_settings (service);
-
-	mechanism = camel_network_settings_dup_auth_mechanism (
-		CAMEL_NETWORK_SETTINGS (settings));
-
-	use_qresync = camel_imapx_settings_get_use_qresync (
-		CAMEL_IMAPX_SETTINGS (settings));
-
-	g_object_unref (settings);
-
-	if (!imapx_connect_to_server (is, cancellable, error))
-		goto exception;
-
-	if (is->state == IMAPX_AUTHENTICATED)
-		goto preauthed;
-
-	if (!camel_session_authenticate_sync (
-		session, service, mechanism, cancellable, error))
-		goto exception;
-
-	/* After login we re-capa unless the server already told us. */
-	if (is->cinfo == NULL) {
-		GError *local_error = NULL;
-
-		ic = camel_imapx_command_new (
-			is, "CAPABILITY", NULL, "CAPABILITY");
-		imapx_command_run (is, ic, cancellable, &local_error);
-		camel_imapx_command_unref (ic);
-
-		if (local_error != NULL) {
-			g_propagate_error (error, local_error);
-			goto exception;
-		}
-	}
-
-	is->state = IMAPX_AUTHENTICATED;
-
-preauthed:
-	/* Fetch namespaces (if supported). */
-	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, NAMESPACE)) {
-		GError *local_error = NULL;
-
-		ic = camel_imapx_command_new (
-			is, "NAMESPACE", NULL, "NAMESPACE");
-		imapx_command_run (is, ic, cancellable, &local_error);
-		camel_imapx_command_unref (ic);
-
-		if (local_error != NULL) {
-			g_propagate_error (error, local_error);
-			goto exception;
-		}
-	}
-
-	/* Enable quick mailbox resynchronization (if supported). */
-	if (use_qresync && CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, QRESYNC)) {
-		GError *local_error = NULL;
-
-		ic = camel_imapx_command_new (
-			is, "ENABLE", NULL, "ENABLE CONDSTORE QRESYNC");
-		imapx_command_run (is, ic, cancellable, &local_error);
-		camel_imapx_command_unref (ic);
-
-		if (local_error != NULL) {
-			g_propagate_error (error, local_error);
-			goto exception;
-		}
-
-		is->use_qresync = TRUE;
-	} else {
-		is->use_qresync = FALSE;
-	}
-
-	/* Set NOTIFY options after enabling QRESYNC (if supported). */
-	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, NOTIFY)) {
-		GError *local_error = NULL;
-
-		/* XXX The list of FETCH attributes is negotiable. */
-		ic = camel_imapx_command_new (
-			is, "NOTIFY", NULL, "NOTIFY SET "
-			"(selected "
-			"(MessageNew (UID RFC822.SIZE RFC822.HEADER FLAGS)"
-			" MessageExpunge"
-			" FlagChange)) "
-			"(personal "
-			"(MessageNew"
-			" MessageExpunge"
-			" MailboxName"
-			" SubscriptionChange))");
-		imapx_command_run (is, ic, cancellable, &local_error);
-		camel_imapx_command_unref (ic);
-
-		if (local_error != NULL) {
-			g_propagate_error (error, local_error);
-			goto exception;
-		}
-	}
-
-	is->state = IMAPX_INITIALISED;
-
-	success = TRUE;
-
-	goto exit;
-
-exception:
-	imapx_disconnect (is);
-
-	if (is->cinfo) {
-		imapx_free_capability (is->cinfo);
-		is->cinfo = NULL;
-	}
-
-exit:
-	g_free (mechanism);
-
-	g_object_unref (session);
-	g_object_unref (store);
-
-	return success;
-}
-
-/* ********************************************************************** */
-
-static void
-imapx_command_fetch_message_done (CamelIMAPXServer *is,
-                                  CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	GetMessageData *data;
-	CamelIMAPXMailbox *mailbox;
-	GCancellable *cancellable;
-	GError *local_error = NULL;
-
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
-
-	/* This is only for pushing status messages. */
-	cancellable = camel_imapx_job_get_cancellable (job);
-
-	data = camel_imapx_job_get_data (job);
-	g_return_if_fail (data != NULL);
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_if_fail (mailbox != NULL);
-
-	/* We either have more to fetch (partial mode?), we are complete,
-	 * or we failed.  Failure is handled in the fetch code, so
-	 * we just return the job, or keep it alive with more requests */
-
-	g_atomic_int_add (&job->commands, -1);
-
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error fetching message"));
-
-	} else if (data->use_multi_fetch) {
-		gsize really_fetched = g_seekable_tell (G_SEEKABLE (data->stream));
-		/* Don't automatically stop when we reach the reported message
-		 * size -- some crappy servers (like Microsoft Exchange) have
-		 * a tendency to lie about it. Keep going (one request at a
-		 * time) until the data actually stop coming. */
-		if (data->fetch_offset < data->size ||
-		    data->fetch_offset == really_fetched) {
-			CamelIMAPXCommand *new_ic;
-
-			camel_operation_progress (
-				cancellable,
-				(data->fetch_offset *100) / data->size);
-
-			new_ic = camel_imapx_command_new (
-				is, "FETCH", mailbox,
-				"UID FETCH %t (BODY.PEEK[]",
-				data->uid);
-			camel_imapx_command_add (new_ic, "<%u.%u>", data->fetch_offset, MULTI_SIZE);
-			camel_imapx_command_add (new_ic, ")");
-			new_ic->complete = imapx_command_fetch_message_done;
-			camel_imapx_command_set_job (new_ic, job);
-			new_ic->pri = job->pri - 1;
-			data->fetch_offset += MULTI_SIZE;
-			g_atomic_int_add (&job->commands, 1);
-
-			imapx_command_queue (is, new_ic);
-
-			camel_imapx_command_unref (new_ic);
-
-			goto exit;
-		}
-	}
-
-	/* If we have more messages to fetch, skip the rest. */
-	if (g_atomic_int_get (&job->commands) > 0) {
-		/* Make sure no command will starve in a queue */
-		QUEUE_LOCK (is);
-		imapx_command_start_next (is);
-		QUEUE_UNLOCK (is);
-
-		goto exit;
-	}
-
-	/* No more messages to fetch, let's wrap things up. */
-
-	if (local_error == NULL) {
-		g_io_stream_close (data->stream, cancellable, &local_error);
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Failed to close the tmp stream"));
-	}
-
-	if (local_error == NULL &&
-	    g_cancellable_set_error_if_cancelled (cancellable, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error fetching message"));
-	}
-
-	if (local_error == NULL) {
-		gchar *cur_filename;
-		gchar *tmp_filename;
-		gchar *dirname;
-
-		cur_filename = camel_data_cache_get_filename (
-			data->message_cache, "cur", data->uid);
-
-		tmp_filename = camel_data_cache_get_filename (
-			data->message_cache, "tmp", data->uid);
-
-		dirname = g_path_get_dirname (cur_filename);
-		g_mkdir_with_parents (dirname, 0700);
-		g_free (dirname);
-
-		if (g_rename (tmp_filename, cur_filename) == 0) {
-			/* Exchange the "tmp" stream for the "cur" stream. */
-			g_clear_object (&data->stream);
-			data->stream = camel_data_cache_get (
-				data->message_cache, "cur",
-				data->uid, &local_error);
-		} else {
-			g_set_error (
-				&local_error, G_FILE_ERROR,
-				g_file_error_from_errno (errno),
-				"%s: %s",
-				_("Failed to copy the tmp file"),
-				g_strerror (errno));
-		}
-
-		g_free (cur_filename);
-		g_free (tmp_filename);
-	}
-
-	/* Delete the 'tmp' file only if the operation succeeded. It's because
-	   cancelled operations end before they are properly finished (IMAP-protocol speaking),
-	   thus if any other GET_MESSAGE operation was waiting for this job, then it
-	   realized that the message was not downloaded and opened its own "tmp" file, but
-	   of the same name, thus this remove would drop file which could be used
-	   by a different GET_MESSAGE job. */
-	if (!local_error && !g_cancellable_is_cancelled (cancellable))
-		camel_data_cache_remove (data->message_cache, "tmp", data->uid, NULL);
-
-	/* Avoid possible use-after-free when the imapx_unregister_job() can
-	   also free the 'job' structure. */
-	camel_imapx_job_ref (job);
-
-	imapx_unregister_job (is, job);
-
-	if (local_error != NULL) {
-		CamelIMAPXJob *pending_job;
-
-		/* Give a chance to other threads. */
-		g_thread_yield ();
-
-		pending_job = imapx_server_ref_job (is, mailbox, IMAPX_JOB_GET_MESSAGE, data->uid);
-		if (pending_job != NULL) {
-			GIOStream *cache_stream;
-
-			/* Wait for the job to finish. */
-			camel_imapx_job_wait (pending_job, NULL);
-			camel_imapx_job_unref (pending_job);
-
-			/* Disregard errors here.  If we failed to retrieve the
-			 * message from cache (implying the job we were waiting
-			 * on failed or got cancelled), we'll just re-fetch it. */
-			cache_stream = camel_data_cache_get (data->message_cache, "cur", data->uid, NULL);
-			if (cache_stream != NULL) {
-				g_clear_error (&local_error);
-
-				g_clear_object (&data->stream);
-				data->stream = cache_stream;
-			}
-		}
-
-		if (local_error) {
-			camel_imapx_job_take_error (job, local_error);
-			local_error = NULL;
-		}
-	}
-
-	camel_imapx_job_unref (job);
-
-exit:
-	if (local_error != NULL)
-		camel_imapx_job_take_error (job, local_error);
-
-	g_object_unref (mailbox);
-}
-
-static gboolean
-imapx_job_get_message_start (CamelIMAPXJob *job,
-                             CamelIMAPXServer *is,
-                             GCancellable *cancellable,
-                             GError **error)
-{
-	CamelIMAPXCommand *ic;
-	CamelIMAPXMailbox *mailbox;
-	GetMessageData *data;
-	gint i;
-	gboolean success = TRUE;
-
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_val_if_fail (mailbox != NULL, FALSE);
-
-	if (data->use_multi_fetch) {
-		for (i = 0; i < 3 && data->fetch_offset < data->size; i++) {
-			ic = camel_imapx_command_new (
-				is, "FETCH", mailbox,
-				"UID FETCH %t (BODY.PEEK[]",
-				data->uid);
-			camel_imapx_command_add (ic, "<%u.%u>", data->fetch_offset, MULTI_SIZE);
-			camel_imapx_command_add (ic, ")");
-			ic->complete = imapx_command_fetch_message_done;
-			camel_imapx_command_set_job (ic, job);
-			ic->pri = job->pri;
-			data->fetch_offset += MULTI_SIZE;
-			g_atomic_int_add (&job->commands, 1);
-
-			imapx_command_queue (is, ic);
-
-			camel_imapx_command_unref (ic);
-		}
-	} else {
-		ic = camel_imapx_command_new (
-			is, "FETCH", mailbox,
-			"UID FETCH %t (BODY.PEEK[])",
-			data->uid);
-		ic->complete = imapx_command_fetch_message_done;
-		camel_imapx_command_set_job (ic, job);
-		ic->pri = job->pri;
-		g_atomic_int_add (&job->commands, 1);
-
-		imapx_command_queue (is, ic);
-
-		camel_imapx_command_unref (ic);
-	}
-
-	g_object_unref (mailbox);
-
-	return success;
-}
-
-static gboolean
-imapx_job_get_message_matches (CamelIMAPXJob *job,
-                               CamelIMAPXMailbox *mailbox,
-                               const gchar *uid)
-{
-	GetMessageData *data;
-
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
-
-	if (!camel_imapx_job_has_mailbox (job, mailbox))
-		return FALSE;
-
-	if (g_strcmp0 (uid, data->uid) != 0)
-		return FALSE;
-
-	return TRUE;
-}
-
-/* ********************************************************************** */
-
-static void
-imapx_command_copy_messages_step_done (CamelIMAPXServer *is,
-                                       CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	CamelFolder *folder;
-	CamelIMAPXMailbox *mailbox;
-	CopyMessagesData *data;
-	GPtrArray *uids;
-	gint i;
-	GError *local_error = NULL;
-
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
-
-	data = camel_imapx_job_get_data (job);
-	g_return_if_fail (data != NULL);
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_if_fail (mailbox != NULL);
-
-	folder = imapx_server_ref_folder (is, mailbox);
-	g_return_if_fail (folder != NULL);
-
-	uids = data->uids;
-	i = data->index;
-
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		if (data->use_move_command)
-			g_prefix_error (
-				&local_error, "%s: ",
-				_("Error moving messages"));
-		else
-			g_prefix_error (
-				&local_error, "%s: ",
-				_("Error copying messages"));
-		camel_imapx_job_take_error (job, local_error);
-		goto exit;
-	}
-
-	if (ic->status && ic->status->u.copyuid.uids && ic->status->u.copyuid.copied_uids &&
-	    ic->status->u.copyuid.uids->len == ic->status->u.copyuid.copied_uids->len) {
-		CamelFolder *destination;
-
-		destination = imapx_server_ref_folder (is, data->destination);
-		if (destination) {
-			CamelMessageInfo *source_info, *destination_info;
-			CamelFolderChangeInfo *changes;
-			gint ii;
-
-			changes = camel_folder_change_info_new ();
-
-			for (ii = 0; ii < ic->status->u.copyuid.uids->len; ii++) {
-				gchar *uid;
-				gboolean is_new = FALSE;
-
-				uid = g_strdup_printf ("%d", g_array_index (ic->status->u.copyuid.uids, guint32, ii));
-				source_info = camel_folder_summary_get (folder->summary, uid);
-				g_free (uid);
-
-				if (!source_info)
-					continue;
-
-				uid = g_strdup_printf ("%d", g_array_index (ic->status->u.copyuid.copied_uids, guint32, ii));
-				destination_info = camel_folder_summary_get (folder->summary, uid);
-
-				if (!destination_info) {
-					is_new = TRUE;
-					destination_info = camel_message_info_clone (source_info);
-					destination_info->summary = destination->summary;
-					camel_pstring_free (destination_info->uid);
-					destination_info->uid = camel_pstring_strdup (uid);
-				}
-
-				g_free (uid);
-
-				imapx_set_message_info_flags_for_new_message (
-					destination_info,
-					((CamelMessageInfoBase *) source_info)->flags,
-					((CamelMessageInfoBase *) source_info)->user_flags,
-					TRUE,
-					((CamelMessageInfoBase *) source_info)->user_tags,
-					camel_imapx_mailbox_get_permanentflags (data->destination));
-				if (is_new)
-					camel_folder_summary_add (destination->summary, destination_info);
-				camel_folder_change_info_add_uid (changes, destination_info->uid);
-
-				camel_message_info_unref (source_info);
-				if (!is_new)
-					camel_message_info_unref (destination_info);
-			}
-
-			if (camel_folder_change_info_changed (changes)) {
-				camel_folder_summary_touch (destination->summary);
-				camel_folder_summary_save_to_db (destination->summary, NULL);
-				camel_folder_changed (destination, changes);
-			}
-
-			camel_folder_change_info_free (changes);
-			g_object_unref (destination);
-		}
-	}
-
-	if (data->delete_originals) {
-		gint j;
-
-		for (j = data->last_index; j < i; j++)
-			camel_folder_delete_message (folder, uids->pdata[j]);
-	}
-
-	if (i < uids->len) {
-		imapx_command_copy_messages_step_start (
-			is, job, i, &local_error);
-
-		if (local_error != NULL)
-			camel_imapx_job_take_error (job, local_error);
-	}
-
-exit:
-	g_object_unref (folder);
-	g_object_unref (mailbox);
-
-	imapx_unregister_job (is, job);
-}
-
-static gboolean
-imapx_command_copy_messages_step_start (CamelIMAPXServer *is,
-                                        CamelIMAPXJob *job,
-                                        gint index,
-                                        GError **error)
-{
-	CamelIMAPXMailbox *mailbox;
-	CamelIMAPXCommand *ic;
-	CopyMessagesData *data;
-	GPtrArray *uids;
-	gint i = index;
-	gboolean success = TRUE;
-
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_val_if_fail (mailbox != NULL, FALSE);
-
-	uids = data->uids;
-
-	if (data->use_move_command)
-		ic = camel_imapx_command_new (is, "MOVE", mailbox, "UID MOVE ");
-	else
-		ic = camel_imapx_command_new (is, "COPY", mailbox, "UID COPY ");
-	ic->complete = imapx_command_copy_messages_step_done;
-	camel_imapx_command_set_job (ic, job);
-	ic->pri = job->pri;
-	data->last_index = i;
-
-	g_object_unref (mailbox);
-
-	for (; i < uids->len; i++) {
-		gint res;
-		const gchar *uid = (gchar *) g_ptr_array_index (uids, i);
-
-		res = imapx_uidset_add (&data->uidset, ic, uid);
-		if (res == 1) {
-			camel_imapx_command_add (ic, " %M", data->destination);
-			data->index = i + 1;
-			imapx_command_queue (is, ic);
-			goto exit;
-		}
-	}
-
-	data->index = i;
-	if (imapx_uidset_done (&data->uidset, ic)) {
-		camel_imapx_command_add (ic, " %M", data->destination);
-		imapx_command_queue (is, ic);
-		goto exit;
-	}
-
-exit:
-	camel_imapx_command_unref (ic);
-
-	return success;
-}
-
-static gboolean
-imapx_job_copy_messages_start (CamelIMAPXJob *job,
-                               CamelIMAPXServer *is,
-                               GCancellable *cancellable,
-                               GError **error)
-{
-	CamelIMAPXMailbox *mailbox;
-	CopyMessagesData *data;
-	gboolean success;
-
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_val_if_fail (mailbox != NULL, FALSE);
-
-	success = imapx_server_sync_changes (
-		is, mailbox, job->type, job->pri, cancellable, error);
-	if (!success)
-		imapx_unregister_job (is, job);
-
-	/* XXX Should we still do this even if a failure occurred? */
-	g_ptr_array_sort (data->uids, (GCompareFunc) imapx_uids_array_cmp);
-	imapx_uidset_init (&data->uidset, 0, MAX_COMMAND_LEN);
-
-	g_object_unref (mailbox);
-
-	return imapx_command_copy_messages_step_start (is, job, 0, error);
-}
-
-static gboolean
-imapx_job_copy_messages_matches (CamelIMAPXJob *job,
-				 CamelIMAPXMailbox *mailbox,
-				 const gchar *uid)
-{
-	return camel_imapx_job_has_mailbox (job, mailbox);
-}
-
-/* ********************************************************************** */
-
-static void
-imapx_command_append_message_done (CamelIMAPXServer *is,
-                                   CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	CamelIMAPXFolder *ifolder;
-	CamelIMAPXMailbox *mailbox;
-	CamelFolder *folder;
-	CamelMessageInfo *mi;
-	AppendMessageData *data;
-	gchar *cur, *old_uid;
-	guint32 uidvalidity;
-	GError *local_error = NULL;
-
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
-
-	data = camel_imapx_job_get_data (job);
-	g_return_if_fail (data != NULL);
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_if_fail (mailbox != NULL);
-
-	folder = imapx_server_ref_folder (is, mailbox);
-	g_return_if_fail (folder != NULL);
-
-	uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox);
-
-	ifolder = CAMEL_IMAPX_FOLDER (folder);
-
-	/* Append done.  If we the server supports UIDPLUS we will get
-	 * an APPENDUID response with the new uid.  This lets us move the
-	 * message we have directly to the cache and also create a correctly
-	 * numbered MessageInfo, without losing any information.  Otherwise
-	 * we have to wait for the server to let us know it was appended. */
-
-	mi = camel_message_info_clone (data->info);
-	old_uid = g_strdup (data->info->uid);
-
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error appending message"));
-		camel_imapx_job_take_error (job, local_error);
-
-	} else if (ic->status && ic->status->condition == IMAPX_APPENDUID) {
-		c (is->tagprefix, "Got appenduid %d %d\n", (gint) ic->status->u.appenduid.uidvalidity, (gint) ic->status->u.appenduid.uid);
-		if (ic->status->u.appenduid.uidvalidity == uidvalidity) {
-			CamelFolderChangeInfo *changes;
-
-			data->appended_uid = g_strdup_printf ("%u", (guint) ic->status->u.appenduid.uid);
-			mi->uid = camel_pstring_add (data->appended_uid, FALSE);
-
-			cur = camel_data_cache_get_filename  (ifolder->cache, "cur", mi->uid);
-			if (g_rename (data->path, cur) == -1 && errno != ENOENT) {
-				g_warning ("%s: Failed to rename '%s' to '%s': %s", G_STRFUNC, data->path, cur, g_strerror (errno));
-			}
-
-			imapx_set_message_info_flags_for_new_message (
-				mi,
-				((CamelMessageInfoBase *) data->info)->flags,
-				((CamelMessageInfoBase *) data->info)->user_flags,
-				TRUE,
-				((CamelMessageInfoBase *) data->info)->user_tags,
-				camel_imapx_mailbox_get_permanentflags (mailbox));
-			camel_folder_summary_add (folder->summary, mi);
-			changes = camel_folder_change_info_new ();
-			camel_folder_change_info_add_uid (changes, mi->uid);
-			camel_folder_changed (folder, changes);
-			camel_folder_change_info_free (changes);
-
-			g_free (cur);
-		} else {
-			c (is->tagprefix, "but uidvalidity changed \n");
-		}
-	}
-
-	camel_data_cache_remove (ifolder->cache, "new", old_uid, NULL);
-	g_free (old_uid);
-
-	g_object_unref (folder);
-	g_object_unref (mailbox);
-
-	imapx_unregister_job (is, job);
-}
-
-static const gchar *
-get_month_str (gint month)
-{
-	static const gchar tm_months[][4] = {
-		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
-		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-	};
-
-	if (month < 1 || month > 12)
-		return NULL;
-
-	return tm_months[month - 1];
-}
-
-static gboolean
-imapx_job_append_message_start (CamelIMAPXJob *job,
-                                CamelIMAPXServer *is,
-                                GCancellable *cancellable,
-                                GError **error)
-{
-	CamelIMAPXMailbox *mailbox;
-	CamelIMAPXCommand *ic;
-	AppendMessageData *data;
-
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_val_if_fail (mailbox != NULL, FALSE);
-
-	if (data->date_time > 0) {
-		gchar *date_time;
-		struct tm stm;
-
-		gmtime_r (&data->date_time, &stm);
-
-		/* Store always in UTC */
-		date_time = g_strdup_printf (
-			"\"%02d-%s-%04d %02d:%02d:%02d +0000\"",
-			stm.tm_mday,
-			get_month_str (stm.tm_mon + 1),
-			stm.tm_year + 1900,
-			stm.tm_hour,
-			stm.tm_min,
-			stm.tm_sec);
-
-		ic = camel_imapx_command_new (
-			is, "APPEND", NULL,
-			"APPEND %M %F %t %P", mailbox,
-			((CamelMessageInfoBase *) data->info)->flags,
-			((CamelMessageInfoBase *) data->info)->user_flags,
-			date_time,
-			data->path);
-
-		g_free (date_time);
-	} else {
-		ic = camel_imapx_command_new (
-			is, "APPEND", NULL,
-			"APPEND %M %F %P", mailbox,
-			((CamelMessageInfoBase *) data->info)->flags,
-			((CamelMessageInfoBase *) data->info)->user_flags,
-			data->path);
-	}
-
-	ic->complete = imapx_command_append_message_done;
-	camel_imapx_command_set_job (ic, job);
-	ic->pri = job->pri;
-	g_atomic_int_add (&job->commands, 1);
-
-	imapx_command_queue (is, ic);
-
-	camel_imapx_command_unref (ic);
-
-	g_object_unref (mailbox);
-
-	return TRUE;
-}
-
-/* ********************************************************************** */
-
-static gint
-imapx_refresh_info_uid_cmp (gconstpointer ap,
-                            gconstpointer bp,
-                            gboolean ascending)
-{
-	guint av, bv;
-
-	av = g_ascii_strtoull ((const gchar *) ap, NULL, 10);
-	bv = g_ascii_strtoull ((const gchar *) bp, NULL, 10);
-
-	if (av < bv)
-		return ascending ? -1 : 1;
-	else if (av > bv)
-		return ascending ? 1 : -1;
-	else
-		return 0;
-}
-
-static gint
-imapx_uids_array_cmp (gconstpointer ap,
-                      gconstpointer bp)
-{
-	const gchar **a = (const gchar **) ap;
-	const gchar **b = (const gchar **) bp;
-
-	return imapx_refresh_info_uid_cmp (*a, *b, TRUE);
-}
-
-static gint
-imapx_refresh_info_cmp (gconstpointer ap,
-                        gconstpointer bp)
-{
-	const struct _refresh_info *a = ap;
-	const struct _refresh_info *b = bp;
-
-	return imapx_refresh_info_uid_cmp (a->uid, b->uid, TRUE);
-}
-
-static gint
-imapx_refresh_info_cmp_descending (gconstpointer ap,
-                                   gconstpointer bp)
-{
-	const struct _refresh_info *a = ap;
-	const struct _refresh_info *b = bp;
-
-	return imapx_refresh_info_uid_cmp (a->uid, b->uid, FALSE);
-
-}
-
-/* skips over non-server uids (pending appends) */
-static guint
-imapx_index_next (GPtrArray *uids,
-                  CamelFolderSummary *s,
-                  guint index)
-{
-
-	while (index < uids->len) {
-		CamelMessageInfo *info;
-
-		index++;
-		if (index >= uids->len)
-			break;
-
-		info = camel_folder_summary_get (s, g_ptr_array_index (uids, index));
-		if (!info)
-			continue;
-
-		if (info && (strchr (camel_message_info_uid (info), '-') != NULL)) {
-			camel_message_info_unref (info);
-			e ('?', "Ignoring offline uid '%s'\n", camel_message_info_uid (info));
-		} else {
-			camel_message_info_unref (info);
-			break;
-		}
-	}
-
-	return index;
-}
-
-static void
-imapx_command_step_fetch_done (CamelIMAPXServer *is,
-                               CamelIMAPXCommand *ic)
-{
-	CamelIMAPXMailbox *mailbox;
-	CamelIMAPXSummary *isum;
-	CamelIMAPXJob *job;
-	CamelFolder *folder;
-	RefreshInfoData *data;
-	gint i;
-	GError *local_error = NULL;
-
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
-
-	data = camel_imapx_job_get_data (job);
-	g_return_if_fail (data != NULL);
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_if_fail (mailbox != NULL);
-
-	folder = imapx_server_ref_folder (is, mailbox);
-	g_return_if_fail (folder != NULL);
-
-	data->scan_changes = FALSE;
-
-	isum = CAMEL_IMAPX_SUMMARY (folder->summary);
-
-	i = data->index;
-
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error fetching message headers"));
-		camel_imapx_job_take_error (job, local_error);
-		goto exit;
-	}
-
-	if (camel_folder_change_info_changed (data->changes)) {
-		imapx_update_store_summary (folder);
-		camel_folder_summary_save_to_db (folder->summary, NULL);
-		camel_folder_changed (folder, data->changes);
-	}
-
-	camel_folder_change_info_clear (data->changes);
-
-	if (i < data->infos->len) {
-		ic = camel_imapx_command_new (
-			is, "FETCH", mailbox, "UID FETCH ");
-		ic->complete = imapx_command_step_fetch_done;
-		camel_imapx_command_set_job (ic, job);
-		ic->pri = job->pri - 1;
-
-		data->last_index = i;
-
-		for (; i < data->infos->len; i++) {
-			gint res;
-			struct _refresh_info *r = &g_array_index (data->infos, struct _refresh_info, i);
-
-			if (!r->exists) {
-				res = imapx_uidset_add (&data->uidset, ic, r->uid);
-				if (res == 1) {
-					camel_imapx_command_add (ic, " (RFC822.SIZE RFC822.HEADER)");
-					data->index = i + 1;
-
-					imapx_command_queue (is, ic);
-
-					camel_imapx_command_unref (ic);
-
-					g_object_unref (folder);
-					g_object_unref (mailbox);
-
-					return;
-				}
-			}
-		}
-
-		data->index = data->infos->len;
-		if (imapx_uidset_done (&data->uidset, ic)) {
-			camel_imapx_command_add (ic, " (RFC822.SIZE RFC822.HEADER)");
-
-			imapx_command_queue (is, ic);
-
-			camel_imapx_command_unref (ic);
-
-			g_object_unref (folder);
-			g_object_unref (mailbox);
-
-			return;
-		}
-
-		/* XXX What fate for our newly-created but unsubmitted
-		 *     CamelIMAPXCommand if we get here?  I guess just
-		 *     discard it and move on?  Also warn so I know if
-		 *     we're actually taking this branch for real. */
-		camel_imapx_command_unref (ic);
-		g_warn_if_reached ();
-	}
-
-	if (camel_folder_summary_count (folder->summary)) {
-		gchar *uid;
-		guint32 uidl;
-		guint32 uidnext;
-
-		uid = camel_imapx_dup_uid_from_summary_index (
-			folder,
-			camel_folder_summary_count (folder->summary) - 1);
-		if (uid) {
-			uidl = (guint32) strtoull (uid, NULL, 10);
-			g_free (uid);
-
-			uidl++;
-
-			uidnext = camel_imapx_mailbox_get_uidnext (mailbox);
-
-			if (uidl > uidnext) {
-				c (
-					is->tagprefix,
-					"Updating uidnext for '%s' to %ul\n",
-					camel_imapx_mailbox_get_name (mailbox),
-					uidl);
-				camel_imapx_mailbox_set_uidnext (mailbox, uidl);
-			}
-		}
-	}
-
-	isum->uidnext = camel_imapx_mailbox_get_uidnext (mailbox);
-
-exit:
-	refresh_info_data_infos_free (data);
-
-	g_object_unref (folder);
-	g_object_unref (mailbox);
-
-	imapx_unregister_job (is, job);
+	return success;
 }
 
-static gint
-imapx_uid_cmp (gconstpointer ap,
-               gconstpointer bp,
-               gpointer data)
-{
-	const gchar *a = ap, *b = bp;
-	gchar *ae, *be;
-	gulong av, bv;
-
-	av = strtoul (a, &ae, 10);
-	bv = strtoul (b, &be, 10);
-
-	if (av < bv)
-		return -1;
-	else if (av > bv)
-		return 1;
-
-	if (*ae == '-')
-		ae++;
-	if (*be == '-')
-		be++;
+/* ********************************************************************** */
 
-	return strcmp (ae, be);
-}
+/* FIXME: this is basically a copy of the same in camel-imapx-utils.c */
+static struct {
+	const gchar *name;
+	guint32 flag;
+} flags_table[] = {
+	{ "\\ANSWERED", CAMEL_MESSAGE_ANSWERED },
+	{ "\\DELETED", CAMEL_MESSAGE_DELETED },
+	{ "\\DRAFT", CAMEL_MESSAGE_DRAFT },
+	{ "\\FLAGGED", CAMEL_MESSAGE_FLAGGED },
+	{ "\\SEEN", CAMEL_MESSAGE_SEEN },
+	{ "\\RECENT", CAMEL_IMAPX_MESSAGE_RECENT },
+	{ "JUNK", CAMEL_MESSAGE_JUNK },
+	{ "NOTJUNK", CAMEL_MESSAGE_NOTJUNK }
+};
 
 static void
-imapx_job_scan_changes_done (CamelIMAPXServer *is,
-                             CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	CamelIMAPXMailbox *mailbox;
-	CamelIMAPXSettings *settings;
-	CamelFolder *folder;
-	RefreshInfoData *data;
-	GCancellable *cancellable;
-	guint uidset_size;
-	guint32 unseen;
-	GError *local_error = NULL;
-
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
-
-	/* This is only for pushing status messages. */
-	cancellable = camel_imapx_job_get_cancellable (job);
-
-	data = camel_imapx_job_get_data (job);
-	g_return_if_fail (data != NULL);
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_if_fail (mailbox != NULL);
-
-	folder = imapx_server_ref_folder (is, mailbox);
-	g_return_if_fail (folder != NULL);
-
-	data->scan_changes = FALSE;
-
-	settings = camel_imapx_server_ref_settings (is);
-	uidset_size = camel_imapx_settings_get_batch_fetch_count (settings);
-	g_object_unref (settings);
-
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error retrieving message"));
-		camel_imapx_job_take_error (job, local_error);
-
-	} else {
-		GCompareDataFunc uid_cmp = imapx_uid_cmp;
-		CamelMessageInfo *s_minfo = NULL;
-		CamelIMAPXMessageInfo *info;
-		CamelFolderSummary *s = folder->summary;
-		GList *removed = NULL, *l;
-		gboolean fetch_new = FALSE;
-		gint i;
-		guint j = 0;
-		GPtrArray *uids;
-
-		/* Actually we wanted to do this after the SELECT but before the
-		 * FETCH command was issued. But this should suffice. */
-		((CamelIMAPXSummary *) s)->uidnext =
-			camel_imapx_mailbox_get_uidnext (mailbox);
-		((CamelIMAPXSummary *) s)->modseq =
-			camel_imapx_mailbox_get_highestmodseq (mailbox);
-
-		/* Here we do the typical sort/iterate/merge loop.
-		 * If the server flags dont match what we had, we modify our
-		 * flags to pick up what the server now has - but we merge
-		 * not overwrite */
-
-		/* FIXME: We also have to check the offline directory for
-		 * anything missing in our summary, and also queue up jobs
-		 * for all outstanding messages to be uploaded */
-
-		camel_folder_summary_lock (s);
-
-		/* obtain a copy to be thread safe */
-		uids = camel_folder_summary_get_array (s);
-
-		qsort (data->infos->data, data->infos->len, sizeof (struct _refresh_info), imapx_refresh_info_cmp);
-		g_ptr_array_sort (uids, (GCompareFunc) imapx_uids_array_cmp);
-
-		if (uids->len)
-			s_minfo = camel_folder_summary_get (s, g_ptr_array_index (uids, 0));
-
-		for (i = 0; i < data->infos->len; i++) {
-			struct _refresh_info *r = &g_array_index (data->infos, struct _refresh_info, i);
-
-			while (s_minfo && uid_cmp (camel_message_info_uid (s_minfo), r->uid, s) < 0) {
-				const gchar *uid = camel_message_info_uid (s_minfo);
-
-				camel_folder_change_info_remove_uid (data->changes, uid);
-				removed = g_list_prepend (removed, (gpointer ) g_strdup (uid));
-				camel_message_info_unref (s_minfo);
-				s_minfo = NULL;
-
-				j = imapx_index_next (uids, s, j);
-				if (j < uids->len)
-					s_minfo = camel_folder_summary_get (s, g_ptr_array_index (uids, j));
-			}
-
-			info = NULL;
-			if (s_minfo && uid_cmp (s_minfo->uid, r->uid, s) == 0) {
-				info = (CamelIMAPXMessageInfo *) s_minfo;
-
-				if (imapx_update_message_info_flags (
-						(CamelMessageInfo *) info,
-						r->server_flags,
-						r->server_user_flags,
-						camel_imapx_mailbox_get_permanentflags (mailbox),
-						folder, FALSE))
-					camel_folder_change_info_change_uid (
-						data->changes,
-						camel_message_info_uid (s_minfo));
-				r->exists = TRUE;
-			} else
-				fetch_new = TRUE;
-
-			if (s_minfo) {
-				camel_message_info_unref (s_minfo);
-				s_minfo = NULL;
-			}
-
-			if (j >= uids->len)
-				break;
-
-			j = imapx_index_next (uids, s, j);
-			if (j < uids->len)
-				s_minfo = camel_folder_summary_get (s, g_ptr_array_index (uids, j));
-		}
-
-		if (s_minfo)
-			camel_message_info_unref (s_minfo);
-
-		while (j < uids->len) {
-			s_minfo = camel_folder_summary_get (s, g_ptr_array_index (uids, j));
-
-			if (!s_minfo) {
-				j++;
-				continue;
-			}
-
-			e (is->tagprefix, "Message %s vanished\n", s_minfo->uid);
-			removed = g_list_prepend (removed, (gpointer) g_strdup (s_minfo->uid));
-			camel_message_info_unref (s_minfo);
-			j++;
-		}
-
-		for (l = removed; l != NULL; l = g_list_next (l)) {
-			gchar *uid = (gchar *) l->data;
-
-			camel_folder_change_info_remove_uid (data->changes, uid);
-		}
-
-		if (removed != NULL) {
-			camel_folder_summary_remove_uids (s, removed);
-			camel_folder_summary_touch (s);
-
-			g_list_free_full (removed, (GDestroyNotify) g_free);
-		}
-
-		camel_folder_summary_save_to_db (s, NULL);
-		imapx_update_store_summary (folder);
-
-		camel_folder_summary_unlock (s);
-
-		if (camel_folder_change_info_changed (data->changes))
-			camel_folder_changed (folder, data->changes);
-		camel_folder_change_info_clear (data->changes);
-
-		camel_folder_summary_free_array (uids);
-
-		/* If we have any new messages, download their headers, but only a few (100?) at a time */
-		if (fetch_new) {
-			job->pop_operation_msg = TRUE;
-
-			camel_operation_push_message (
-				cancellable,
-				_("Fetching summary information for new messages in '%s'"),
-				camel_folder_get_display_name (folder));
-
-			imapx_uidset_init (&data->uidset, uidset_size, 0);
-			/* These are new messages which arrived since we last knew the unseen count;
-			 * update it as they arrive. */
-			data->update_unseen = TRUE;
-
-			g_object_unref (folder);
-			g_object_unref (mailbox);
-
-			return imapx_command_step_fetch_done (is, ic);
-		}
-	}
-
-	refresh_info_data_infos_free (data);
-
-	/* There's no sane way to get the server-side unseen count
-	 * on the select mailbox, so just work it out from the flags. */
-	unseen = camel_folder_summary_get_unread_count (folder->summary);
-	camel_imapx_mailbox_set_unseen (mailbox, unseen);
-
-	g_object_unref (folder);
-	g_object_unref (mailbox);
-
-	imapx_unregister_job (is, job);
-}
-
-static gboolean
-imapx_job_scan_changes_start (CamelIMAPXJob *job,
-                              CamelIMAPXServer *is,
-                              GCancellable *cancellable,
-                              GError **error)
+imapx_server_set_store (CamelIMAPXServer *server,
+                        CamelIMAPXStore *store)
 {
-	CamelFolder *folder;
-	CamelIMAPXCommand *ic;
-	CamelIMAPXMailbox *mailbox;
-	RefreshInfoData *data;
-
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_val_if_fail (mailbox != NULL, FALSE);
-
-	folder = imapx_server_ref_folder (is, mailbox);
-	g_return_val_if_fail (folder != NULL, FALSE);
-
-	job->pop_operation_msg = TRUE;
-
-	camel_operation_push_message (
-		cancellable,
-		_("Scanning for changed messages in '%s'"),
-		camel_folder_get_display_name (folder));
-
-	ic = camel_imapx_command_new (
-		is, "FETCH", mailbox,
-		"UID FETCH 1:* (UID FLAGS)");
-	camel_imapx_command_set_job (ic, job);
-	ic->complete = imapx_job_scan_changes_done;
-
-	data->scan_changes = TRUE;
-	ic->pri = job->pri;
-	refresh_info_data_infos_free (data);
-	data->infos = g_array_new (0, 0, sizeof (struct _refresh_info));
-
-	imapx_command_queue (is, ic);
-
-	camel_imapx_command_unref (ic);
-
-	g_object_unref (folder);
-	g_object_unref (mailbox);
+	g_return_if_fail (CAMEL_IS_IMAPX_STORE (store));
 
-	return TRUE;
+	g_weak_ref_set (&server->priv->store, store);
 }
 
 static void
-imapx_command_fetch_new_messages_done (CamelIMAPXServer *is,
-                                       CamelIMAPXCommand *ic)
+imapx_server_set_property (GObject *object,
+                           guint property_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
 {
-	CamelIMAPXJob *job;
-	CamelIMAPXSummary *isum;
-	CamelIMAPXMailbox *mailbox;
-	CamelFolder *folder;
-	RefreshInfoData *data;
-	GError *local_error = NULL;
-
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
-
-	data = camel_imapx_job_get_data (job);
-	g_return_if_fail (data != NULL);
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_if_fail (mailbox != NULL);
-
-	folder = imapx_server_ref_folder (is, mailbox);
-	g_return_if_fail (folder != NULL);
-
-	isum = CAMEL_IMAPX_SUMMARY (folder->summary);
-
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error fetching new messages"));
-		camel_imapx_job_take_error (job, local_error);
-		goto exit;
-	}
-
-	if (camel_folder_change_info_changed (data->changes)) {
-		camel_folder_summary_save_to_db (folder->summary, NULL);
-		imapx_update_store_summary (folder);
-		camel_folder_changed (folder, data->changes);
-		camel_folder_change_info_clear (data->changes);
-	}
-
-	if (camel_folder_summary_count (folder->summary)) {
-		gchar *uid;
-		guint32 uidl;
-		guint32 uidnext;
-
-		uid = camel_imapx_dup_uid_from_summary_index (
-			folder,
-			camel_folder_summary_count (folder->summary) - 1);
-		if (uid) {
-			uidl = (guint32) strtoull (uid, NULL, 10);
-			g_free (uid);
-
-			uidl++;
-
-			uidnext = camel_imapx_mailbox_get_uidnext (mailbox);
-
-			if (uidl > uidnext) {
-				c (
-					is->tagprefix,
-					"Updating uidnext for '%s' to %ul\n",
-					camel_imapx_mailbox_get_name (mailbox),
-					uidl);
-				camel_imapx_mailbox_set_uidnext (mailbox, uidl);
-			}
-		}
+	switch (property_id) {
+		case PROP_STORE:
+			imapx_server_set_store (
+				CAMEL_IMAPX_SERVER (object),
+				g_value_get_object (value));
+			return;
 	}
 
-	isum->uidnext = camel_imapx_mailbox_get_uidnext (mailbox);
-
-exit:
-	g_object_unref (folder);
-	g_object_unref (mailbox);
-
-	imapx_unregister_job (is, job);
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 }
 
 static void
-imapx_command_fetch_new_uids_done (CamelIMAPXServer *is,
-                                   CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	RefreshInfoData *data;
-
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
-
-	data = camel_imapx_job_get_data (job);
-	g_return_if_fail (data != NULL);
-
-	data->scan_changes = FALSE;
-
-	qsort (
-		data->infos->data,
-		data->infos->len,
-		sizeof (struct _refresh_info),
-		imapx_refresh_info_cmp_descending);
-
-	imapx_command_step_fetch_done (is, ic);
-}
-
-static gboolean
-imapx_job_fetch_new_messages_start (CamelIMAPXJob *job,
-                                    CamelIMAPXServer *is,
-                                    GCancellable *cancellable,
-                                    GError **error)
+imapx_server_get_property (GObject *object,
+                           guint property_id,
+                           GValue *value,
+                           GParamSpec *pspec)
 {
-	CamelIMAPXCommand *ic;
-	CamelFolder *folder;
-	CamelIMAPXMailbox *mailbox;
-	CamelIMAPXSettings *settings;
-	CamelSortType fetch_order;
-	RefreshInfoData *data;
-	guint32 total, diff;
-	guint32 messages;
-	guint uidset_size;
-	gchar *uid = NULL;
-
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_val_if_fail (mailbox != NULL, FALSE);
-
-	folder = imapx_server_ref_folder (is, mailbox);
-	g_return_val_if_fail (folder != NULL, FALSE);
-
-	settings = camel_imapx_server_ref_settings (is);
-	fetch_order = camel_imapx_settings_get_fetch_order (settings);
-	uidset_size = camel_imapx_settings_get_batch_fetch_count (settings);
-	g_object_unref (settings);
-
-	messages = camel_imapx_mailbox_get_messages (mailbox);
+	switch (property_id) {
+		case PROP_STORE:
+			g_value_take_object (
+				value,
+				camel_imapx_server_ref_store (
+				CAMEL_IMAPX_SERVER (object)));
+			return;
+	}
 
-	total = camel_folder_summary_count (folder->summary);
-	diff = messages - total;
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
 
-	if (total > 0) {
-		guint64 uidl;
-		uid = camel_imapx_dup_uid_from_summary_index (folder, total - 1);
-		if (uid) {
-			uidl = strtoull (uid, NULL, 10);
-			g_free (uid);
-			uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uidl + 1);
-		} else {
-			uid = g_strdup ("1");
-		}
-	} else
-		uid = g_strdup ("1");
+static void
+imapx_server_dispose (GObject *object)
+{
+	CamelIMAPXServer *server = CAMEL_IMAPX_SERVER (object);
 
-	job->pop_operation_msg = TRUE;
+	g_cancellable_cancel (server->priv->cancellable);
 
-	camel_operation_push_message (
-		cancellable,
-		_("Fetching summary information for new messages in '%s'"),
-		camel_folder_get_display_name (folder));
+	imapx_disconnect (server);
 
-	if (diff > uidset_size || fetch_order == CAMEL_SORT_DESCENDING) {
-		ic = camel_imapx_command_new (
-			is, "FETCH", mailbox,
-			"UID FETCH %s:* (UID FLAGS)", uid);
-		imapx_uidset_init (&data->uidset, uidset_size, 0);
-		refresh_info_data_infos_free (data);
-		data->infos = g_array_new (0, 0, sizeof (struct _refresh_info));
-		ic->pri = job->pri;
+	g_weak_ref_set (&server->priv->store, NULL);
 
-		data->scan_changes = TRUE;
+	g_clear_object (&server->priv->subprocess);
 
-		if (fetch_order == CAMEL_SORT_DESCENDING)
-			ic->complete = imapx_command_fetch_new_uids_done;
-		else
-			ic->complete = imapx_command_step_fetch_done;
-	} else {
-		ic = camel_imapx_command_new (
-			is, "FETCH", mailbox,
-			"UID FETCH %s:* (RFC822.SIZE RFC822.HEADER FLAGS)", uid);
-		ic->pri = job->pri;
-		ic->complete = imapx_command_fetch_new_messages_done;
+	g_mutex_lock (&server->priv->idle_lock);
+	g_clear_object (&server->priv->idle_cancellable);
+	g_clear_object (&server->priv->idle_mailbox);
+	if (server->priv->idle_pending) {
+		g_source_destroy (server->priv->idle_pending);
+		g_source_unref (server->priv->idle_pending);
+		server->priv->idle_pending = NULL;
 	}
+	g_mutex_unlock (&server->priv->idle_lock);
 
-	camel_imapx_command_set_job (ic, job);
-
-	imapx_command_queue (is, ic);
-
-	camel_imapx_command_unref (ic);
-
-	g_free (uid);
-
-	g_object_unref (folder);
-	g_object_unref (mailbox);
+	g_clear_object (&server->priv->subprocess);
 
-	return TRUE;
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (camel_imapx_server_parent_class)->dispose (object);
 }
 
-static gboolean
-imapx_job_refresh_info_start (CamelIMAPXJob *job,
-                              CamelIMAPXServer *is,
-                              GCancellable *cancellable,
-                              GError **error)
+static void
+imapx_server_finalize (GObject *object)
 {
-	CamelIMAPXSummary *isum;
-	CamelFolder *folder;
-	CamelIMAPXMailbox *mailbox;
-	const gchar *full_name;
-	gboolean need_rescan = FALSE;
-	gboolean is_selected = FALSE;
-	gboolean can_qresync = FALSE;
-	gboolean success;
-	guint32 messages;
-	guint32 unseen;
-	guint32 uidnext;
-	guint32 uidvalidity;
-	guint64 highestmodseq;
-	guint32 total;
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_val_if_fail (mailbox != NULL, FALSE);
-
-	folder = imapx_server_ref_folder (is, mailbox);
-	g_return_val_if_fail (folder != NULL, FALSE);
+	CamelIMAPXServer *is = CAMEL_IMAPX_SERVER (object);
 
-	isum = CAMEL_IMAPX_SUMMARY (folder->summary);
+	g_mutex_clear (&is->priv->stream_lock);
+	g_mutex_clear (&is->priv->select_lock);
+	g_mutex_clear (&is->priv->changes_lock);
 
-	full_name = camel_folder_get_full_name (folder);
+	camel_folder_change_info_free (is->priv->changes);
+	imapx_free_status (is->priv->copyuid_status);
 
-	/* Sync changes first, else unread count will not
-	 * match. Need to think about better ways for this */
-	success = imapx_server_sync_changes (
-		is, mailbox, job->type, job->pri, cancellable, error);
-	if (!success)
-		goto done;
+	g_free (is->priv->context);
+	g_hash_table_destroy (is->priv->untagged_handlers);
 
-	messages = camel_imapx_mailbox_get_messages (mailbox);
-	unseen = camel_imapx_mailbox_get_unseen (mailbox);
-	uidnext = camel_imapx_mailbox_get_uidnext (mailbox);
-	uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox);
-	highestmodseq = camel_imapx_mailbox_get_highestmodseq (mailbox);
+	if (is->priv->inactivity_timeout != NULL)
+		g_source_unref (is->priv->inactivity_timeout);
+	g_mutex_clear (&is->priv->inactivity_timeout_lock);
 
-#if 0	/* There are issues with this still; continue with the buggy
-	 * behaviour where we issue STATUS on the current folder, for now. */
-	if (is->priv->select_folder == folder)
-		is_selected = TRUE;
-#endif
-	total = camel_folder_summary_count (folder->summary);
+	g_free (is->priv->status_data_items);
+	g_free (is->priv->list_return_opts);
 
-	if (uidvalidity > 0 && uidvalidity != isum->validity)
-		need_rescan = TRUE;
+	if (is->priv->search_results != NULL)
+		g_array_unref (is->priv->search_results);
+	g_mutex_clear (&is->priv->search_results_lock);
 
-	/* We don't have valid unread count or modseq for currently-selected server
-	 * (unless we want to re-SELECT it). We fake unread count when fetching
-	 * message flags, but don't depend on modseq for the selected folder */
-	if (total != messages ||
-	    isum->uidnext != uidnext ||
-	    camel_folder_summary_get_unread_count (folder->summary) != unseen ||
-	    (!is_selected && isum->modseq != highestmodseq))
-		need_rescan = TRUE;
-
-	/* This is probably the first check of this folder after startup;
-	 * use STATUS to check whether the cached summary is valid, rather
-	 * than blindly updating. Only for servers which support CONDSTORE
-	 * though. */
-	if (isum->modseq > 0 && highestmodseq == 0)
-		need_rescan = FALSE;
+	g_hash_table_destroy (is->priv->known_alerts);
+	g_mutex_clear (&is->priv->known_alerts_lock);
 
-	/* If we don't think there's anything to do, poke it to check */
-	if (!need_rescan) {
-		CamelIMAPXCommand *ic;
+	g_mutex_clear (&is->priv->idle_lock);
+	g_cond_clear (&is->priv->idle_cond);
 
-		#if 0  /* see comment for disabled bits above */
-		if (is_selected) {
-			/* We may not issue STATUS on the current folder. Use SELECT or NOOP instead. */
-			if (0 /* server needs SELECT not just NOOP */) {
-				if (imapx_in_idle (is))
-					if (!imapx_stop_idle (is, error))
-						goto done;
-				/* This doesn't work -- this is an immediate command, not queued */
-				imapx_maybe_select (is, folder)
-			} else {
-				/* Or maybe just NOOP, unless we're in IDLE in which case do nothing */
-				if (!imapx_in_idle (is)) {
-					if (!camel_imapx_server_noop (is, folder, cancellable, error))
-						goto done;
-				}
-			}
-		} else
-		#endif
-		{
-			ic = camel_imapx_command_new (
-				is, "STATUS", NULL, "STATUS %M (%t)",
-				mailbox, is->priv->status_data_items);
+	g_rec_mutex_clear (&is->priv->command_lock);
 
-			camel_imapx_command_set_job (ic, job);
-			ic->pri = job->pri;
+	g_weak_ref_clear (&is->priv->store);
+	g_weak_ref_clear (&is->priv->select_mailbox);
+	g_weak_ref_clear (&is->priv->select_pending);
+	g_clear_object (&is->priv->cancellable);
 
-			success = imapx_command_run_sync (
-				is, ic, cancellable, error);
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (camel_imapx_server_parent_class)->finalize (object);
+}
 
-			camel_imapx_command_unref (ic);
+static void
+imapx_server_constructed (GObject *object)
+{
+	CamelIMAPXServer *server;
 
-			if (!success) {
-				g_prefix_error (
-					error, "%s: ",
-					_("Error refreshing folder"));
-				goto done;
-			}
-		}
+	/* Chain up to parent's method. */
+	G_OBJECT_CLASS (camel_imapx_server_parent_class)->constructed (object);
 
-		/* Recalulate need_rescan */
+	server = CAMEL_IMAPX_SERVER (object);
+	server->priv->tagprefix = 'Z';
+}
 
-		messages = camel_imapx_mailbox_get_messages (mailbox);
-		unseen = camel_imapx_mailbox_get_unseen (mailbox);
-		uidnext = camel_imapx_mailbox_get_uidnext (mailbox);
-		highestmodseq = camel_imapx_mailbox_get_highestmodseq (mailbox);
+static void
+camel_imapx_server_class_init (CamelIMAPXServerClass *class)
+{
+	GObjectClass *object_class;
 
-		if (total != messages ||
-		    isum->uidnext != uidnext ||
-		    camel_folder_summary_get_unread_count (folder->summary) != unseen ||
-		    (!is_selected && isum->modseq != highestmodseq))
-			need_rescan = TRUE;
-	}
+	g_type_class_add_private (class, sizeof (CamelIMAPXServerPrivate));
 
-	messages = camel_imapx_mailbox_get_messages (mailbox);
-	unseen = camel_imapx_mailbox_get_unseen (mailbox);
-	uidnext = camel_imapx_mailbox_get_uidnext (mailbox);
-	uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox);
-	highestmodseq = camel_imapx_mailbox_get_highestmodseq (mailbox);
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = imapx_server_set_property;
+	object_class->get_property = imapx_server_get_property;
+	object_class->finalize = imapx_server_finalize;
+	object_class->dispose = imapx_server_dispose;
+	object_class->constructed = imapx_server_constructed;
 
-	if (is->use_qresync && isum->modseq > 0 && uidvalidity > 0)
-		can_qresync = TRUE;
+	g_object_class_install_property (
+		object_class,
+		PROP_STORE,
+		g_param_spec_object (
+			"store",
+			"Store",
+			"IMAPX store for this server",
+			CAMEL_TYPE_IMAPX_STORE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY |
+			G_PARAM_STATIC_STRINGS));
 
-	e (
-		is->tagprefix,
-		"folder %s is %sselected, "
-		"total %u / %u, unread %u / %u, modseq %"
-		G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT
-		", uidnext %u / %u: will %srescan\n",
-		full_name,
-		is_selected ? "" : "not ",
-		total,
-		messages,
-		camel_folder_summary_get_unread_count (folder->summary),
-		unseen,
-		isum->modseq,
-		highestmodseq,
-		isum->uidnext,
-		uidnext,
-		need_rescan ? "" : "not ");
-
-	/* Fetch new messages first, so that they appear to the user ASAP */
-	if (messages > total || uidnext > isum->uidnext) {
-		if (!total)
-			need_rescan = FALSE;
+	signals[REFRESH_MAILBOX] = g_signal_new (
+		"refresh-mailbox",
+		G_OBJECT_CLASS_TYPE (class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (CamelIMAPXServerClass, refresh_mailbox),
+		NULL, NULL, NULL,
+		G_TYPE_NONE, 1,
+		CAMEL_TYPE_IMAPX_MAILBOX);
+}
 
-		success = imapx_server_fetch_new_messages (
-			is, mailbox, FALSE, FALSE, cancellable, error);
-		if (!success)
-			goto done;
+static void
+camel_imapx_server_init (CamelIMAPXServer *is)
+{
+	is->priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is);
 
-		/* If QRESYNC-capable we'll have got all flags changes in SELECT */
-		if (can_qresync)
-			goto qresync_done;
-	}
+	is->priv->untagged_handlers = create_initial_untagged_handler_table ();
 
-	if (!need_rescan)
-		goto done;
+	g_mutex_init (&is->priv->stream_lock);
+	g_mutex_init (&is->priv->inactivity_timeout_lock);
+	g_mutex_init (&is->priv->select_lock);
+	g_mutex_init (&is->priv->changes_lock);
+	g_mutex_init (&is->priv->search_results_lock);
+	g_mutex_init (&is->priv->known_alerts_lock);
 
-	if (can_qresync) {
-		/* Actually we only want to select it; no need for the NOOP */
-		success = camel_imapx_server_noop (
-			is, mailbox, cancellable, error);
-		if (!success)
-			goto done;
-	qresync_done:
-		messages = camel_imapx_mailbox_get_messages (mailbox);
-		unseen = camel_imapx_mailbox_get_unseen (mailbox);
-		highestmodseq = camel_imapx_mailbox_get_highestmodseq (mailbox);
+	g_weak_ref_init (&is->priv->store, NULL);
+	g_weak_ref_init (&is->priv->select_mailbox, NULL);
+	g_weak_ref_init (&is->priv->select_pending, NULL);
 
-		isum->modseq = highestmodseq;
-		total = camel_folder_summary_count (folder->summary);
-		if (total != messages ||
-		    camel_folder_summary_get_unread_count (folder->summary) != unseen ||
-		    (isum->modseq != highestmodseq)) {
-			c (
-				is->tagprefix,
-				"Eep, after QRESYNC we're out of sync. "
-				"total %u / %u, unread %u / %u, modseq %"
-				G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT "\n",
-				total, messages,
-				camel_folder_summary_get_unread_count (folder->summary),
-				unseen,
-				isum->modseq,
-				highestmodseq);
-		} else {
-			c (
-				is->tagprefix,
-				"OK, after QRESYNC we're still in sync. "
-				"total %u / %u, unread %u / %u, modseq %"
-				G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT "\n",
-				total, messages,
-				camel_folder_summary_get_unread_count (folder->summary),
-				unseen,
-				isum->modseq,
-				highestmodseq);
-			goto done;
-		}
-	}
+	is->priv->cancellable = g_cancellable_new ();
 
-	g_object_unref (folder);
-	g_object_unref (mailbox);
+	is->priv->state = IMAPX_DISCONNECTED;
+	is->priv->is_cyrus = FALSE;
+	is->priv->copyuid_status = NULL;
 
-	return imapx_job_scan_changes_start (job, is, cancellable, error);
+	is->priv->changes = camel_folder_change_info_new ();
 
-done:
-	g_object_unref (folder);
-	g_object_unref (mailbox);
+	is->priv->known_alerts = g_hash_table_new_full (
+		(GHashFunc) g_str_hash,
+		(GEqualFunc) g_str_equal,
+		(GDestroyNotify) g_free,
+		(GDestroyNotify) NULL);
 
-	imapx_unregister_job (is, job);
+	/* Initialize IDLE members. */
+	g_mutex_init (&is->priv->idle_lock);
+	g_cond_init (&is->priv->idle_cond);
+	is->priv->idle_state = IMAPX_IDLE_STATE_OFF;
+	is->priv->idle_stamp = 0;
 
-	return success;
+	g_rec_mutex_init (&is->priv->command_lock);
 }
 
-static gboolean
-imapx_job_refresh_info_matches (CamelIMAPXJob *job,
-                                CamelIMAPXMailbox *mailbox,
-                                const gchar *uid)
+CamelIMAPXServer *
+camel_imapx_server_new (CamelIMAPXStore *store)
 {
-	return camel_imapx_job_has_mailbox (job, mailbox);
-}
+	g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), NULL);
 
-/* ********************************************************************** */
+	return g_object_new (
+		CAMEL_TYPE_IMAPX_SERVER,
+		"store", store, NULL);
+}
 
-static void
-imapx_command_expunge_done (CamelIMAPXServer *is,
-                            CamelIMAPXCommand *ic)
+CamelIMAPXStore *
+camel_imapx_server_ref_store (CamelIMAPXServer *server)
 {
-	CamelIMAPXJob *job;
-	CamelIMAPXMailbox *mailbox;
-	CamelFolder *folder;
-	GError *local_error = NULL;
-
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_if_fail (mailbox != NULL);
-
-	folder = imapx_server_ref_folder (is, mailbox);
-	g_return_if_fail (folder != NULL);
-
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error expunging message"));
-		camel_imapx_job_take_error (job, local_error);
-
-	} else {
-		GPtrArray *uids;
-		CamelStore *parent_store;
-		const gchar *full_name;
-
-		full_name = camel_folder_get_full_name (folder);
-		parent_store = camel_folder_get_parent_store (folder);
-
-		camel_folder_summary_lock (folder->summary);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), NULL);
 
-		camel_folder_summary_save_to_db (folder->summary, NULL);
-		uids = camel_db_get_folder_deleted_uids (parent_store->cdb_r, full_name, NULL);
+	return g_weak_ref_get (&server->priv->store);
+}
 
-		if (uids && uids->len) {
-			CamelFolderChangeInfo *changes;
-			GList *removed = NULL;
-			gint i;
+CamelIMAPXSettings *
+camel_imapx_server_ref_settings (CamelIMAPXServer *server)
+{
+	CamelIMAPXStore *store;
+	CamelSettings *settings;
 
-			changes = camel_folder_change_info_new ();
-			for (i = 0; i < uids->len; i++) {
-				gchar *uid = uids->pdata[i];
-				CamelMessageInfo *mi;
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), NULL);
 
-				mi = camel_folder_summary_peek_loaded (folder->summary, uid);
-				if (mi) {
-					camel_folder_summary_remove (folder->summary, mi);
-					camel_message_info_unref (mi);
-				} else {
-					camel_folder_summary_remove_uid (folder->summary, uid);
-				}
+	store = camel_imapx_server_ref_store (server);
+	settings = camel_service_ref_settings (CAMEL_SERVICE (store));
+	g_object_unref (store);
 
-				camel_folder_change_info_remove_uid (changes, uids->pdata[i]);
-				removed = g_list_prepend (removed, (gpointer) uids->pdata[i]);
-			}
+	return CAMEL_IMAPX_SETTINGS (settings);
+}
 
-			camel_folder_summary_save_to_db (folder->summary, NULL);
-			camel_folder_changed (folder, changes);
-			camel_folder_change_info_free (changes);
+/**
+ * camel_imapx_server_ref_input_stream:
+ * @is: a #CamelIMAPXServer
+ *
+ * Returns the #GInputStream for @is, which is owned by either a
+ * #GTcpConnection or a #GSubprocess.  If the #CamelIMAPXServer is not
+ * yet connected or has lost its connection, the function returns %NULL.
+ *
+ * The returned #GInputStream is referenced for thread-safety and must
+ * be unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: a #GInputStream, or %NULL
+ *
+ * Since: 3.12
+ **/
+GInputStream *
+camel_imapx_server_ref_input_stream (CamelIMAPXServer *is)
+{
+	GInputStream *input_stream = NULL;
 
-			g_list_free (removed);
-			g_ptr_array_foreach (uids, (GFunc) camel_pstring_free, NULL);
-		}
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
 
-		if (uids)
-			g_ptr_array_free (uids, TRUE);
+	g_mutex_lock (&is->priv->stream_lock);
 
-		camel_folder_summary_unlock (folder->summary);
-	}
+	if (is->priv->input_stream != NULL)
+		input_stream = g_object_ref (is->priv->input_stream);
 
-	g_object_unref (folder);
-	g_object_unref (mailbox);
+	g_mutex_unlock (&is->priv->stream_lock);
 
-	imapx_unregister_job (is, job);
+	return input_stream;
 }
 
-static gboolean
-imapx_job_expunge_start (CamelIMAPXJob *job,
-                         CamelIMAPXServer *is,
-                         GCancellable *cancellable,
-                         GError **error)
+/**
+ * camel_imapx_server_ref_output_stream:
+ * @is: a #CamelIMAPXServer
+ *
+ * Returns the #GOutputStream for @is, which is owned by either a
+ * #GTcpConnection or a #GSubprocess.  If the #CamelIMAPXServer is not
+ * yet connected or has lost its connection, the function returns %NULL.
+ *
+ * The returned #GOutputStream is referenced for thread-safety and must
+ * be unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: a #GOutputStream, or %NULL
+ *
+ * Since: 3.12
+ **/
+GOutputStream *
+camel_imapx_server_ref_output_stream (CamelIMAPXServer *is)
 {
-	CamelIMAPXCommand *ic;
-	CamelIMAPXMailbox *mailbox;
-	gboolean success;
-
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_val_if_fail (mailbox != NULL, FALSE);
-
-	success = imapx_server_sync_changes (
-		is, mailbox, job->type, job->pri, cancellable, error);
+	GOutputStream *output_stream = NULL;
 
-	if (success) {
-		/* TODO handle UIDPLUS capability */
-		ic = camel_imapx_command_new (
-			is, "EXPUNGE", mailbox, "EXPUNGE");
-		camel_imapx_command_set_job (ic, job);
-		ic->pri = job->pri;
-		ic->complete = imapx_command_expunge_done;
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
 
-		imapx_command_queue (is, ic);
+	g_mutex_lock (&is->priv->stream_lock);
 
-		camel_imapx_command_unref (ic);
-	}
+	if (is->priv->output_stream != NULL)
+		output_stream = g_object_ref (is->priv->output_stream);
 
-	g_object_unref (mailbox);
+	g_mutex_unlock (&is->priv->stream_lock);
 
-	return success;
+	return output_stream;
 }
 
-static gboolean
-imapx_job_expunge_matches (CamelIMAPXJob *job,
-                           CamelIMAPXMailbox *mailbox,
-                           const gchar *uid)
+/**
+ * camel_imapx_server_ref_selected:
+ * @is: a #CamelIMAPXServer
+ *
+ * Returns the #CamelIMAPXMailbox representing the currently selected
+ * mailbox (or mailbox <emphasis>being</emphasis> selected if a SELECT
+ * command is in progress) on the IMAP server, or %NULL if no mailbox
+ * is currently selected or being selected on the server.
+ *
+ * The returned #CamelIMAPXMailbox is reference for thread-safety and
+ * should be unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: a #CamelIMAPXMailbox, or %NULL
+ *
+ * Since: 3.12
+ **/
+CamelIMAPXMailbox *
+camel_imapx_server_ref_selected (CamelIMAPXServer *is)
 {
-	return camel_imapx_job_has_mailbox (job, mailbox);
-}
+	CamelIMAPXMailbox *mailbox;
 
-/* ********************************************************************** */
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
 
-static void
-imapx_command_list_done (CamelIMAPXServer *is,
-                         CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	GError *local_error = NULL;
+	g_mutex_lock (&is->priv->select_lock);
 
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+	mailbox = g_weak_ref_get (&is->priv->select_mailbox);
+	if (mailbox == NULL)
+		mailbox = g_weak_ref_get (&is->priv->select_pending);
 
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error fetching folders"));
-		camel_imapx_job_take_error (job, local_error);
-	}
+	g_mutex_unlock (&is->priv->select_lock);
 
-	e (is->tagprefix, "==== list or lsub completed ==== \n");
-	imapx_unregister_job (is, job);
+	return mailbox;
 }
 
-static void
-imapx_command_list_lsub (CamelIMAPXServer *is,
-                         CamelIMAPXCommand *ic)
+/* Some untagged responses updated pending SELECT mailbox, not the currently
+   selected or closing one, thus use this function instead. */
+CamelIMAPXMailbox *
+camel_imapx_server_ref_pending_or_selected (CamelIMAPXServer *is)
 {
-	CamelIMAPXJob *job;
-	ListData *data;
-	GError *local_error = NULL;
-
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+	CamelIMAPXMailbox *mailbox;
 
-	data = camel_imapx_job_get_data (job);
-	g_return_if_fail (data != NULL);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
 
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error fetching folders"));
-		camel_imapx_job_take_error (job, local_error);
-		imapx_unregister_job (is, job);
+	g_mutex_lock (&is->priv->select_lock);
 
-	} else {
-		ic = camel_imapx_command_new (
-			is, "LIST", NULL,
-			"LSUB \"\" %s",
-			data->pattern);
-
-		ic->pri = job->pri;
-		camel_imapx_command_set_job (ic, job);
-		ic->complete = imapx_command_list_done;
+	mailbox = g_weak_ref_get (&is->priv->select_pending);
+	if (mailbox == NULL)
+		mailbox = g_weak_ref_get (&is->priv->select_mailbox);
 
-		imapx_command_queue (is, ic);
+	g_mutex_unlock (&is->priv->select_lock);
 
-		camel_imapx_command_unref (ic);
-	}
+	return mailbox;
 }
 
-static gboolean
-imapx_job_list_start (CamelIMAPXJob *job,
-                      CamelIMAPXServer *is,
-                      GCancellable *cancellable,
-                      GError **error)
+gboolean
+camel_imapx_server_mailbox_selected (CamelIMAPXServer *is,
+				     CamelIMAPXMailbox *mailbox)
 {
-	CamelIMAPXCommand *ic;
-	ListData *data;
-
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
-
-	if (is->priv->list_return_opts != NULL) {
-		ic = camel_imapx_command_new (
-			is, "LIST", NULL,
-			"LIST \"\" %s RETURN (%t)",
-			data->pattern,
-			is->priv->list_return_opts);
-		ic->complete = imapx_command_list_done;
-	} else {
-		ic = camel_imapx_command_new (
-			is, "LIST", NULL,
-			"LIST \"\" %s",
-			data->pattern);
-		ic->complete = imapx_command_list_lsub;
-	}
-
-	ic->pri = job->pri;
-	camel_imapx_command_set_job (ic, job);
-
-	imapx_command_queue (is, ic);
+	CamelIMAPXMailbox *selected_mailbox;
+	gboolean res;
 
-	camel_imapx_command_unref (ic);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
 
-	return TRUE;
-}
+	g_mutex_lock (&is->priv->select_lock);
+	selected_mailbox = g_weak_ref_get (&is->priv->select_mailbox);
+	res = selected_mailbox == mailbox;
+	g_clear_object (&selected_mailbox);
+	g_mutex_unlock (&is->priv->select_lock);
 
-static gboolean
-imapx_job_list_matches (CamelIMAPXJob *job,
-                        CamelIMAPXMailbox *mailbox,
-                        const gchar *uid)
-{
-	return TRUE;  /* matches everything */
+	return res;
 }
 
-/* ********************************************************************** */
-
-static void
-imapx_command_create_mailbox_done (CamelIMAPXServer *is,
-                                   CamelIMAPXCommand *ic)
+gboolean
+camel_imapx_server_ensure_selected_sync (CamelIMAPXServer *is,
+					 CamelIMAPXMailbox *mailbox,
+					 GCancellable *cancellable,
+					 GError **error)
 {
-	CamelIMAPXJob *job;
-	GError *local_error = NULL;
+	CamelIMAPXCommand *ic;
+	CamelIMAPXMailbox *selected_mailbox;
+	gboolean success;
 
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
 
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error creating folder"));
-		camel_imapx_job_take_error (job, local_error);
-	}
+	if (g_cancellable_set_error_if_cancelled (cancellable, error))
+		return FALSE;
 
-	imapx_unregister_job (is, job);
-}
+	g_mutex_lock (&is->priv->select_lock);
+	selected_mailbox = g_weak_ref_get (&is->priv->select_mailbox);
+	if (selected_mailbox == mailbox) {
+		gboolean request_noop;
+		gint change_stamp;
 
-static gboolean
-imapx_job_create_mailbox_start (CamelIMAPXJob *job,
-                                CamelIMAPXServer *is,
-                                GCancellable *cancellable,
-                                GError **error)
-{
-	CamelIMAPXCommand *ic;
-	MailboxData *data;
+		change_stamp = selected_mailbox ? camel_imapx_mailbox_get_change_stamp (selected_mailbox) : 0;
+		request_noop = selected_mailbox && is->priv->last_selected_mailbox_change_stamp != change_stamp;
 
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
+		if (request_noop)
+			is->priv->last_selected_mailbox_change_stamp = change_stamp;
 
-	ic = camel_imapx_command_new (
-		is, "CREATE", NULL, "CREATE %m",
-		data->mailbox_name);
-	ic->pri = job->pri;
-	camel_imapx_command_set_job (ic, job);
-	ic->complete = imapx_command_create_mailbox_done;
+		g_mutex_unlock (&is->priv->select_lock);
+		g_clear_object (&selected_mailbox);
 
-	imapx_command_queue (is, ic);
+		if (request_noop) {
+			c (is->priv->tagprefix, "%s: Selected mailbox '%s' changed, do NOOP instead\n", G_STRFUNC, camel_imapx_mailbox_get_name (mailbox));
 
-	camel_imapx_command_unref (ic);
+			return camel_imapx_server_noop_sync (is, mailbox, cancellable, error);
+		}
 
-	return TRUE;
-}
+		return TRUE;
+	}
 
-/* ********************************************************************** */
+	g_clear_object (&selected_mailbox);
 
-static void
-imapx_command_delete_mailbox_done (CamelIMAPXServer *is,
-                                   CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	MailboxData *data;
-	GError *local_error = NULL;
+	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_SELECT, "SELECT %M", mailbox);
 
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+	if (is->priv->use_qresync) {
+		CamelFolder *folder;
 
-	data = camel_imapx_job_get_data (job);
-	g_return_if_fail (data != NULL);
+		folder = imapx_server_ref_folder (is, mailbox);
+		camel_imapx_command_add_qresync_parameter (ic, folder);
+		g_clear_object (&folder);
+	}
 
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error deleting folder"));
-		camel_imapx_job_take_error (job, local_error);
+	g_weak_ref_set (&is->priv->select_pending, mailbox);
+	g_mutex_unlock (&is->priv->select_lock);
 
-	} else {
-		CamelIMAPXStore *imapx_store;
+	success = camel_imapx_server_process_command_sync (is, ic, _("Failed to select mailbox"), cancellable, error);
 
-		/* Perform the same processing as imapx_untagged_list()
-		 * would if the server notified us of a deleted mailbox. */
+	camel_imapx_command_unref (ic);
 
-		imapx_store = camel_imapx_server_ref_store (is);
+	g_mutex_lock (&is->priv->select_lock);
 
-		camel_imapx_mailbox_deleted (data->mailbox);
-		camel_imapx_store_emit_mailbox_updated (imapx_store, data->mailbox);
+	g_weak_ref_set (&is->priv->select_pending, NULL);
 
-		g_clear_object (&imapx_store);
+	if (success) {
+		is->priv->state = IMAPX_SELECTED;
+		is->priv->last_selected_mailbox_change_stamp = camel_imapx_mailbox_get_change_stamp (mailbox);
+		g_weak_ref_set (&is->priv->select_mailbox, mailbox);
+	} else {
+		is->priv->state = IMAPX_INITIALISED;
+		is->priv->last_selected_mailbox_change_stamp = 0;
+		g_weak_ref_set (&is->priv->select_mailbox, NULL);
 	}
 
-	imapx_unregister_job (is, job);
+	g_mutex_unlock (&is->priv->select_lock);
+
+	return success;
 }
 
-static gboolean
-imapx_job_delete_mailbox_start (CamelIMAPXJob *job,
-                                CamelIMAPXServer *is,
-                                GCancellable *cancellable,
-                                GError **error)
+gboolean
+camel_imapx_server_process_command_sync (CamelIMAPXServer *is,
+					 CamelIMAPXCommand *ic,
+					 const gchar *error_prefix,
+					 GCancellable *cancellable,
+					 GError **error)
 {
-	CamelIMAPXStore *imapx_store;
-	CamelIMAPXCommand *ic;
-	MailboxData *data;
-	CamelIMAPXMailbox *inbox;
-
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
-
-	imapx_store = camel_imapx_server_ref_store (is);
-	/* Keep going, even if this returns NULL. */
-	inbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");
-	g_clear_object (&imapx_store);
+	CamelIMAPXCommandPart *cp;
+	GInputStream *input_stream = NULL;
+	GOutputStream *output_stream = NULL;
+	gboolean cp_literal_plus;
+	GList *head;
+	gchar *string;
+	gboolean success = FALSE;
+	GError *local_error = NULL;
 
-	/* Make sure the to-be-deleted folder is not
-	 * selected by selecting INBOX for this operation. */
-	ic = camel_imapx_command_new (
-		is, "DELETE", inbox,
-		"DELETE %M", data->mailbox);
-	ic->pri = job->pri;
-	camel_imapx_command_set_job (ic, job);
-	ic->complete = imapx_command_delete_mailbox_done;
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic), FALSE);
 
-	imapx_command_queue (is, ic);
+	camel_imapx_command_close (ic);
+	if (ic->status) {
+		imapx_free_status (ic->status);
+		ic->status = NULL;
+	}
+	ic->completed = FALSE;
 
-	camel_imapx_command_unref (ic);
+	head = g_queue_peek_head_link (&ic->parts);
+	g_return_val_if_fail (head != NULL, FALSE);
+	cp = (CamelIMAPXCommandPart *) head->data;
+	ic->current_part = head;
 
-	g_clear_object (&inbox);
+	if (g_cancellable_set_error_if_cancelled (cancellable, &local_error)) {
+		if (error_prefix && local_error)
+			g_prefix_error (&local_error, "%s: ", error_prefix);
 
-	return TRUE;
-}
+		if (local_error)
+			g_propagate_error (error, local_error);
 
-/* ********************************************************************** */
+		return FALSE;
+	}
 
-static void
-imapx_command_rename_mailbox_done (CamelIMAPXServer *is,
-                                   CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	MailboxData *data;
-	GError *local_error = NULL;
+	cp_literal_plus = ((cp->type & CAMEL_IMAPX_COMMAND_LITERAL_PLUS) != 0);
 
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+	COMMAND_LOCK (is);
 
-	data = camel_imapx_job_get_data (job);
-	g_return_if_fail (data != NULL);
+	if (is->priv->current_command != NULL) {
+		g_warning ("%s: [%c] %p: Starting command %p (%s) while still processing %p (%s)", G_STRFUNC,
+			is->priv->tagprefix, is, ic, camel_imapx_job_get_kind_name (ic->job_kind),
+			is->priv->current_command, camel_imapx_job_get_kind_name (is->priv->current_command->job_kind));
+	}
 
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error renaming folder"));
-		camel_imapx_job_take_error (job, local_error);
+	if (g_cancellable_set_error_if_cancelled (cancellable, &local_error)) {
+		c (is->priv->tagprefix, "%s: command %p (%s) cancelled\n", G_STRFUNC, ic, camel_imapx_job_get_kind_name (ic->job_kind));
 
-	} else {
-		CamelIMAPXStore *imapx_store;
+		COMMAND_UNLOCK (is);
 
-		/* Perform the same processing as imapx_untagged_list()
-		 * would if the server notified us of a renamed mailbox. */
+		if (error_prefix && local_error)
+			g_prefix_error (&local_error, "%s: ", error_prefix);
 
-		imapx_store = camel_imapx_server_ref_store (is);
-		camel_imapx_store_handle_mailbox_rename (imapx_store, data->mailbox, data->mailbox_name);
+		if (local_error)
+			g_propagate_error (error, local_error);
 
-		g_clear_object (&imapx_store);
+		return FALSE;
 	}
 
-	imapx_unregister_job (is, job);
-}
+	c (is->priv->tagprefix, "%s: %p (%s) ~> %p (%s)\n", G_STRFUNC, is->priv->current_command,
+		is->priv->current_command ? camel_imapx_job_get_kind_name (is->priv->current_command->job_kind) : "",
+		ic, camel_imapx_job_get_kind_name (ic->job_kind));
 
-static gboolean
-imapx_job_rename_mailbox_start (CamelIMAPXJob *job,
-                                CamelIMAPXServer *is,
-                                GCancellable *cancellable,
-                                GError **error)
-{
-	CamelIMAPXCommand *ic;
-	CamelIMAPXStore *imapx_store;
-	CamelIMAPXMailbox *inbox;
-	MailboxData *data;
+	is->priv->current_command = ic;
+	is->priv->continuation_command = ic;
 
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
+	COMMAND_UNLOCK (is);
 
-	imapx_store = camel_imapx_server_ref_store (is);
-	inbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");
-	g_clear_object (&imapx_store);
-	g_return_val_if_fail (inbox != NULL, FALSE);
+	input_stream = camel_imapx_server_ref_input_stream (is);
+	output_stream = camel_imapx_server_ref_output_stream (is);
 
-	camel_imapx_job_set_mailbox (job, inbox);
+	if (output_stream == NULL) {
+		local_error = g_error_new_literal (
+			CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT,
+			_("Cannot issue command, no stream available"));
+		goto exit;
+	}
+
+	c (
+		is->priv->tagprefix,
+		"Starting command (%s) %c%05u %s\r\n",
+		is->priv->current_command ? " literal" : "",
+		is->priv->tagprefix,
+		ic->tag,
+		cp->data && g_str_has_prefix (cp->data, "LOGIN") ?
+			"LOGIN..." : cp->data);
 
-	ic = camel_imapx_command_new (
-		is, "RENAME", inbox, "RENAME %M %m",
-		data->mailbox, data->mailbox_name);
-	ic->pri = job->pri;
-	camel_imapx_command_set_job (ic, job);
-	ic->complete = imapx_command_rename_mailbox_done;
+	if (ic->job_kind == CAMEL_IMAPX_JOB_DONE)
+		string = g_strdup_printf ("%s\r\n", cp->data);
+	else
+		string = g_strdup_printf ("%c%05u %s\r\n", is->priv->tagprefix, ic->tag, cp->data);
+	g_mutex_lock (&is->priv->stream_lock);
+	success = g_output_stream_write_all (
+		output_stream, string, strlen (string),
+		NULL, cancellable, &local_error);
+	g_mutex_unlock (&is->priv->stream_lock);
+	g_free (string);
 
-	imapx_command_queue (is, ic);
+	if (local_error != NULL || !success)
+		goto exit;
 
-	camel_imapx_command_unref (ic);
+	while (is->priv->continuation_command == ic && cp_literal_plus) {
+		/* Sent LITERAL+ continuation immediately */
+		imapx_continuation (
+			is, input_stream, output_stream,
+			TRUE, cancellable, &local_error);
+		if (local_error != NULL)
+			goto exit;
+	}
 
-	g_object_unref (inbox);
+	while (success && !ic->completed)
+		success = imapx_step (is, input_stream, output_stream, cancellable, &local_error);
 
-	return TRUE;
-}
+	imapx_server_reset_inactivity_timer (is);
 
-/* ********************************************************************** */
+ exit:
 
-static void
-imapx_command_subscribe_mailbox_done (CamelIMAPXServer *is,
-                                      CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	MailboxData *data;
-	GError *local_error = NULL;
+	COMMAND_LOCK (is);
 
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+	if (is->priv->current_command == ic) {
+		c (is->priv->tagprefix, "%s: %p ~> %p; success:%d local-error:%s result:%s status-text:'%s'\n", G_STRFUNC,
+			is->priv->current_command, NULL, success, local_error ? local_error->message : "[null]",
+			ic->status ? (
+				ic->status->result == IMAPX_OK ? "OK" :
+				ic->status->result == IMAPX_NO ? "NO" :
+				ic->status->result == IMAPX_BAD ? "BAD" :
+				ic->status->result == IMAPX_PREAUTH ? "PREAUTH" :
+				ic->status->result == IMAPX_BYE ? "BYE" : "???") : "[null]",
+			ic->status ? ic->status->text : "[null]");
 
-	data = camel_imapx_job_get_data (job);
-	g_return_if_fail (data != NULL);
+		is->priv->current_command = NULL;
+		is->priv->continuation_command = NULL;
+	} else {
+		c (is->priv->tagprefix, "%s: current command:%p doesn't match passed-in command:%p success:%d local-error:%s result:%s status-text:'%s'\n", G_STRFUNC,
+			is->priv->current_command, ic, success, local_error ? local_error->message : "[null]",
+			ic->status ? (
+				ic->status->result == IMAPX_OK ? "OK" :
+				ic->status->result == IMAPX_NO ? "NO" :
+				ic->status->result == IMAPX_BAD ? "BAD" :
+				ic->status->result == IMAPX_PREAUTH ? "PREAUTH" :
+				ic->status->result == IMAPX_BYE ? "BYE" : "???") : "[null]",
+			ic->status ? ic->status->text : "[null]");
+	}
 
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error subscribing to folder"));
-		camel_imapx_job_take_error (job, local_error);
+	COMMAND_UNLOCK (is);
 
-	} else {
-		CamelIMAPXStore *imapx_store;
+	/* Server reported error. */
+	if (success && ic->status && ic->status->result != IMAPX_OK) {
+		g_set_error (
+			&local_error, CAMEL_ERROR,
+			CAMEL_ERROR_GENERIC,
+			"%s", ic->status->text);
+	}
 
-		/* Perform the same processing as imapx_untagged_list()
-		 * would if the server notified us of a subscription. */
+	if (local_error) {
+		/* Sadly, G_IO_ERROR_FAILED is also used for 'Connection reset by peer' error;
+		   since GLib 2.44 is used G_IO_ERROR_CONNECTION_CLOSED, which is the same as G_IO_ERROR_BROKEN_PIPE */
+		if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
+		    g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE) ||
+		    g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) {
+			local_error->domain = CAMEL_IMAPX_SERVER_ERROR;
+			local_error->code = CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT;
+		}
 
-		imapx_store = camel_imapx_server_ref_store (is);
+		if (error_prefix && local_error)
+			g_prefix_error (&local_error, "%s: ", error_prefix);
 
-		camel_imapx_mailbox_subscribed (data->mailbox);
-		camel_imapx_store_emit_mailbox_updated (imapx_store, data->mailbox);
+		g_propagate_error (error, local_error);
 
-		g_clear_object (&imapx_store);
+		success = FALSE;
 	}
 
-	imapx_unregister_job (is, job);
+	g_clear_object (&input_stream);
+	g_clear_object (&output_stream);
+
+	return success;
 }
 
-static gboolean
-imapx_job_subscribe_mailbox_start (CamelIMAPXJob *job,
-                                   CamelIMAPXServer *is,
-                                   GCancellable *cancellable,
-                                   GError **error)
+static void
+imapx_disconnect (CamelIMAPXServer *is)
 {
-	CamelIMAPXCommand *ic;
-	MailboxData *data;
+	g_cancellable_cancel (is->priv->cancellable);
 
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
+	g_mutex_lock (&is->priv->stream_lock);
 
-	ic = camel_imapx_command_new (
-		is, "SUBSCRIBE", NULL,
-		"SUBSCRIBE %M", data->mailbox);
+	if (is->priv->connection) {
+		/* No need to wait for close for too long */
+		imapx_server_set_connection_timeout (is->priv->connection, 3);
+	}
 
-	ic->pri = job->pri;
-	camel_imapx_command_set_job (ic, job);
-	ic->complete = imapx_command_subscribe_mailbox_done;
+	g_clear_object (&is->priv->input_stream);
+	g_clear_object (&is->priv->output_stream);
+	g_clear_object (&is->priv->connection);
+	g_clear_object (&is->priv->subprocess);
 
-	imapx_command_queue (is, ic);
+	if (is->priv->cinfo) {
+		imapx_free_capability (is->priv->cinfo);
+		is->priv->cinfo = NULL;
+	}
 
-	camel_imapx_command_unref (ic);
+	g_mutex_unlock (&is->priv->stream_lock);
 
-	return TRUE;
-}
+	g_mutex_lock (&is->priv->select_lock);
+	is->priv->last_selected_mailbox_change_stamp = 0;
+	g_weak_ref_set (&is->priv->select_mailbox, NULL);
+	g_weak_ref_set (&is->priv->select_pending, NULL);
+	g_mutex_unlock (&is->priv->select_lock);
 
-/* ********************************************************************** */
+	is->priv->is_cyrus = FALSE;
+	is->priv->state = IMAPX_DISCONNECTED;
 
-static void
-imapx_command_unsubscribe_mailbox_done (CamelIMAPXServer *is,
-                                        CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	MailboxData *data;
-	GError *local_error = NULL;
+	g_mutex_lock (&is->priv->idle_lock);
+	is->priv->idle_state = IMAPX_IDLE_STATE_OFF;
+	g_cond_broadcast (&is->priv->idle_cond);
+	g_mutex_unlock (&is->priv->idle_lock);
+}
 
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+/* Client commands */
+gboolean
+camel_imapx_server_connect_sync (CamelIMAPXServer *is,
+				 GCancellable *cancellable,
+				 GError **error)
+{
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
 
-	data = camel_imapx_job_get_data (job);
-	g_return_if_fail (data != NULL);
+	if (is->priv->state == IMAPX_SHUTDOWN) {
+		g_set_error (
+			error, CAMEL_SERVICE_ERROR,
+			CAMEL_SERVICE_ERROR_UNAVAILABLE,
+			"Shutting down");
+		return FALSE;
+	}
 
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error unsubscribing from folder"));
-		camel_imapx_job_take_error (job, local_error);
+	if (is->priv->state >= IMAPX_INITIALISED)
+		return TRUE;
 
-	} else {
-		CamelIMAPXStore *imapx_store;
+	is->priv->is_cyrus = FALSE;
 
-		/* Perform the same processing as imapx_untagged_list()
-		 * would if the server notified us of an unsubscription. */
+	if (!imapx_reconnect (is, cancellable, error))
+		return FALSE;
 
-		imapx_store = camel_imapx_server_ref_store (is);
+	g_mutex_lock (&is->priv->stream_lock);
 
-		camel_imapx_mailbox_unsubscribed (data->mailbox);
-		camel_imapx_store_emit_mailbox_updated (imapx_store, data->mailbox);
+	if (CAMEL_IMAPX_LACK_CAPABILITY (is->priv->cinfo, NAMESPACE)) {
+		g_mutex_unlock (&is->priv->stream_lock);
 
-		g_clear_object (&imapx_store);
+		/* This also creates a needed faux NAMESPACE */
+		if (!camel_imapx_server_list_sync (is, "INBOX", 0, cancellable, error))
+			return FALSE;
+	} else {
+		g_mutex_unlock (&is->priv->stream_lock);
 	}
 
-	imapx_unregister_job (is, job);
+	return TRUE;
 }
 
-static gboolean
-imapx_job_unsubscribe_mailbox_start (CamelIMAPXJob *job,
-                                     CamelIMAPXServer *is,
-                                     GCancellable *cancellable,
-                                     GError **error)
+gboolean
+camel_imapx_server_disconnect_sync (CamelIMAPXServer *is,
+				    GCancellable *cancellable,
+				    GError **error)
 {
-	CamelIMAPXCommand *ic;
-	MailboxData *data;
+	GCancellable *idle_cancellable;
+	gboolean success = TRUE;
 
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
 
-	ic = camel_imapx_command_new (
-		is, "UNSUBSCRIBE", NULL,
-		"UNSUBSCRIBE %M", data->mailbox);
+	g_mutex_lock (&is->priv->idle_lock);
+	idle_cancellable = is->priv->idle_cancellable;
+	if (idle_cancellable)
+		g_object_ref (idle_cancellable);
+	g_mutex_unlock (&is->priv->idle_lock);
+
+	if (idle_cancellable)
+		g_cancellable_cancel (idle_cancellable);
+	g_clear_object (&idle_cancellable);
 
-	ic->pri = job->pri;
-	camel_imapx_command_set_job (ic, job);
-	ic->complete = imapx_command_unsubscribe_mailbox_done;
+	g_mutex_lock (&is->priv->stream_lock);
+	if (is->priv->connection) {
+		/* No need to wait for close for too long */
+		imapx_server_set_connection_timeout (is->priv->connection, 3);
+	}
+	g_mutex_unlock (&is->priv->stream_lock);
 
-	imapx_command_queue (is, ic);
+	/* Ignore errors here. */
+	camel_imapx_server_stop_idle_sync (is, cancellable, NULL);
 
-	camel_imapx_command_unref (ic);
+	g_mutex_lock (&is->priv->stream_lock);
+	if (is->priv->connection)
+		success = g_io_stream_close (is->priv->connection, cancellable, error);
+	g_mutex_unlock (&is->priv->stream_lock);
 
-	return TRUE;
-}
+	imapx_disconnect (is);
 
-/* ********************************************************************** */
+	return success;
+}
 
-static void
-imapx_command_update_quota_info_done (CamelIMAPXServer *is,
-                                      CamelIMAPXCommand *ic)
+gboolean
+camel_imapx_server_query_auth_types_sync (CamelIMAPXServer *is,
+					  GCancellable *cancellable,
+					  GError **error)
 {
-	CamelIMAPXJob *job;
-	GError *local_error = NULL;
-
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
-
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error retrieving quota information"));
-		camel_imapx_job_take_error (job, local_error);
-	}
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
 
-	imapx_unregister_job (is, job);
+	return imapx_connect_to_server (is, cancellable, error);
 }
 
-static gboolean
-imapx_job_update_quota_info_start (CamelIMAPXJob *job,
-                                   CamelIMAPXServer *is,
-                                   GCancellable *cancellable,
-                                   GError **error)
+CamelStream *
+camel_imapx_server_get_message_sync (CamelIMAPXServer *is,
+				     CamelIMAPXMailbox *mailbox,
+				     CamelFolderSummary *summary,
+				     CamelDataCache *message_cache,
+				     const gchar *message_uid,
+				     GCancellable *cancellable,
+				     GError **error)
 {
-	CamelIMAPXCommand *ic;
-	CamelIMAPXMailbox *mailbox;
+	CamelMessageInfo *mi;
+	CamelStream *result_stream = NULL;
+	CamelIMAPXSettings *settings;
+	GIOStream *cache_stream;
+	gsize data_size;
+	gboolean use_multi_fetch;
+	gboolean success, retrying = FALSE;
+	GError *local_error = NULL;
 
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_val_if_fail (mailbox != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), NULL);
+	g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
+	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), NULL);
+	g_return_val_if_fail (message_uid != NULL, NULL);
 
-	ic = camel_imapx_command_new (
-		is, "GETQUOTAROOT", NULL,
-		"GETQUOTAROOT %M", mailbox);
-	ic->pri = job->pri;
-	camel_imapx_command_set_job (ic, job);
-	ic->complete = imapx_command_update_quota_info_done;
+	if (!camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error))
+		return NULL;
 
-	imapx_command_queue (is, ic);
+	mi = camel_folder_summary_get (summary, message_uid);
+	if (mi == NULL) {
+		g_set_error (
+			error, CAMEL_FOLDER_ERROR,
+			CAMEL_FOLDER_ERROR_INVALID_UID,
+			_("Cannot get message with message ID %s: %s"),
+			message_uid, _("No such message available."));
+		return NULL;
+	}
 
-	camel_imapx_command_unref (ic);
+	/* This makes sure that if any file is left on the disk, it is not reused.
+	   That can happen when the previous message download had been cancelled
+	   or finished with an error. */
+	camel_data_cache_remove (message_cache, "tmp", message_uid, NULL);
 
-	g_clear_object (&mailbox);
+	cache_stream = camel_data_cache_add (message_cache, "tmp", message_uid, error);
+	if (cache_stream == NULL) {
+		camel_message_info_unref (mi);
+		return NULL;
+	}
 
-	return TRUE;
-}
+	settings = camel_imapx_server_ref_settings (is);
+	data_size = ((CamelMessageInfoBase *) mi)->size;
+	use_multi_fetch = data_size > MULTI_SIZE && camel_imapx_settings_get_use_multi_fetch (settings);
+	g_object_unref (settings);
 
-/* ********************************************************************** */
+	g_warn_if_fail (is->priv->get_message_stream == NULL);
 
-static void
-imapx_command_uid_search_done (CamelIMAPXServer *is,
-                               CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	SearchData *data;
-	GError *local_error = NULL;
+	is->priv->get_message_stream = cache_stream;
 
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+ try_again:
+	if (use_multi_fetch) {
+		CamelIMAPXCommand *ic;
+		gsize fetch_offset = 0;
 
-	data = camel_imapx_job_get_data (job);
-	g_return_if_fail (data != NULL);
+		do {
+			camel_operation_progress (cancellable, fetch_offset * 100 / data_size);
 
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (&local_error, "%s: ", _("Search failed"));
-		camel_imapx_job_take_error (job, local_error);
-	}
+			ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_GET_MESSAGE, "UID FETCH %t (BODY.PEEK[]", message_uid);
+			camel_imapx_command_add (ic, "<%u.%u>", fetch_offset, MULTI_SIZE);
+			camel_imapx_command_add (ic, ")");
+			fetch_offset += MULTI_SIZE;
 
-	/* Don't worry about the success state and presence of search
-	 * results not agreeing here.  camel_imapx_server_uid_search()
-	 * will disregard the search results if an error occurred. */
-	g_mutex_lock (&is->priv->search_results_lock);
-	data->results = is->priv->search_results;
-	is->priv->search_results = NULL;
-	g_mutex_unlock (&is->priv->search_results_lock);
+			success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching message"), cancellable, &local_error);
 
-	imapx_unregister_job (is, job);
-}
+			camel_imapx_command_unref (ic);
+			ic = NULL;
 
-static gboolean
-imapx_job_uid_search_start (CamelIMAPXJob *job,
-                            CamelIMAPXServer *is,
-                            GCancellable *cancellable,
-                            GError **error)
-{
-	CamelIMAPXCommand *ic;
-	CamelIMAPXMailbox *mailbox;
-	SearchData *data;
+			if (success) {
+				gsize really_fetched = g_seekable_tell (G_SEEKABLE (is->priv->get_message_stream));
 
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
+				/* Don't automatically stop when we reach the reported message
+				 * size -- some crappy servers (like Microsoft Exchange) have
+				 * a tendency to lie about it. Keep going (one request at a
+				 * time) until the data actually stop coming. */
+				if (fetch_offset < data_size ||
+				    fetch_offset == really_fetched) {
+					/* just continue */
+				} else {
+					break;
+				}
+			}
+		} while (success);
+	} else {
+		CamelIMAPXCommand *ic;
 
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_val_if_fail (mailbox != NULL, FALSE);
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_GET_MESSAGE, "UID FETCH %t (BODY.PEEK[])", message_uid);
 
-	ic = camel_imapx_command_new (
-		is, "UID SEARCH", mailbox,
-		"UID SEARCH %t", data->criteria);
-	ic->pri = job->pri;
-	camel_imapx_command_set_job (ic, job);
-	ic->complete = imapx_command_uid_search_done;
+		success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching message"), cancellable, &local_error);
 
-	imapx_command_queue (is, ic);
+		camel_imapx_command_unref (ic);
+	}
 
-	camel_imapx_command_unref (ic);
+	if (success && !retrying && !g_seekable_tell (G_SEEKABLE (is->priv->get_message_stream))) {
+		/* Nothing had been read from the server. Maybe this connection
+		   doesn't know about the message on the server side yet, thus
+		   invoke NOOP and retry. */
+		CamelIMAPXCommand *ic;
 
-	g_object_unref (mailbox);
+		retrying = TRUE;
 
-	return TRUE;
-}
+		c (is->priv->tagprefix, "%s: Returned no message data, retrying after NOOP\n", G_STRFUNC);
 
-/* ********************************************************************** */
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NOOP, "NOOP");
 
-static void
-imapx_command_noop_done (CamelIMAPXServer *is,
-                         CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	GError *local_error = NULL;
+		success = camel_imapx_server_process_command_sync (is, ic, _("Error performing NOOP"), cancellable, &local_error);
 
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+		camel_imapx_command_unref (ic);
 
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error performing NOOP"));
-		camel_imapx_job_take_error (job, local_error);
+		if (success)
+			goto try_again;
 	}
 
-	imapx_unregister_job (is, job);
-}
+	is->priv->get_message_stream = NULL;
 
-static gboolean
-imapx_job_noop_start (CamelIMAPXJob *job,
-                      CamelIMAPXServer *is,
-                      GCancellable *cancellable,
-                      GError **error)
-{
-	CamelIMAPXCommand *ic;
-	CamelIMAPXMailbox *mailbox;
+	if (success) {
+		if (local_error == NULL) {
+			g_io_stream_close (cache_stream, cancellable, &local_error);
+			g_prefix_error (
+				&local_error, "%s: ",
+				_("Failed to close the tmp stream"));
+		}
 
-	/* This may be NULL. */
-	mailbox = camel_imapx_job_ref_mailbox (job);
+		if (local_error == NULL &&
+		    g_cancellable_set_error_if_cancelled (cancellable, &local_error)) {
+			g_prefix_error (
+				&local_error, "%s: ",
+				_("Error fetching message"));
+		}
 
-	ic = camel_imapx_command_new (
-		is, "NOOP", mailbox, "NOOP");
+		if (local_error == NULL) {
+			gchar *cur_filename;
+			gchar *tmp_filename;
+			gchar *dirname;
 
-	camel_imapx_command_set_job (ic, job);
-	ic->complete = imapx_command_noop_done;
-	if (mailbox != NULL)
-		ic->pri = IMAPX_PRIORITY_REFRESH_INFO;
-	else
-		ic->pri = IMAPX_PRIORITY_NOOP;
+			cur_filename = camel_data_cache_get_filename (message_cache, "cur", message_uid);
+			tmp_filename = camel_data_cache_get_filename (message_cache, "tmp", message_uid);
 
-	imapx_command_queue (is, ic);
+			dirname = g_path_get_dirname (cur_filename);
+			g_mkdir_with_parents (dirname, 0700);
+			g_free (dirname);
 
-	camel_imapx_command_unref (ic);
+			if (g_rename (tmp_filename, cur_filename) == 0) {
+				/* Exchange the "tmp" stream for the "cur" stream. */
+				g_clear_object (&cache_stream);
+				cache_stream = camel_data_cache_get (message_cache, "cur", message_uid, &local_error);
+			} else {
+				g_set_error (
+					&local_error, G_FILE_ERROR,
+					g_file_error_from_errno (errno),
+					"%s: %s",
+					_("Failed to copy the tmp file"),
+					g_strerror (errno));
+			}
+
+			g_free (cur_filename);
+			g_free (tmp_filename);
+		}
+
+		/* Delete the 'tmp' file only if the operation succeeded. It's because
+		   cancelled operations end before they are properly finished (IMAP-protocol speaking),
+		   thus if any other GET_MESSAGE operation was waiting for this job, then it
+		   realized that the message was not downloaded and opened its own "tmp" file, but
+		   of the same name, thus this remove would drop file which could be used
+		   by a different GET_MESSAGE job. */
+		if (!local_error && !g_cancellable_is_cancelled (cancellable))
+			camel_data_cache_remove (message_cache, "tmp", message_uid, NULL);
+	}
 
-	g_clear_object (&mailbox);
+	if (!local_error) {
+		result_stream = camel_stream_new (cache_stream);
+	} else {
+		g_propagate_error (error, local_error);
+	}
 
-	return TRUE;
+	g_clear_object (&cache_stream);
+
+	return result_stream;
 }
 
-/* ********************************************************************** */
+gboolean
+camel_imapx_server_sync_message_sync (CamelIMAPXServer *is,
+				      CamelIMAPXMailbox *mailbox,
+				      CamelFolderSummary *summary,
+				      CamelDataCache *message_cache,
+				      const gchar *message_uid,
+				      GCancellable *cancellable,
+				      GError **error)
+{
+	gchar *cache_file = NULL;
+	gboolean is_cached;
+	struct stat st;
+	gboolean success = TRUE;
 
-/* FIXME: this is basically a copy of the same in camel-imapx-utils.c */
-static struct {
-	const gchar *name;
-	guint32 flag;
-} flags_table[] = {
-	{ "\\ANSWERED", CAMEL_MESSAGE_ANSWERED },
-	{ "\\DELETED", CAMEL_MESSAGE_DELETED },
-	{ "\\DRAFT", CAMEL_MESSAGE_DRAFT },
-	{ "\\FLAGGED", CAMEL_MESSAGE_FLAGGED },
-	{ "\\SEEN", CAMEL_MESSAGE_SEEN },
-	{ "\\RECENT", CAMEL_IMAPX_MESSAGE_RECENT },
-	{ "JUNK", CAMEL_MESSAGE_JUNK },
-	{ "NOTJUNK", CAMEL_MESSAGE_NOTJUNK }
-};
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+	g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
+	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), FALSE);
+	g_return_val_if_fail (message_uid != NULL, FALSE);
 
-/*
- *  flags 00101000
- * sflags 01001000
- * ^      01100000
- * ~flags 11010111
- * &      01000000
- *
- * &flags 00100000
- */
+	/* Check if the cache file already exists and is non-empty. */
+	cache_file = camel_data_cache_get_filename (message_cache, "cur", message_uid);
+	is_cached = (g_stat (cache_file, &st) == 0 && st.st_size > 0);
+	g_free (cache_file);
 
-static void
-imapx_command_sync_changes_done (CamelIMAPXServer *is,
-                                 CamelIMAPXCommand *ic)
-{
-	CamelIMAPXJob *job;
-	CamelIMAPXMailbox *mailbox;
-	CamelFolder *folder;
-	CamelStore *parent_store;
-	SyncChangesData *data;
-	const gchar *full_name;
-	GError *local_error = NULL;
+	if (!is_cached) {
+		CamelStream *stream;
 
-	job = camel_imapx_command_get_job (ic);
-	g_return_if_fail (CAMEL_IS_IMAPX_JOB (job));
+		stream = camel_imapx_server_get_message_sync (
+			is, mailbox, summary,
+			message_cache, message_uid,
+			cancellable, error);
 
-	data = camel_imapx_job_get_data (job);
-	g_return_if_fail (data != NULL);
+		success = (stream != NULL);
 
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_if_fail (mailbox != NULL);
+		g_clear_object (&stream);
+	}
 
-	folder = imapx_server_ref_folder (is, mailbox);
-	g_return_if_fail (folder != NULL);
+	return success;
+}
 
-	g_atomic_int_add (&job->commands, -1);
+gboolean
+camel_imapx_server_copy_message_sync (CamelIMAPXServer *is,
+				      CamelIMAPXMailbox *mailbox,
+				      CamelIMAPXMailbox *destination,
+				      GPtrArray *uids,
+				      gboolean delete_originals,
+				      gboolean remove_deleted_flags,
+				      GCancellable *cancellable,
+				      GError **error)
+{
+	GPtrArray *data_uids;
+	gint ii;
+	gboolean use_move_command = FALSE;
+	CamelIMAPXCommand *ic;
+	CamelFolder *folder;
+	GHashTable *source_infos;
+	gboolean remove_junk_flags;
+	gboolean success = TRUE;
 
-	full_name = camel_folder_get_full_name (folder);
-	parent_store = camel_folder_get_parent_store (folder);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (destination), FALSE);
+	g_return_val_if_fail (uids != NULL, FALSE);
 
-	/* If this worked, we should really just update the changes that we
-	 * sucessfully stored, so we dont have to worry about sending them
-	 * again ...
-	 * But then we'd have to track which uid's we actually updated, so
-	 * its easier just to refresh all of the ones we got.
-	 *
-	 * Not that ... given all the asynchronicity going on, we're guaranteed
-	 * that what we just set is actually what is on the server now .. but
-	 * if it isn't, i guess we'll fix up next refresh */
-
-	if (camel_imapx_command_set_error_if_failed (ic, &local_error)) {
-		g_prefix_error (
-			&local_error, "%s: ",
-			_("Error syncing changes"));
-		camel_imapx_job_take_error (job, local_error);
-		imapx_unregister_job (is, job);
-		goto exit;
+	/* To get permanent flags. That's okay if the "SELECT" fails here, as it can be
+	   due to the folder being write-only; just ignore the error and continue. */
+	camel_imapx_server_ensure_selected_sync (is, destination, cancellable, NULL);
 
-	/* lock cache ? */
-	} else {
-		guint32 unseen, permanentflags;
-		gint i;
+	if (g_cancellable_set_error_if_cancelled (cancellable, error))
+		return FALSE;
 
-		permanentflags = camel_imapx_mailbox_get_permanentflags (mailbox);
+	if (!camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error))
+		return FALSE;
 
-		for (i = 0; i < data->changed_uids->len; i++) {
-			CamelIMAPXMessageInfo *xinfo = (CamelIMAPXMessageInfo *) camel_folder_summary_get (folder->summary,
-					data->changed_uids->pdata[i]);
+	folder = imapx_server_ref_folder (is, mailbox);
+	g_return_val_if_fail (folder != NULL, FALSE);
 
-			if (!xinfo)
-				continue;
+	remove_deleted_flags = remove_deleted_flags || (folder->folder_flags & CAMEL_FOLDER_IS_TRASH) != 0;
+	remove_junk_flags = (folder->folder_flags & CAMEL_FOLDER_IS_JUNK) != 0;
 
-			xinfo->server_flags = xinfo->info.flags & CAMEL_IMAPX_SERVER_FLAGS;
-			if (!data->remove_deleted_flags ||
-			    !(xinfo->info.flags & CAMEL_MESSAGE_DELETED)) {
-				xinfo->info.flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED;
-			} else {
-				/* to stare back the \Deleted flag */
-				xinfo->server_flags &= ~CAMEL_MESSAGE_DELETED;
-				xinfo->info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
-			}
-			xinfo->info.dirty = TRUE;
-			if ((permanentflags & CAMEL_MESSAGE_USER) != 0 ||
-			    camel_flag_list_size (&xinfo->server_user_flags) == 0)
-				camel_flag_list_copy (&xinfo->server_user_flags, &xinfo->info.user_flags);
+	/* If we're moving messages, prefer "UID MOVE" if supported. */
+	if (delete_originals) {
+		g_mutex_lock (&is->priv->stream_lock);
 
-			camel_folder_summary_touch (folder->summary);
-			camel_message_info_unref (xinfo);
+		if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, MOVE)) {
+			delete_originals = FALSE;
+			use_move_command = TRUE;
 		}
 
-		/* Apply the changes to server-side unread count; it won't tell
-		 * us of these changes, of course. */
-		unseen = camel_imapx_mailbox_get_unseen (mailbox);
-		unseen += data->unread_change;
-		camel_imapx_mailbox_set_unseen (mailbox, unseen);
+		g_mutex_unlock (&is->priv->stream_lock);
 	}
 
-	if (g_atomic_int_get (&job->commands) == 0) {
-		if (folder->summary && (folder->summary->flags & CAMEL_FOLDER_SUMMARY_DIRTY) != 0) {
-			CamelStoreInfo *si;
-
-			/* ... and store's summary when folder's summary is dirty */
-			si = camel_store_summary_path (CAMEL_IMAPX_STORE (parent_store)->summary, full_name);
-			if (si) {
-				if (si->total != camel_folder_summary_get_saved_count (folder->summary) ||
-				    si->unread != camel_folder_summary_get_unread_count (folder->summary)) {
-					si->total = camel_folder_summary_get_saved_count (folder->summary);
-					si->unread = camel_folder_summary_get_unread_count (folder->summary);
-					camel_store_summary_touch (CAMEL_IMAPX_STORE (parent_store)->summary);
-				}
-
-				camel_store_summary_info_unref (CAMEL_IMAPX_STORE (parent_store)->summary, si);
-			}
-		}
+	source_infos = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, camel_message_info_unref);
+	data_uids = g_ptr_array_new ();
 
-		camel_folder_summary_save_to_db (folder->summary, NULL);
-		camel_store_summary_save (CAMEL_IMAPX_STORE (parent_store)->summary);
+	for (ii = 0; ii < uids->len; ii++) {
+		gchar *uid = (gchar *) camel_pstring_strdup (uids->pdata[ii]);
 
-		imapx_unregister_job (is, job);
-	} else {
-		/* Make sure no command will starve in a queue */
-		QUEUE_LOCK (is);
-		imapx_command_start_next (is);
-		QUEUE_UNLOCK (is);
+		g_ptr_array_add (data_uids, uid);
+		g_hash_table_insert (source_infos, uid, camel_folder_summary_get (folder->summary, uid));
 	}
 
-exit:
-	g_object_unref (folder);
-	g_object_unref (mailbox);
-}
+	g_ptr_array_sort (data_uids, (GCompareFunc) imapx_uids_array_cmp);
 
-static gboolean
-imapx_job_sync_changes_start (CamelIMAPXJob *job,
-                              CamelIMAPXServer *is,
-                              GCancellable *cancellable,
-                              GError **error)
-{
-	SyncChangesData *data;
-	CamelFolder *folder;
-	CamelIMAPXMailbox *mailbox;
-	guint32 i, j, permanentflags;
-	struct _uidset_state ss;
-	GPtrArray *uids;
-	gint on;
+	ii = 0;
+	while (ii < data_uids->len && success) {
+		struct _uidset_state uidset;
+		gint last_index = ii;
 
-	data = camel_imapx_job_get_data (job);
-	g_return_val_if_fail (data != NULL, FALSE);
+		imapx_uidset_init (&uidset, 0, MAX_COMMAND_LEN);
 
-	mailbox = camel_imapx_job_ref_mailbox (job);
-	g_return_val_if_fail (mailbox != NULL, FALSE);
+		if (use_move_command)
+			ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_MOVE_MESSAGE, "UID MOVE ");
+		else
+			ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_COPY_MESSAGE, "UID COPY ");
 
-	folder = imapx_server_ref_folder (is, mailbox);
-	g_return_val_if_fail (folder != NULL, FALSE);
+		while (ii < data_uids->len) {
+			const gchar *uid = (gchar *) g_ptr_array_index (data_uids, ii);
 
-	permanentflags = camel_imapx_mailbox_get_permanentflags (mailbox);
-	uids = data->changed_uids;
+			ii++;
 
-	for (on = 0; on < 2; on++) {
-		guint32 orset = on ? data->on_set : data->off_set;
-		GArray *user_set = on ? data->on_user : data->off_user;
+			if (imapx_uidset_add (&uidset, ic, uid) == 1)
+				break;
+		}
 
-		for (j = 0; j < G_N_ELEMENTS (flags_table); j++) {
-			guint32 flag = flags_table[j].flag;
-			CamelIMAPXCommand *ic = NULL;
+		imapx_uidset_done (&uidset, ic);
 
-			if ((orset & flag) == 0)
-				continue;
+		camel_imapx_command_add (ic, " %M", destination);
 
-			c (is->tagprefix, "checking/storing %s flags '%s'\n", on?"on":"off", flags_table[j].name);
-			imapx_uidset_init (&ss, 0, 100);
-			for (i = 0; i < uids->len; i++) {
-				CamelIMAPXMessageInfo *info;
-				gboolean remove_deleted_flag;
-				guint32 flags;
-				guint32 sflags;
-				gint send;
+		imapx_free_status (is->priv->copyuid_status);
+		is->priv->copyuid_status = NULL;
 
-				info = (CamelIMAPXMessageInfo *)
-					camel_folder_summary_get (
-						folder->summary,
-						uids->pdata[i]);
+		success = camel_imapx_server_process_command_sync (is, ic,
+			use_move_command ? _("Error moving messages") : _("Error copying messages"),
+			cancellable, error);
 
-				if (info == NULL)
-					continue;
+		if (success) {
+			struct _status_info *copyuid_status = is->priv->copyuid_status;
 
-				flags = (info->info.flags & CAMEL_IMAPX_SERVER_FLAGS) & permanentflags;
-				sflags = (info->server_flags & CAMEL_IMAPX_SERVER_FLAGS) & permanentflags;
-				send = 0;
+			if (ic->status && ic->status->condition == IMAPX_COPYUID)
+				copyuid_status = ic->status;
 
-				remove_deleted_flag =
-					data->remove_deleted_flags &&
-					(flags & CAMEL_MESSAGE_DELETED);
+			if (copyuid_status && copyuid_status->u.copyuid.uids &&
+			    copyuid_status->u.copyuid.copied_uids &&
+			    copyuid_status->u.copyuid.uids->len == copyuid_status->u.copyuid.copied_uids->len) {
+				CamelFolder *destination_folder;
+
+				destination_folder = imapx_server_ref_folder (is, destination);
+				if (destination_folder) {
+					CamelMessageInfo *source_info, *destination_info;
+					CamelFolderChangeInfo *changes;
+					gint ii;
+
+					changes = camel_folder_change_info_new ();
+
+					for (ii = 0; ii < copyuid_status->u.copyuid.uids->len; ii++) {
+						gchar *uid;
+						gboolean is_new = FALSE;
+
+						uid = g_strdup_printf ("%d", g_array_index (copyuid_status->u.copyuid.uids, guint32, ii));
+						source_info = g_hash_table_lookup (source_infos, uid);
+						g_free (uid);
+
+						if (!source_info)
+							continue;
+
+						uid = g_strdup_printf ("%d", g_array_index (copyuid_status->u.copyuid.copied_uids, guint32, ii));
+						destination_info = camel_folder_summary_get (folder->summary, uid);
+
+						if (!destination_info) {
+							is_new = TRUE;
+							destination_info = camel_message_info_clone (source_info);
+							destination_info->summary = destination_folder->summary;
+							camel_pstring_free (destination_info->uid);
+							destination_info->uid = camel_pstring_strdup (uid);
+						}
+
+						g_free (uid);
+
+						imapx_set_message_info_flags_for_new_message (
+							destination_info,
+							((CamelMessageInfoBase *) source_info)->flags,
+							((CamelMessageInfoBase *) source_info)->user_flags,
+							TRUE,
+							((CamelMessageInfoBase *) source_info)->user_tags,
+							camel_imapx_mailbox_get_permanentflags (destination));
+						if (remove_deleted_flags)
+							camel_message_info_set_flags (destination_info, CAMEL_MESSAGE_DELETED, 0);
+						if (remove_junk_flags)
+							camel_message_info_set_flags (destination_info, CAMEL_MESSAGE_JUNK, 0);
+						if (is_new)
+							camel_folder_summary_add (destination_folder->summary, destination_info);
+						camel_folder_change_info_add_uid (changes, destination_info->uid);
 
-				if (remove_deleted_flag) {
-					/* Remove the DELETED flag so the
-					 * message appears normally in the
-					 * real Trash folder when copied. */
-					flags &= ~CAMEL_MESSAGE_DELETED;
-				}
+						if (!is_new)
+							camel_message_info_unref (destination_info);
+					}
 
-				if ( (on && (((flags ^ sflags) & flags) & flag))
-				     || (!on && (((flags ^ sflags) & ~flags) & flag))) {
-					if (ic == NULL) {
-						ic = camel_imapx_command_new (
-							is, "STORE", mailbox,
-							"UID STORE ");
-						ic->complete = imapx_command_sync_changes_done;
-						camel_imapx_command_set_job (ic, job);
-						ic->pri = job->pri;
+					if (camel_folder_change_info_changed (changes)) {
+						camel_folder_summary_touch (destination_folder->summary);
+						camel_folder_summary_save_to_db (destination_folder->summary, NULL);
+						camel_folder_changed (destination_folder, changes);
 					}
-					send = imapx_uidset_add (&ss, ic, camel_message_info_uid (info));
-				}
-				if (send == 1 || (i == uids->len - 1 && ic && imapx_uidset_done (&ss, ic))) {
-					g_atomic_int_add (&job->commands, 1);
-					camel_imapx_command_add (ic, " %tFLAGS.SILENT (%t)", on?"+":"-", flags_table[j].name);
-					imapx_command_queue (is, ic);
-					camel_imapx_command_unref (ic);
-					ic = NULL;
-				}
-				if (flag == CAMEL_MESSAGE_SEEN) {
-					/* Remember how the server's unread count will change if this
-					 * command succeeds */
-					if (on)
-						data->unread_change--;
-					else
-						data->unread_change++;
-				}
 
-				/* The second round and the server doesn't support saving user flags,
-				   thus store them at least locally */
-				if (on && (permanentflags & CAMEL_MESSAGE_USER) == 0) {
-					camel_flag_list_copy (&info->server_user_flags, &info->info.user_flags);
+					camel_folder_change_info_free (changes);
+					g_object_unref (destination_folder);
 				}
-
-				camel_message_info_unref (info);
 			}
 
-			g_warn_if_fail (ic == NULL);
-		}
+			if (delete_originals || use_move_command) {
+				CamelFolderChangeInfo *changes = NULL;
+				gint jj;
 
-		if (user_set && (permanentflags & CAMEL_MESSAGE_USER) != 0) {
-			CamelIMAPXCommand *ic = NULL;
+				camel_folder_freeze (folder);
 
-			for (j = 0; j < user_set->len; j++) {
-				struct _imapx_flag_change *c = &g_array_index (user_set, struct _imapx_flag_change, j);
+				for (jj = last_index; jj < ii; jj++) {
+					const gchar *uid = uids->pdata[jj];
 
-				imapx_uidset_init (&ss, 0, 100);
-				for (i = 0; i < c->infos->len; i++) {
-					CamelIMAPXMessageInfo *info = c->infos->pdata[i];
+					if (delete_originals) {
+						camel_folder_delete_message (folder, uid);
+					} else {
+						if (camel_folder_summary_remove_uid (folder->summary, uid)) {
+							if (!changes)
+								changes = camel_folder_change_info_new ();
 
-					if (ic == NULL) {
-						ic = camel_imapx_command_new (
-							is, "STORE", mailbox,
-							"UID STORE ");
-						ic->complete = imapx_command_sync_changes_done;
-						camel_imapx_command_set_job (ic, job);
-						ic->pri = job->pri;
+							camel_folder_change_info_remove_uid (changes, uid);
+						}
 					}
+				}
 
-					if (imapx_uidset_add (&ss, ic, camel_message_info_uid (info)) == 1
-					    || (i == c->infos->len - 1 && imapx_uidset_done (&ss, ic))) {
-						g_atomic_int_add (&job->commands, 1);
-						camel_imapx_command_add (ic, " %tFLAGS.SILENT (%t)", on?"+":"-", c->name);
-						imapx_command_queue (is, ic);
-						camel_imapx_command_unref (ic);
-						ic = NULL;
-					}
+				if (changes && camel_folder_change_info_changed (changes)) {
+					camel_folder_summary_touch (folder->summary);
+					camel_folder_summary_save_to_db (folder->summary, NULL);
+					camel_folder_changed (folder, changes);
 				}
+
+				camel_folder_thaw (folder);
+
+				if (changes)
+					camel_folder_change_info_free (changes);
 			}
 		}
-	}
 
-	g_object_unref (folder);
-	g_object_unref (mailbox);
+		imapx_free_status (is->priv->copyuid_status);
+		is->priv->copyuid_status = NULL;
 
-	if (g_atomic_int_get (&job->commands) == 0) {
-		imapx_unregister_job (is, job);
-	} else {
-		/* Make sure no command will starve in a queue */
-		QUEUE_LOCK (is);
-		imapx_command_start_next (is);
-		QUEUE_UNLOCK (is);
+		camel_imapx_command_unref (ic);
 	}
 
-	return TRUE;
+	g_hash_table_destroy (source_infos);
+	g_ptr_array_foreach (data_uids, (GFunc) camel_pstring_free, NULL);
+	g_ptr_array_free (data_uids, TRUE);
+	g_object_unref (folder);
+
+	return success;
 }
 
-static gboolean
-imapx_job_sync_changes_matches (CamelIMAPXJob *job,
-                                CamelIMAPXMailbox *mailbox,
-                                const gchar *uid)
+static const gchar *
+get_month_str (gint month)
 {
-	return camel_imapx_job_has_mailbox (job, mailbox);
+	static const gchar tm_months[][4] = {
+		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+	};
+
+	if (month < 1 || month > 12)
+		return NULL;
+
+	return tm_months[month - 1];
 }
 
-static void
-imapx_abort_all_commands (CamelIMAPXServer *is,
-                          const GError *error)
+gboolean
+camel_imapx_server_append_message_sync (CamelIMAPXServer *is,
+					CamelIMAPXMailbox *mailbox,
+					CamelFolderSummary *summary,
+					CamelDataCache *message_cache,
+					CamelMimeMessage *message,
+					const CamelMessageInfo *mi,
+					gchar **appended_uid,
+					GCancellable *cancellable,
+					GError **error)
 {
-	CamelIMAPXCommandQueue *queue;
-	GList *head, *link;
+	gchar *uid = NULL, *path = NULL;
+	CamelMimeFilter *filter;
+	CamelIMAPXCommand *ic;
+	CamelMessageInfo *info;
+	GIOStream *base_stream;
+	GOutputStream *output_stream;
+	GOutputStream *filter_stream;
+	gint res;
+	time_t date_time;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+	g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
+	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), FALSE);
+	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+	/* CamelMessageInfo can be NULL. */
+
+	/* That's okay if the "SELECT" fails here, as it can be due to
+	   the folder being write-only; just ignore the error and continue. */
+	camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, NULL);
 
-	/* Transfer all pending and active commands to a separate
-	 * command queue to complete them without holding QUEUE_LOCK. */
+	if (g_cancellable_set_error_if_cancelled (cancellable, error))
+		return FALSE;
+
+	/* Append just assumes we have no/a dodgy connection.  We dump
+	 * stuff into the 'new' directory, and let the summary know it's
+	 * there.  Then we fire off a no-reply job which will asynchronously
+	 * upload the message at some point in the future, and fix up the
+	 * summary to match */
 
-	queue = camel_imapx_command_queue_new ();
+	/* chen cleanup this later */
+	uid = imapx_get_temp_uid ();
+	base_stream = camel_data_cache_add (message_cache, "new", uid, error);
+	if (base_stream == NULL) {
+		g_prefix_error (error, _("Cannot create spool file: "));
+		g_free (uid);
+		return FALSE;
+	}
 
-	imapx_server_set_shutdown_error (is, error);
+	output_stream = g_io_stream_get_output_stream (base_stream);
+	filter = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_CRLF);
+	filter_stream = camel_filter_output_stream_new (output_stream, filter);
 
-	QUEUE_LOCK (is);
+	g_filter_output_stream_set_close_base_stream (
+		G_FILTER_OUTPUT_STREAM (filter_stream), FALSE);
 
-	camel_imapx_command_queue_transfer (is->queue, queue);
-	camel_imapx_command_queue_transfer (is->active, queue);
+	res = camel_data_wrapper_write_to_output_stream_sync (
+		CAMEL_DATA_WRAPPER (message),
+		filter_stream, cancellable, error);
 
-	head = camel_imapx_command_queue_peek_head_link (queue);
-	for (link = head; link != NULL; link = g_list_next (link)) {
-		CamelIMAPXCommand *ic = link->data;
+	g_object_unref (base_stream);
+	g_object_unref (filter_stream);
+	g_object_unref (filter);
 
-		if (ic)
-			imapx_server_command_removed (is, ic);
+	if (res == -1) {
+		g_prefix_error (error, _("Cannot create spool file: "));
+		camel_data_cache_remove (message_cache, "new", uid, NULL);
+		g_free (uid);
+		return FALSE;
 	}
 
-	QUEUE_UNLOCK (is);
+	date_time = camel_mime_message_get_date (message, NULL);
+	path = camel_data_cache_get_filename (message_cache, "new", uid);
+	info = camel_folder_summary_info_new_from_message (summary, message, NULL);
+	info->uid = camel_pstring_strdup (uid);
 
-	head = camel_imapx_command_queue_peek_head_link (queue);
+	if (mi != NULL) {
+		struct icaltimetype icaltime;
+		CamelMessageInfoBase *base_info = (CamelMessageInfoBase *) info;
+		const CamelFlag *flag;
+		const CamelTag *tag;
 
-	for (link = head; link != NULL; link = g_list_next (link)) {
-		CamelIMAPXCommand *ic = link->data;
+		base_info->flags = camel_message_info_flags (mi);
+		base_info->size = camel_message_info_size (mi);
 
-		/* Sanity check the CamelIMAPXCommand before proceeding.
-		 * XXX We are actually getting reports of crashes here...
-		 *     not sure how this is happening but it's happening. */
-		if (ic == NULL)
-			continue;
+		flag = camel_message_info_user_flags (mi);
+		while (flag != NULL) {
+			if (*flag->name != '\0')
+				camel_flag_set (
+					&base_info->user_flags,
+					flag->name, TRUE);
+			flag = flag->next;
+		}
+
+		tag = camel_message_info_user_tags (mi);
+		while (tag != NULL) {
+			if (*tag->name != '\0')
+				camel_tag_set (
+					&base_info->user_tags,
+					tag->name, tag->value);
+			tag = tag->next;
+		}
+
+		if (date_time > 0) {
+			icaltime = icaltime_from_timet (date_time, FALSE);
+			if (!icaltime_is_valid_time (icaltime))
+				date_time = -1;
+		}
+
+		if (date_time <= 0)
+			date_time = camel_message_info_date_received (mi);
 
-		/* Insert an error into the CamelIMAPXCommand to be
-		 * propagated when the completion callback function
-		 * calls camel_imapx_command_set_error_if_failed(). */
-		camel_imapx_command_failed (ic, error);
+		if (date_time > 0) {
+			icaltime = icaltime_from_timet (date_time, FALSE);
+			if (!icaltime_is_valid_time (icaltime))
+				date_time = -1;
+		}
+	}
 
-		/* Invoke the completion callback function so it can
-		 * perform any cleanup processing and unregister its
-		 * CamelIMAPXJob. */
-		ic->complete (is, ic);
+	if (!camel_message_info_size (info)) {
+		CamelStreamNull *sn = (CamelStreamNull *) camel_stream_null_new ();
+
+		camel_data_wrapper_write_to_stream_sync (
+			CAMEL_DATA_WRAPPER (message),
+			CAMEL_STREAM (sn), NULL, NULL);
+		((CamelMessageInfoBase *) info)->size = sn->written;
+		g_object_unref (sn);
 	}
 
-	camel_imapx_command_queue_free (queue);
+	g_free (uid);
 
-	QUEUE_LOCK (is);
+	if (camel_mime_message_has_attachment (message))
+		((CamelMessageInfoBase *) info)->flags |= CAMEL_MESSAGE_ATTACHMENTS;
 
-	/* Abort also any pending jobs which are not in the command queues yet */
-	if (!g_queue_is_empty (&is->jobs)) {
-		GList *jobs, *iter;
+	if (date_time > 0) {
+		gchar *date_time_str;
+		struct tm stm;
 
-		jobs = g_list_copy (g_queue_peek_head_link (&is->jobs));
-		g_list_foreach (jobs, (GFunc) camel_imapx_job_ref, NULL);
+		gmtime_r (&date_time, &stm);
 
-		for (iter = jobs; iter != NULL; iter = g_list_next (iter)) {
-			CamelIMAPXJob *job = iter->data;
+		/* Store always in UTC */
+		date_time_str = g_strdup_printf (
+			"\"%02d-%s-%04d %02d:%02d:%02d +0000\"",
+			stm.tm_mday,
+			get_month_str (stm.tm_mon + 1),
+			stm.tm_year + 1900,
+			stm.tm_hour,
+			stm.tm_min,
+			stm.tm_sec);
 
-			camel_imapx_job_take_error (job, g_error_copy (error));
-			camel_imapx_job_done (job);
-		}
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_APPEND_MESSAGE, "APPEND %M %F %t %P",
+			mailbox,
+			((CamelMessageInfoBase *) info)->flags,
+			((CamelMessageInfoBase *) info)->user_flags,
+			date_time_str,
+			path);
 
-		g_list_free_full (jobs, (GDestroyNotify) camel_imapx_job_unref);
+		g_free (date_time_str);
+	} else {
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_APPEND_MESSAGE, "APPEND %M %F %P",
+			mailbox,
+			((CamelMessageInfoBase *) info)->flags,
+			((CamelMessageInfoBase *) info)->user_flags,
+			path);
 	}
 
-	QUEUE_UNLOCK (is);
-}
+	success = camel_imapx_server_process_command_sync (is, ic, _("Error appending message"), cancellable, error);
 
-/* ********************************************************************** */
+	if (success) {
+		CamelIMAPXFolder *imapx_folder;
+		CamelFolder *folder;
+		CamelMessageInfo *mi;
+		gchar *cur, *old_uid;
+		guint32 uidvalidity;
 
-static gboolean
-imapx_ready_to_read (GInputStream *input_stream,
-                     CamelIMAPXServer *is)
-{
-	GOutputStream *output_stream;
-	GCancellable *cancellable;
-	GError *local_error = NULL;
+		folder = imapx_server_ref_folder (is, mailbox);
+		g_return_val_if_fail (folder != NULL, FALSE);
 
-	/* XXX Don't use the passed in GInputStream because that's
-	 *     the CamelIMAPXInputStream base stream.  We need the
-	 *     CamelIMAPXInputStream itself. */
+		uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox);
 
-	input_stream = camel_imapx_server_ref_input_stream (is);
-	output_stream = camel_imapx_server_ref_output_stream (is);
+		imapx_folder = CAMEL_IMAPX_FOLDER (folder);
 
-	cancellable = g_weak_ref_get (&is->priv->parser_cancellable);
+		/* Append done.  If we the server supports UIDPLUS we will get
+		 * an APPENDUID response with the new uid.  This lets us move the
+		 * message we have directly to the cache and also create a correctly
+		 * numbered MessageInfo, without losing any information.  Otherwise
+		 * we have to wait for the server to let us know it was appended. */
+
+		mi = camel_message_info_clone (info);
+		old_uid = g_strdup (info->uid);
+
+		if (ic->status && ic->status->condition == IMAPX_APPENDUID) {
+			c (is->priv->tagprefix, "Got appenduid %d %d\n", (gint) ic->status->u.appenduid.uidvalidity, (gint) ic->status->u.appenduid.uid);
+			if (ic->status->u.appenduid.uidvalidity == uidvalidity) {
+				if (appended_uid)
+					*appended_uid = g_strdup_printf ("%u", (guint) ic->status->u.appenduid.uid);
+				mi->uid = camel_pstring_add (g_strdup_printf ("%u", (guint) ic->status->u.appenduid.uid), TRUE);
+
+				cur = camel_data_cache_get_filename  (imapx_folder->cache, "cur", mi->uid);
+				if (g_rename (path, cur) == -1 && errno != ENOENT) {
+					g_warning ("%s: Failed to rename '%s' to '%s': %s", G_STRFUNC, path, cur, g_strerror (errno));
+				}
 
-	while (imapx_step (is, input_stream, cancellable, &local_error)) {
-		gint bytes_buffered;
+				imapx_set_message_info_flags_for_new_message (
+					mi,
+					((CamelMessageInfoBase *) info)->flags,
+					((CamelMessageInfoBase *) info)->user_flags,
+					TRUE,
+					((CamelMessageInfoBase *) info)->user_tags,
+					camel_imapx_mailbox_get_permanentflags (mailbox));
 
-		bytes_buffered = camel_imapx_input_stream_buffered (
-			CAMEL_IMAPX_INPUT_STREAM (input_stream));
-		if (bytes_buffered == 0)
-			break;
-	}
+				camel_folder_summary_add (folder->summary, mi);
 
-	if (g_cancellable_is_cancelled (cancellable)) {
-		gboolean active_queue_is_empty, is_shutdown_request;
+				g_mutex_lock (&is->priv->changes_lock);
+				camel_folder_change_info_add_uid (is->priv->changes, mi->uid);
+				g_mutex_unlock (&is->priv->changes_lock);
 
-		QUEUE_LOCK (is);
-		active_queue_is_empty =
-			camel_imapx_command_queue_is_empty (is->active);
-		is_shutdown_request = is->state == IMAPX_SHUTDOWN;
-		QUEUE_UNLOCK (is);
+				mi = NULL;
 
-		if (!is_shutdown_request && (active_queue_is_empty || imapx_in_idle (is))) {
-			g_cancellable_reset (cancellable);
-			g_clear_error (&local_error);
-		} else {
-			/* Cancelled error should be set. */
-			g_warn_if_fail (local_error != NULL);
+				g_free (cur);
+			} else {
+				c (is->priv->tagprefix, "but uidvalidity changed \n");
+			}
 		}
-	}
 
-	g_clear_object (&input_stream);
-	g_clear_object (&output_stream);
-	g_clear_object (&cancellable);
+		camel_data_cache_remove (imapx_folder->cache, "new", old_uid, NULL);
+		g_free (old_uid);
 
-	if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) {
-		QUEUE_LOCK (is);
-		if (camel_imapx_command_queue_is_empty (is->active) && is->state != IMAPX_SHUTDOWN) {
-			camel_imapx_debug (io, is->tagprefix, "Ignoring timeout error, nothing was waiting (original error: %s)\n", local_error->message);
-			g_clear_error (&local_error);
-		}
-		QUEUE_UNLOCK (is);
+		camel_imapx_command_unref (ic);
+		if (mi)
+			camel_message_info_unref (mi);
+		g_object_unref (folder);
 	}
 
-	if (local_error != NULL) {
-		camel_imapx_debug (io, is->tagprefix, "Data read failed with error '%s'\n", local_error->message);
-
-		/* Sadly, G_IO_ERROR_FAILED is also used for 'Connection reset by peer' error */
-		if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
-		    g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) {
-			local_error->domain = CAMEL_IMAPX_SERVER_ERROR;
-			local_error->code = CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT;
-		}
-
-		imapx_server_set_shutdown_error (is, local_error);
-
-		/* Call the signal early, certain thread interleaving can cause the closed connection
-		   being reused on the following reconnect attempt. There is also re-setting
-		   the shutdown_error above, because the signal handler in connection manager
-		   also calls camel_imapx_server_shutdown(), but without the error, while we want
-		   to have there propagated the "try reconnect" error instead. As there is no
-		   guarantee that it'll be called, then we also quit the parser's mainloop and
-		   call the imapx_abort_all_commands() below - just in case. */
-		g_signal_emit (is, signals[SHUTDOWN], 0, local_error);
-
-		g_main_loop_quit (is->priv->parser_main_loop);
-		imapx_abort_all_commands (is, local_error);
-		g_clear_error (&local_error);
-		return G_SOURCE_REMOVE;
-	}
+	g_free (path);
 
-	return G_SOURCE_CONTINUE;
+	return success;
 }
 
-/*
- * The main processing (reading) loop.
- *
- * Main area of locking required is command_queue
- * and command_start_next, the 'literal' command,
- * the jobs queue, the active queue, the queue
- * queue. */
-static gpointer
-imapx_parser_thread (gpointer user_data)
+gboolean
+camel_imapx_server_noop_sync (CamelIMAPXServer *is,
+			      CamelIMAPXMailbox *mailbox,
+			      GCancellable *cancellable,
+			      GError **error)
 {
-	CamelIMAPXServer *is;
-	GInputStream *input_stream;
-	GCancellable *cancellable;
-	GSource *pollable_source;
-	GError *shutdown_error;
-
-	is = CAMEL_IMAPX_SERVER (user_data);
-
-	/* Do not use CamelOperation here, because it can be cancelled at
-	 * an application end with camel_operation_cancel_all() call, which
-	 * is done too early, before any pending jobs are properly finished
-	 * (it can be IDLE job, or save of folder changes back to the server).
-	 */
-	cancellable = g_cancellable_new ();
-	g_weak_ref_set (&is->priv->parser_cancellable, cancellable);
-
-	input_stream = camel_imapx_server_ref_input_stream (is);
-	g_return_val_if_fail (input_stream != NULL, NULL);
+	gboolean success = TRUE;
 
-	g_main_context_push_thread_default (is->priv->parser_main_context);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	/* Mailbox may be NULL. */
 
-	pollable_source = g_pollable_input_stream_create_source (
-		G_POLLABLE_INPUT_STREAM (input_stream), cancellable);
-	g_source_set_callback (
-		pollable_source,
-		(GSourceFunc) imapx_ready_to_read,
-		g_object_ref (is),
-		(GDestroyNotify) g_object_unref);
-	g_source_attach (
-		pollable_source,
-		is->priv->parser_main_context);
-	g_source_unref (pollable_source);
+	if (mailbox)
+		success = camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error);
 
-	g_clear_object (&cancellable);
-	g_clear_object (&input_stream);
+	if (success) {
+		CamelIMAPXCommand *ic;
 
-	g_main_loop_run (is->priv->parser_main_loop);
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NOOP, "NOOP");
 
-	QUEUE_LOCK (is);
-	is->state = IMAPX_SHUTDOWN;
-	QUEUE_UNLOCK (is);
+		success = camel_imapx_server_process_command_sync (is, ic, _("Error performing NOOP"), cancellable, error);
 
-	g_main_context_pop_thread_default (is->priv->parser_main_context);
+		camel_imapx_command_unref (ic);
+	}
 
-	shutdown_error = imapx_server_dup_shutdown_error (is);
+	return success;
+}
 
-	g_signal_emit (is, signals[SHUTDOWN], 0, shutdown_error);
+/* ********************************************************************** */
 
-	g_clear_error (&shutdown_error);
+static gint
+imapx_refresh_info_uid_cmp (gconstpointer ap,
+                            gconstpointer bp,
+                            gboolean ascending)
+{
+	guint av, bv;
 
-	g_object_unref (is);
+	av = g_ascii_strtoull ((const gchar *) ap, NULL, 10);
+	bv = g_ascii_strtoull ((const gchar *) bp, NULL, 10);
 
-	return NULL;
+	if (av < bv)
+		return ascending ? -1 : 1;
+	else if (av > bv)
+		return ascending ? 1 : -1;
+	else
+		return 0;
 }
 
-static void
-imapx_server_set_store (CamelIMAPXServer *server,
-                        CamelIMAPXStore *store)
+static gint
+imapx_uids_array_cmp (gconstpointer ap,
+                      gconstpointer bp)
 {
-	g_return_if_fail (CAMEL_IS_IMAPX_STORE (store));
+	const gchar **a = (const gchar **) ap;
+	const gchar **b = (const gchar **) bp;
 
-	g_weak_ref_set (&server->priv->store, store);
+	return imapx_refresh_info_uid_cmp (*a, *b, TRUE);
 }
 
-static void
-imapx_server_set_property (GObject *object,
-                           guint property_id,
-                           const GValue *value,
-                           GParamSpec *pspec)
+static gint
+imapx_uids_desc_cmp (gconstpointer ap,
+		     gconstpointer bp)
 {
-	switch (property_id) {
-		case PROP_STORE:
-			imapx_server_set_store (
-				CAMEL_IMAPX_SERVER (object),
-				g_value_get_object (value));
-			return;
-	}
+	const gchar *a = (const gchar *) ap;
+	const gchar *b = (const gchar *) bp;
 
-	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+	return imapx_refresh_info_uid_cmp (a, b, FALSE);
 }
 
 static void
-imapx_server_get_property (GObject *object,
-                           guint property_id,
-                           GValue *value,
-                           GParamSpec *pspec)
+imapx_server_process_fetch_changes_infos (CamelIMAPXServer *is,
+					  CamelIMAPXMailbox *mailbox,
+					  CamelFolder *folder,
+					  GHashTable *infos,
+					  GHashTable *known_uids,
+					  GSList **out_fetch_summary_uids,
+					  guint64 from_uidl,
+					  guint64 to_uidl)
 {
-	switch (property_id) {
-		case PROP_STORE:
-			g_value_take_object (
-				value,
-				camel_imapx_server_ref_store (
-				CAMEL_IMAPX_SERVER (object)));
-			return;
-	}
+	GHashTableIter iter;
+	gpointer key, value;
+	CamelFolderSummary *summary;
 
-	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+	g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is));
+	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
+	g_return_if_fail (CAMEL_IS_FOLDER (folder));
+	g_return_if_fail (infos != NULL);
+
+	if (out_fetch_summary_uids)
+		g_return_if_fail (*out_fetch_summary_uids == NULL);
+
+	summary = folder->summary;
+
+	g_hash_table_iter_init (&iter, infos);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		const gchar *uid = key;
+		FetchChangesInfo *nfo = value;
+		CamelMessageInfo *minfo;
+
+		if (!uid || !nfo)
+			continue;
+
+		if (known_uids)
+			g_hash_table_insert (known_uids, (gpointer) camel_pstring_strdup (uid), GINT_TO_POINTER (1));
+
+		if (!camel_folder_summary_check_uid (summary, uid) ||
+		    !(minfo = camel_folder_summary_get (summary, uid))) {
+			if (out_fetch_summary_uids) {
+				*out_fetch_summary_uids = g_slist_prepend (*out_fetch_summary_uids,
+					(gpointer) camel_pstring_strdup (uid));
+			}
+
+			continue;
+		}
+
+		if (imapx_update_message_info_flags (
+			minfo,
+			nfo->server_flags,
+			nfo->server_user_flags,
+			camel_imapx_mailbox_get_permanentflags (mailbox),
+			folder, FALSE)) {
+			g_mutex_lock (&is->priv->changes_lock);
+			camel_folder_change_info_change_uid (is->priv->changes, camel_message_info_uid (minfo));
+			g_mutex_unlock (&is->priv->changes_lock);
+		}
+
+		camel_message_info_unref (minfo);
+	}
 }
 
-static void
-imapx_server_dispose (GObject *object)
+static gboolean
+imapx_server_fetch_changes (CamelIMAPXServer *is,
+			    CamelIMAPXMailbox *mailbox,
+			    CamelFolder *folder,
+			    GHashTable *known_uids,
+			    guint64 from_uidl,
+			    guint64 to_uidl,
+			    GCancellable *cancellable,
+			    GError **error)
 {
-	CamelIMAPXServer *server = CAMEL_IMAPX_SERVER (object);
-	gboolean idle_main_loop_is_running;
-	gboolean parser_main_loop_is_running;
+	GSList *fetch_summary_uids = NULL;
+	GHashTable *infos; /* uid ~> FetchChangesInfo */
+	CamelIMAPXCommand *ic;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, error))
+		return FALSE;
+
+	if (!from_uidl)
+		from_uidl = 1;
 
-	/* Server should be shut down already.  Warn if
-	 * the idle or parser threads are still running. */
-	idle_main_loop_is_running =
-		g_main_loop_is_running (server->priv->idle_main_loop);
-	parser_main_loop_is_running =
-		g_main_loop_is_running (server->priv->parser_main_loop);
-	g_warn_if_fail (!idle_main_loop_is_running);
-	g_warn_if_fail (!parser_main_loop_is_running);
-
-	if (server->priv->parser_thread != NULL) {
-		g_thread_unref (server->priv->parser_thread);
-		server->priv->parser_thread = NULL;
-	}
-
-	if (server->priv->idle_thread != NULL) {
-		g_thread_unref (server->priv->idle_thread);
-		server->priv->idle_thread = NULL;
+	if (to_uidl > 0) {
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_REFRESH_INFO, "UID FETCH %lld:%lld (UID FLAGS)", from_uidl, to_uidl);
+	} else {
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_REFRESH_INFO, "UID FETCH %lld:* (UID FLAGS)", from_uidl);
 	}
 
-	imapx_disconnect (server);
+	g_return_val_if_fail (is->priv->fetch_changes_mailbox == NULL, FALSE);
+	g_return_val_if_fail (is->priv->fetch_changes_folder == NULL, FALSE);
+	g_return_val_if_fail (is->priv->fetch_changes_infos == NULL, FALSE);
 
-	g_weak_ref_set (&server->priv->store, NULL);
+	infos = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, fetch_changes_info_free);
 
-#if GLIB_CHECK_VERSION(2,39,0)
-	g_clear_object (&server->priv->subprocess);
-#endif
+	is->priv->fetch_changes_mailbox = mailbox;
+	is->priv->fetch_changes_folder = folder;
+	is->priv->fetch_changes_infos = infos;
+	is->priv->fetch_changes_last_progress = 0;
 
-	/* Chain up to parent's dispose() method. */
-	G_OBJECT_CLASS (camel_imapx_server_parent_class)->dispose (object);
-}
+	camel_operation_push_message (cancellable,
+		_("Scanning for changed messages in '%s'"),
+		camel_folder_get_display_name (folder));
 
-static void
-imapx_server_finalize (GObject *object)
-{
-	CamelIMAPXServer *is = CAMEL_IMAPX_SERVER (object);
+	success = camel_imapx_server_process_command_sync (is, ic, _("Error scanning changes"), cancellable, error);
 
-	g_mutex_clear (&is->priv->stream_lock);
+	camel_operation_pop_message (cancellable);
+	camel_imapx_command_unref (ic);
 
-	camel_imapx_command_queue_free (is->queue);
-	camel_imapx_command_queue_free (is->active);
-	camel_imapx_command_queue_free (is->done);
-
-	is->queue = NULL;
-	is->active = NULL;
-	is->done = NULL;
+	/* It can partly succeed. */
+	imapx_server_process_fetch_changes_infos (is, mailbox, folder, infos, known_uids, &fetch_summary_uids, from_uidl, to_uidl);
 
-	g_rec_mutex_clear (&is->queue_lock);
-	g_mutex_clear (&is->priv->select_lock);
+	g_hash_table_remove_all (infos);
 
-	g_main_loop_unref (is->priv->parser_main_loop);
-	g_main_context_unref (is->priv->parser_main_context);
+	if (success && fetch_summary_uids) {
+		struct _uidset_state uidset;
+		GSList *link;
 
-	camel_folder_change_info_free (is->priv->changes);
+		ic = NULL;
+		imapx_uidset_init (&uidset, 0, 100);
 
-	g_free (is->priv->context);
-	g_hash_table_destroy (is->priv->untagged_handlers);
+		camel_operation_push_message (cancellable,
+			_("Fetching summary information for new messages in '%s'"),
+			camel_folder_get_display_name (folder));
 
-	if (is->priv->inactivity_timeout != NULL)
-		g_source_unref (is->priv->inactivity_timeout);
-	g_mutex_clear (&is->priv->inactivity_timeout_lock);
+		fetch_summary_uids = g_slist_sort (fetch_summary_uids, imapx_uids_desc_cmp);
 
-	g_free (is->priv->status_data_items);
-	g_free (is->priv->list_return_opts);
+		for (link = fetch_summary_uids; link; link = g_slist_next (link)) {
+			const gchar *uid = link->data;
 
-	if (is->priv->search_results != NULL)
-		g_array_unref (is->priv->search_results);
-	g_mutex_clear (&is->priv->search_results_lock);
+			if (!uid)
+				continue;
 
-	g_hash_table_destroy (is->priv->known_alerts);
-	g_mutex_clear (&is->priv->known_alerts_lock);
+			if (!ic)
+				ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_REFRESH_INFO, "UID FETCH ");
 
-	g_rec_mutex_clear (&is->priv->idle_lock);
-	g_main_loop_unref (is->priv->idle_main_loop);
-	g_main_context_unref (is->priv->idle_main_context);
+			if (imapx_uidset_add (&uidset, ic, uid) == 1 || (!link->next && ic && imapx_uidset_done (&uidset, ic))) {
+				camel_imapx_command_add (ic, " (RFC822.SIZE RFC822.HEADER FLAGS)");
 
-	g_mutex_clear (&is->priv->jobs_prop_lock);
-	g_hash_table_destroy (is->priv->jobs_prop_folder_paths);
+				success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching message info"), cancellable, error);
 
-	g_mutex_clear (&is->priv->shutdown_error_lock);
-	g_clear_error (&is->priv->shutdown_error);
+				camel_imapx_command_unref (ic);
+				ic = NULL;
 
-	g_weak_ref_clear (&is->priv->store);
-	g_weak_ref_clear (&is->priv->parser_cancellable);
-	g_weak_ref_clear (&is->priv->select_mailbox);
-	g_weak_ref_clear (&is->priv->select_closing);
-	g_weak_ref_clear (&is->priv->select_pending);
+				if (!success)
+					break;
 
-	/* Chain up to parent's finalize() method. */
-	G_OBJECT_CLASS (camel_imapx_server_parent_class)->finalize (object);
-}
+				imapx_server_process_fetch_changes_infos (is, mailbox, folder, infos, NULL, NULL, 0, 0);
+				g_hash_table_remove_all (infos);
+			}
+		}
 
-static void
-imapx_server_constructed (GObject *object)
-{
-	CamelIMAPXServer *server;
+		camel_operation_pop_message (cancellable);
 
-	/* Chain up to parent's method. */
-	G_OBJECT_CLASS (camel_imapx_server_parent_class)->constructed (object);
+		imapx_server_process_fetch_changes_infos (is, mailbox, folder, infos, NULL, NULL, 0, 0);
+	}
 
-	server = CAMEL_IMAPX_SERVER (object);
-	server->tagprefix = 'Z';
-}
+	g_return_val_if_fail (is->priv->fetch_changes_mailbox == mailbox, FALSE);
+	g_return_val_if_fail (is->priv->fetch_changes_folder == folder, FALSE);
+	g_return_val_if_fail (is->priv->fetch_changes_infos == infos, FALSE);
 
-static void
-imapx_server_mailbox_select (CamelIMAPXServer *is,
-                             CamelIMAPXMailbox *mailbox)
-{
-	imapx_server_add_job_mailbox (is, mailbox);
+	is->priv->fetch_changes_mailbox = NULL;
+	is->priv->fetch_changes_folder = NULL;
+	is->priv->fetch_changes_infos = NULL;
+
+	g_slist_free_full (fetch_summary_uids, (GDestroyNotify) camel_pstring_free);
+	g_hash_table_destroy (infos);
 
-	e (
-		is->tagprefix,
-		"%s::mailbox-select (\"%s\")\n",
-		G_OBJECT_TYPE_NAME (is),
-		camel_imapx_mailbox_get_name (mailbox));
+	return success;
 }
 
-static void
-imapx_server_mailbox_closed (CamelIMAPXServer *is,
-                             CamelIMAPXMailbox *mailbox)
+gboolean
+camel_imapx_server_refresh_info_sync (CamelIMAPXServer *is,
+				      CamelIMAPXMailbox *mailbox,
+				      GCancellable *cancellable,
+				      GError **error)
 {
-	imapx_server_remove_job_mailbox (is, mailbox);
+	CamelIMAPXCommand *ic;
+	CamelIMAPXMailbox *selected_mailbox;
+	CamelIMAPXSummary *imapx_summary;
+	CamelFolder *folder;
+	GHashTable *known_uids;
+	guint32 messages;
+	guint32 unseen;
+	guint32 uidnext;
+	guint32 uidvalidity;
+	guint64 highestmodseq;
+	guint32 total;
+	guint64 uidl;
+	gboolean need_rescan;
+	gboolean success;
 
-	e (
-		is->tagprefix,
-		"%s::mailbox-closed (\"%s\")\n",
-		G_OBJECT_TYPE_NAME (is),
-		camel_imapx_mailbox_get_name (mailbox));
-}
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
 
-static void
-camel_imapx_server_class_init (CamelIMAPXServerClass *class)
-{
-	GObjectClass *object_class;
+	selected_mailbox = camel_imapx_server_ref_pending_or_selected (is);
+	if (selected_mailbox == mailbox) {
+		success = camel_imapx_server_noop_sync (is, mailbox, cancellable, error);
+	} else {
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_STATUS, "STATUS %M (%t)", mailbox, is->priv->status_data_items);
 
-	g_type_class_add_private (class, sizeof (CamelIMAPXServerPrivate));
+		success = camel_imapx_server_process_command_sync (is, ic, _("Error running STATUS"), cancellable, error);
 
-	object_class = G_OBJECT_CLASS (class);
-	object_class->set_property = imapx_server_set_property;
-	object_class->get_property = imapx_server_get_property;
-	object_class->finalize = imapx_server_finalize;
-	object_class->dispose = imapx_server_dispose;
-	object_class->constructed = imapx_server_constructed;
+		camel_imapx_command_unref (ic);
+	}
+	g_clear_object (&selected_mailbox);
 
-	class->mailbox_select = imapx_server_mailbox_select;
-	class->mailbox_closed = imapx_server_mailbox_closed;
+	if (!success)
+		return FALSE;
 
-	g_object_class_install_property (
-		object_class,
-		PROP_STORE,
-		g_param_spec_object (
-			"store",
-			"Store",
-			"IMAPX store for this server",
-			CAMEL_TYPE_IMAPX_STORE,
-			G_PARAM_READWRITE |
-			G_PARAM_CONSTRUCT_ONLY |
-			G_PARAM_STATIC_STRINGS));
+	folder = imapx_server_ref_folder (is, mailbox);
+	g_return_val_if_fail (folder != NULL, FALSE);
 
-	signals[MAILBOX_SELECT] = g_signal_new (
-		"mailbox-select",
-		G_OBJECT_CLASS_TYPE (class),
-		G_SIGNAL_RUN_LAST,
-		G_STRUCT_OFFSET (CamelIMAPXServerClass, mailbox_select),
-		NULL, NULL, NULL,
-		G_TYPE_NONE, 1,
-		CAMEL_TYPE_IMAPX_MAILBOX);
+	imapx_summary = CAMEL_IMAPX_SUMMARY (folder->summary);
 
-	signals[MAILBOX_CLOSED] = g_signal_new (
-		"mailbox-closed",
-		G_OBJECT_CLASS_TYPE (class),
-		G_SIGNAL_RUN_LAST,
-		G_STRUCT_OFFSET (CamelIMAPXServerClass, mailbox_closed),
-		NULL, NULL, NULL,
-		G_TYPE_NONE, 1,
-		CAMEL_TYPE_IMAPX_MAILBOX);
+	messages = camel_imapx_mailbox_get_messages (mailbox);
+	unseen = camel_imapx_mailbox_get_unseen (mailbox);
+	uidnext = camel_imapx_mailbox_get_uidnext (mailbox);
+	uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox);
+	highestmodseq = camel_imapx_mailbox_get_highestmodseq (mailbox);
+	total = camel_folder_summary_count (folder->summary);
 
-	/**
-	 * CamelIMAPXServer::shutdown
-	 * @server: the #CamelIMAPXServer which emitted the signal
-	 * @error: a #GError, which caused the shutdown; can be %NULL
-	 **/
-	signals[SHUTDOWN] = g_signal_new (
-		"shutdown",
-		G_OBJECT_CLASS_TYPE (class),
-		G_SIGNAL_RUN_FIRST,
-		G_STRUCT_OFFSET (CamelIMAPXServerClass, shutdown),
-		NULL, NULL,
-		g_cclosure_marshal_VOID__BOXED,
-		G_TYPE_NONE, 1, G_TYPE_ERROR);
-}
+	need_rescan =
+		(uidvalidity > 0 && uidvalidity != imapx_summary->validity) ||
+		total != messages ||
+		imapx_summary->uidnext != uidnext ||
+		camel_folder_summary_get_unread_count (folder->summary) != unseen ||
+		imapx_summary->modseq != highestmodseq;
 
-static void
-camel_imapx_server_init (CamelIMAPXServer *is)
-{
-	GMainContext *main_context;
+	if (!need_rescan) {
+		g_object_unref (folder);
+		return TRUE;
+	}
 
-	is->priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is);
+	if (!camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error)) {
+		g_object_unref (folder);
+		return FALSE;
+	}
 
-	is->priv->untagged_handlers = create_initial_untagged_handler_table ();
+	if (is->priv->use_qresync && imapx_summary->modseq > 0 && uidvalidity > 0) {
+		imapx_summary->modseq = highestmodseq;
+		if (total != messages ||
+		    camel_folder_summary_get_unread_count (folder->summary) != unseen ||
+		    imapx_summary->modseq != highestmodseq) {
+			c (
+				is->priv->tagprefix,
+				"Eep, after QRESYNC we're out of sync. "
+				"total %u / %u, unread %u / %u, modseq %"
+				G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT "\n",
+				total, messages,
+				camel_folder_summary_get_unread_count (folder->summary),
+				unseen,
+				imapx_summary->modseq,
+				highestmodseq);
+		} else {
+			c (
+				is->priv->tagprefix,
+				"OK, after QRESYNC we're still in sync. "
+				"total %u / %u, unread %u / %u, modseq %"
+				G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT "\n",
+				total, messages,
+				camel_folder_summary_get_unread_count (folder->summary),
+				unseen,
+				imapx_summary->modseq,
+				highestmodseq);
+			g_object_unref (folder);
+			return TRUE;
+		}
+	}
 
-	g_mutex_init (&is->priv->stream_lock);
-	g_mutex_init (&is->priv->inactivity_timeout_lock);
-	g_mutex_init (&is->priv->select_lock);
-	g_mutex_init (&is->priv->search_results_lock);
-	g_mutex_init (&is->priv->known_alerts_lock);
-	g_mutex_init (&is->priv->jobs_prop_lock);
-	g_mutex_init (&is->priv->shutdown_error_lock);
+	if (total > 0) {
+		gchar *uid = camel_imapx_dup_uid_from_summary_index (folder, total - 1);
+		if (uid) {
+			uidl = g_ascii_strtoull (uid, NULL, 10);
+			g_free (uid);
+			uidl++;
+		} else {
+			uidl = 1;
+		}
+	} else {
+		uidl = 1;
+	}
 
-	g_weak_ref_init (&is->priv->store, NULL);
-	g_weak_ref_init (&is->priv->parser_cancellable, NULL);
-	g_weak_ref_init (&is->priv->select_mailbox, NULL);
-	g_weak_ref_init (&is->priv->select_closing, NULL);
-	g_weak_ref_init (&is->priv->select_pending, NULL);
+	camel_folder_summary_prepare_fetch_all (folder->summary, NULL);
 
-	is->priv->jobs_prop_folder_paths = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-	is->priv->jobs_prop_command_count = 0;
-	is->priv->jobs_prop_expensive_command_count = 0;
+	known_uids = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL);
 
-	is->queue = camel_imapx_command_queue_new ();
-	is->active = camel_imapx_command_queue_new ();
-	is->done = camel_imapx_command_queue_new ();
+	success = imapx_server_fetch_changes (is, mailbox, folder, known_uids, uidl, 0, cancellable, error);
+	if (success && uidl != 1)
+		success = imapx_server_fetch_changes (is, mailbox, folder, known_uids, 0, uidl, cancellable, error);
 
-	g_queue_init (&is->jobs);
+	if (success) {
+		CamelFolderChangeInfo *changes;
+		GList *removed = NULL;
+		GPtrArray *array;
+		gint ii;
 
-	g_rec_mutex_init (&is->queue_lock);
+		camel_folder_summary_lock (folder->summary);
 
-	is->state = IMAPX_DISCONNECTED;
+		changes = camel_folder_change_info_new ();
 
-	is->priv->changes = camel_folder_change_info_new ();
+		array = camel_folder_summary_get_array (folder->summary);
+		for (ii = 0; array && ii < array->len; ii++) {
+			const gchar *uid = array->pdata[ii];
 
-	is->priv->known_alerts = g_hash_table_new_full (
-		(GHashFunc) g_str_hash,
-		(GEqualFunc) g_str_equal,
-		(GDestroyNotify) g_free,
-		(GDestroyNotify) NULL);
+			if (!uid)
+				continue;
 
-	/* Initialize parser thread structs. */
+			if (!g_hash_table_contains (known_uids, uid)) {
+				removed = g_list_prepend (removed, (gpointer) uid);
+				camel_folder_change_info_remove_uid (changes, uid);
+			}
+		}
 
-	main_context = g_main_context_new ();
+		camel_folder_summary_unlock (folder->summary);
 
-	is->priv->parser_main_loop = g_main_loop_new (main_context, FALSE);
-	is->priv->parser_main_context = g_main_context_ref (main_context);
-	is->priv->shutdown_error = NULL;
+		if (removed != NULL) {
+			camel_folder_summary_remove_uids (folder->summary, removed);
+			camel_folder_summary_touch (folder->summary);
 
-	g_main_context_unref (main_context);
+			/* Shares UIDs with the 'array'. */
+			g_list_free (removed);
+		}
 
-	/* Initialize IDLE thread structs. */
+		if (camel_folder_change_info_changed (changes)) {
+			camel_folder_summary_save_to_db (folder->summary, NULL);
+			imapx_update_store_summary (folder);
+			camel_folder_changed (folder, changes);
+		}
 
-	main_context = g_main_context_new ();
+		camel_folder_change_info_free (changes);
+		camel_folder_summary_free_array (array);
+	}
 
-	g_rec_mutex_init (&is->priv->idle_lock);
-	is->priv->idle_main_loop = g_main_loop_new (main_context, FALSE);
-	is->priv->idle_main_context = g_main_context_ref (main_context);
+	g_hash_table_destroy (known_uids);
+	g_object_unref (folder);
 
-	g_main_context_unref (main_context);
+	return success;
 }
 
-CamelIMAPXServer *
-camel_imapx_server_new (CamelIMAPXStore *store)
+static void
+imapx_sync_free_user (GArray *user_set)
 {
-	g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), NULL);
+	gint i;
 
-	return g_object_new (
-		CAMEL_TYPE_IMAPX_SERVER,
-		"store", store, NULL);
-}
+	if (user_set == NULL)
+		return;
 
-CamelIMAPXStore *
-camel_imapx_server_ref_store (CamelIMAPXServer *server)
-{
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), NULL);
+	for (i = 0; i < user_set->len; i++) {
+		struct _imapx_flag_change *flag_change = &g_array_index (user_set, struct _imapx_flag_change, i);
+		GPtrArray *infos = flag_change->infos;
+		gint j;
 
-	return g_weak_ref_get (&server->priv->store);
+		for (j = 0; j < infos->len; j++) {
+			CamelMessageInfo *info = g_ptr_array_index (infos, j);
+			camel_message_info_unref (info);
+		}
+
+		g_ptr_array_free (infos, TRUE);
+		g_free (flag_change->name);
+	}
+	g_array_free (user_set, TRUE);
 }
 
-CamelIMAPXSettings *
-camel_imapx_server_ref_settings (CamelIMAPXServer *server)
+static void
+imapx_unset_folder_flagged_flag (CamelFolderSummary *summary,
+				 GPtrArray *changed_uids,
+				 gboolean except_deleted_messages)
 {
-	CamelIMAPXStore *store;
-	CamelSettings *settings;
+	CamelMessageInfo *info;
+	gboolean changed = FALSE;
+	gint ii;
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), NULL);
+	g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
+	g_return_if_fail (changed_uids != NULL);
 
-	store = camel_imapx_server_ref_store (server);
-	settings = camel_service_ref_settings (CAMEL_SERVICE (store));
-	g_object_unref (store);
+	for (ii = 0; ii < changed_uids->len; ii++) {
+		info = camel_folder_summary_get (summary, changed_uids->pdata[ii]);
 
-	return CAMEL_IMAPX_SETTINGS (settings);
+		if (info) {
+			CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
+
+			/* some infos could be only 'dirty' (needed to save into summary) */
+			if ((mi->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0 &&
+			   (!except_deleted_messages || (mi->flags & CAMEL_MESSAGE_DELETED) == 0)) {
+				mi->flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED;
+				mi->dirty = TRUE;
+				changed = TRUE;
+			}
+
+			camel_message_info_unref (info);
+		}
+	}
+
+	if (changed) {
+		camel_folder_summary_touch (summary);
+		camel_folder_summary_save_to_db (summary, NULL);
+	}
 }
 
-/**
- * camel_imapx_server_ref_input_stream:
- * @is: a #CamelIMAPXServer
- *
- * Returns the #GInputStream for @is, which is owned by either a
- * #GTcpConnection or a #GSubprocess.  If the #CamelIMAPXServer is not
- * yet connected or has lost its connection, the function returns %NULL.
- *
- * The returned #GInputStream is referenced for thread-safety and must
- * be unreferenced with g_object_unref() when finished with it.
- *
- * Returns: a #GInputStream, or %NULL
- *
- * Since: 3.12
- **/
-GInputStream *
-camel_imapx_server_ref_input_stream (CamelIMAPXServer *is)
-{
-	GInputStream *input_stream = NULL;
+static void
+imapx_server_info_changed_cb (CamelIMAPXSummary *summary,
+			      CamelMessageInfo *info,
+			      gpointer user_data)
+{
+	GHashTable *changed_meanwhile = user_data;
+
+	g_return_if_fail (info != NULL);
+	g_return_if_fail (changed_meanwhile != NULL);
+
+	/* The UID can be NULL in case of a newly fetched message, for example when creating
+	   the message info in imapx_untagged_fetch() by camel_folder_summary_info_new_from_parser() */
+	if (camel_message_info_uid (info)) {
+		g_hash_table_insert (changed_meanwhile,
+			(gpointer) camel_pstring_strdup (camel_message_info_uid (info)),
+			GINT_TO_POINTER (1));
+	}
+}
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
+gboolean
+camel_imapx_server_sync_changes_sync (CamelIMAPXServer *is,
+				      CamelIMAPXMailbox *mailbox,
+				      gboolean can_influence_flags,
+				      GCancellable *cancellable,
+				      GError **error)
+{
+	guint i, jj, on, on_orset, off_orset;
+	GPtrArray *changed_uids;
+	GArray *on_user = NULL, *off_user = NULL;
+	CamelFolder *folder;
+	CamelIMAPXMessageInfo *info;
+	GHashTable *changed_meanwhile;
+	gulong changed_meanwhile_handler_id;
+	guint32 permanentflags;
+	struct _uidset_state uidset;
+	gint unread_change = 0;
+	gboolean use_real_junk_path = FALSE;
+	gboolean use_real_trash_path = FALSE;
+	gboolean remove_deleted_flags = FALSE;
+	gboolean nothing_to_do;
+	gboolean success;
 
-	g_mutex_lock (&is->priv->stream_lock);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
 
-	if (is->priv->input_stream != NULL)
-		input_stream = g_object_ref (is->priv->input_stream);
+	folder = imapx_server_ref_folder (is, mailbox);
+	g_return_val_if_fail (folder != NULL, FALSE);
 
-	g_mutex_unlock (&is->priv->stream_lock);
+	/* We calculate two masks, a mask of all flags which have been
+	 * turned off and a mask of all flags which have been turned
+	 * on. If either of these aren't 0, then we have work to do,
+	 * and we fire off a job to do it.
+	 *
+	 * User flags are a bit more tricky, we rely on the user
+	 * flags being sorted, and then we create a bunch of lists;
+	 * one for each flag being turned off, including each
+	 * info being turned off, and one for each flag being turned on.
+	 */
+	changed_uids = camel_folder_summary_get_changed (folder->summary);
 
-	return input_stream;
-}
+	if (changed_uids->len == 0) {
+		camel_folder_free_uids (folder, changed_uids);
+		g_object_unref (folder);
+		return TRUE;
+	}
 
-/**
- * camel_imapx_server_ref_output_stream:
- * @is: a #CamelIMAPXServer
- *
- * Returns the #GOutputStream for @is, which is owned by either a
- * #GTcpConnection or a #GSubprocess.  If the #CamelIMAPXServer is not
- * yet connected or has lost its connection, the function returns %NULL.
- *
- * The returned #GOutputStream is referenced for thread-safety and must
- * be unreferenced with g_object_unref() when finished with it.
- *
- * Returns: a #GOutputStream, or %NULL
- *
- * Since: 3.12
- **/
-GOutputStream *
-camel_imapx_server_ref_output_stream (CamelIMAPXServer *is)
-{
-	GOutputStream *output_stream = NULL;
+	changed_meanwhile = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL);
+	changed_meanwhile_handler_id = g_signal_connect (folder->summary, "info-changed",
+		G_CALLBACK (imapx_server_info_changed_cb), changed_meanwhile);
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
+	if (can_influence_flags) {
+		CamelIMAPXSettings *settings;
 
-	g_mutex_lock (&is->priv->stream_lock);
+		settings = camel_imapx_server_ref_settings (is);
+		use_real_junk_path = camel_imapx_settings_get_use_real_junk_path (settings);
+		use_real_trash_path = camel_imapx_settings_get_use_real_trash_path (settings);
+		if (use_real_trash_path) {
+			CamelFolder *trash_folder = NULL;
+			gchar *real_trash_path;
+
+			real_trash_path = camel_imapx_settings_dup_real_trash_path (settings);
+			if (real_trash_path)
+				trash_folder = camel_store_get_folder_sync (
+					camel_folder_get_parent_store (folder),
+					real_trash_path, 0, cancellable, NULL);
 
-	if (is->priv->output_stream != NULL)
-		output_stream = g_object_ref (is->priv->output_stream);
+			/* Remove deleted flags in all but the trash folder itself */
+			remove_deleted_flags = !trash_folder || trash_folder != folder;
 
-	g_mutex_unlock (&is->priv->stream_lock);
+			use_real_trash_path = trash_folder != NULL;
 
-	return output_stream;
-}
+			g_clear_object (&trash_folder);
+			g_free (real_trash_path);
+		}
+		g_object_unref (settings);
+	}
 
-/**
- * camel_imapx_server_ref_selected:
- * @is: a #CamelIMAPXServer
- *
- * Returns the #CamelIMAPXMailbox representing the currently selected
- * mailbox (or mailbox <emphasis>being</emphasis> selected if a SELECT
- * command is in progress) on the IMAP server, or %NULL if no mailbox
- * is currently selected or being selected on the server.
- *
- * The returned #CamelIMAPXMailbox is reference for thread-safety and
- * should be unreferenced with g_object_unref() when finished with it.
- *
- * Returns: a #CamelIMAPXMailbox, or %NULL
- *
- * Since: 3.12
- **/
-CamelIMAPXMailbox *
-camel_imapx_server_ref_selected (CamelIMAPXServer *is)
-{
-	CamelIMAPXMailbox *mailbox;
+	if (changed_uids->len > 20)
+		camel_folder_summary_prepare_fetch_all (folder->summary, NULL);
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
+	off_orset = on_orset = 0;
+	for (i = 0; i < changed_uids->len; i++) {
+		guint32 flags, sflags;
+		CamelFlag *uflags, *suflags;
+		const gchar *uid;
+		guint j = 0;
 
-	g_mutex_lock (&is->priv->select_lock);
+		uid = g_ptr_array_index (changed_uids, i);
 
-	mailbox = g_weak_ref_get (&is->priv->select_mailbox);
-	if (mailbox == NULL)
-		mailbox = g_weak_ref_get (&is->priv->select_closing);
-	if (mailbox == NULL)
-		mailbox = g_weak_ref_get (&is->priv->select_pending);
+		info = (CamelIMAPXMessageInfo *)
+			camel_folder_summary_get (folder->summary, uid);
 
-	g_mutex_unlock (&is->priv->select_lock);
+		if (info == NULL)
+			continue;
 
-	return mailbox;
-}
+		if (!(info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED)) {
+			camel_message_info_unref (info);
+			continue;
+		}
 
-static void
-imapx_disconnect (CamelIMAPXServer *is)
-{
-	g_mutex_lock (&is->priv->stream_lock);
+		flags = info->info.flags & CAMEL_IMAPX_SERVER_FLAGS;
+		sflags = info->server_flags & CAMEL_IMAPX_SERVER_FLAGS;
 
-	g_clear_object (&is->priv->input_stream);
-	g_clear_object (&is->priv->output_stream);
-	g_clear_object (&is->priv->connection);
-#if GLIB_CHECK_VERSION(2,39,0)
-	g_clear_object (&is->priv->subprocess);
-#endif
+		if (can_influence_flags) {
+			gboolean move_to_real_junk;
+			gboolean move_to_real_trash;
+
+			move_to_real_junk =
+				use_real_junk_path &&
+				(flags & CAMEL_MESSAGE_JUNK);
+
+			move_to_real_trash =
+				use_real_trash_path && remove_deleted_flags &&
+				(flags & CAMEL_MESSAGE_DELETED);
+
+			if (move_to_real_junk)
+				camel_imapx_folder_add_move_to_real_junk (
+					CAMEL_IMAPX_FOLDER (folder), uid);
+
+			if (move_to_real_trash)
+				camel_imapx_folder_add_move_to_real_trash (
+					CAMEL_IMAPX_FOLDER (folder), uid);
+		}
 
-	g_mutex_unlock (&is->priv->stream_lock);
+		if (flags != sflags) {
+			off_orset |= (flags ^ sflags) & ~flags;
+			on_orset |= (flags ^ sflags) & flags;
+		}
 
-	g_mutex_lock (&is->priv->select_lock);
-	g_weak_ref_set (&is->priv->select_mailbox, NULL);
-	g_weak_ref_set (&is->priv->select_closing, NULL);
-	g_weak_ref_set (&is->priv->select_pending, NULL);
-	g_mutex_unlock (&is->priv->select_lock);
+		uflags = info->info.user_flags;
+		suflags = info->server_user_flags;
+		while (uflags || suflags) {
+			gint res;
 
-	if (is->cinfo) {
-		imapx_free_capability (is->cinfo);
-		is->cinfo = NULL;
-	}
+			if (uflags) {
+				if (suflags)
+					res = strcmp (uflags->name, suflags->name);
+				else if (*uflags->name)
+					res = -1;
+				else {
+					uflags = uflags->next;
+					continue;
+				}
+			} else {
+				res = 1;
+			}
 
-	is->state = IMAPX_DISCONNECTED;
-}
+			if (res == 0) {
+				uflags = uflags->next;
+				suflags = suflags->next;
+			} else {
+				GArray *user_set;
+				CamelFlag *user_flag;
+				struct _imapx_flag_change *change = NULL, add = { 0 };
 
-/* Client commands */
-gboolean
-camel_imapx_server_connect (CamelIMAPXServer *is,
-                            GCancellable *cancellable,
-                            GError **error)
-{
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+				if (res < 0) {
+					if (on_user == NULL)
+						on_user = g_array_new (FALSE, FALSE, sizeof (struct _imapx_flag_change));
+					user_set = on_user;
+					user_flag = uflags;
+					uflags = uflags->next;
+				} else {
+					if (off_user == NULL)
+						off_user = g_array_new (FALSE, FALSE, sizeof (struct _imapx_flag_change));
+					user_set = off_user;
+					user_flag = suflags;
+					suflags = suflags->next;
+				}
 
-	if (is->state == IMAPX_SHUTDOWN) {
-		g_set_error (
-			error, CAMEL_SERVICE_ERROR,
-			CAMEL_SERVICE_ERROR_UNAVAILABLE,
-			"Shutting down");
-		return FALSE;
+				/* Could sort this and binary search */
+				for (j = 0; j < user_set->len; j++) {
+					change = &g_array_index (user_set, struct _imapx_flag_change, j);
+					if (strcmp (change->name, user_flag->name) == 0)
+						goto found;
+				}
+				add.name = g_strdup (user_flag->name);
+				add.infos = g_ptr_array_new ();
+				g_array_append_val (user_set, add);
+				change = &add;
+			found:
+				camel_message_info_ref (info);
+				g_ptr_array_add (change->infos, info);
+			}
+		}
+
+		camel_message_info_unref (info);
 	}
 
-	if (is->state >= IMAPX_INITIALISED)
-		return TRUE;
+	nothing_to_do =
+		(on_orset == 0) &&
+		(off_orset == 0) &&
+		(on_user == NULL) &&
+		(off_user == NULL);
 
-	if (!imapx_reconnect (is, cancellable, error))
-		return FALSE;
+	if (nothing_to_do) {
+		g_signal_handler_disconnect (folder->summary, changed_meanwhile_handler_id);
+
+		imapx_sync_free_user (on_user);
+		imapx_sync_free_user (off_user);
+		imapx_unset_folder_flagged_flag (folder->summary, changed_uids, remove_deleted_flags);
+		camel_folder_free_uids (folder, changed_uids);
+		g_hash_table_destroy (changed_meanwhile);
+		g_object_unref (folder);
+		return TRUE;
+	}
 
-	is->priv->parser_thread = g_thread_new (
-		NULL, imapx_parser_thread, g_object_ref (is));
+	if (!camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error)) {
+		g_signal_handler_disconnect (folder->summary, changed_meanwhile_handler_id);
 
-	if (CAMEL_IMAPX_LACK_CAPABILITY (is->cinfo, NAMESPACE)) {
-		/* This also creates a needed faux NAMESPACE */
-		if (!camel_imapx_server_list (is, "INBOX", 0, cancellable, error))
-			return FALSE;
+		imapx_sync_free_user (on_user);
+		imapx_sync_free_user (off_user);
+		camel_folder_free_uids (folder, changed_uids);
+		g_hash_table_destroy (changed_meanwhile);
+		g_object_unref (folder);
+		return FALSE;
 	}
 
-	return TRUE;
-}
+	permanentflags = camel_imapx_mailbox_get_permanentflags (mailbox);
 
-static CamelStream *
-imapx_server_get_message (CamelIMAPXServer *is,
-                          CamelIMAPXMailbox *mailbox,
-                          CamelFolderSummary *summary,
-                          CamelDataCache *message_cache,
-                          const gchar *message_uid,
-                          gint pri,
-                          GCancellable *cancellable,
-                          GError **error)
-{
-	CamelStream *stream = NULL;
-	CamelIMAPXJob *job;
-	CamelMessageInfo *mi;
-	GIOStream *cache_stream;
-	GetMessageData *data;
-	gboolean registered;
+	success = TRUE;
+	for (on = 0; on < 2 && success; on++) {
+		guint32 orset = on ? on_orset : off_orset;
+		GArray *user_set = on ? on_user : off_user;
 
-	while (job = imapx_server_ref_job (is, mailbox, IMAPX_JOB_GET_MESSAGE, message_uid), job != NULL) {
-		/* Promote the existing GET_MESSAGE
-		 * job's priority if ours is higher. */
-		if (pri > job->pri)
-			job->pri = pri;
-
-		/* Wait for the job to finish. */
-		camel_imapx_job_wait (job, NULL);
-		camel_imapx_job_unref (job);
-
-		/* Disregard errors here.  If we failed to retreive the
-		 * message from cache (implying the job we were waiting
-		 * on failed or got cancelled), we'll just re-fetch it. */
-		cache_stream = camel_data_cache_get (
-			message_cache, "cur", message_uid, NULL);
-		if (cache_stream != NULL) {
-			/* Return new file stream, instead of a DataCache's to not fight
-			   on its content and position with other jobs, if any. */
-			gchar *filename = camel_data_cache_get_filename (message_cache, "cur", message_uid);
-			stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0, NULL);
-			g_free (filename);
-			g_object_unref (cache_stream);
-
-			if (stream)
-				return stream;
-		}
-	}
+		for (jj = 0; jj < G_N_ELEMENTS (flags_table) && success; jj++) {
+			guint32 flag = flags_table[jj].flag;
+			CamelIMAPXCommand *ic = NULL;
 
-	QUEUE_LOCK (is);
+			if ((orset & flag) == 0)
+				continue;
 
-	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
-		QUEUE_UNLOCK (is);
+			c (is->priv->tagprefix, "checking/storing %s flags '%s'\n", on ? "on" : "off", flags_table[jj].name);
+			imapx_uidset_init (&uidset, 0, 100);
+			for (i = 0; i < changed_uids->len && success; i++) {
+				CamelIMAPXMessageInfo *info;
+				gboolean remove_deleted_flag;
+				guint32 flags;
+				guint32 sflags;
+				gint send;
 
-		return NULL;
-	}
+				info = (CamelIMAPXMessageInfo *)
+					camel_folder_summary_get (
+						folder->summary,
+						changed_uids->pdata[i]);
 
-	mi = camel_folder_summary_get (summary, message_uid);
-	if (mi == NULL) {
-		g_set_error (
-			error, CAMEL_FOLDER_ERROR,
-			CAMEL_FOLDER_ERROR_INVALID_UID,
-			_("Cannot get message with message ID %s: %s"),
-			message_uid, _("No such message available."));
-		QUEUE_UNLOCK (is);
-		return NULL;
-	}
+				if (info == NULL)
+					continue;
 
-	/* This makes sure that if any file is left on the disk, it is not reused.
-	   That can happen when the previous message download had been cancelled
-	   or finished with an error. */
-	camel_data_cache_remove (message_cache, "tmp", message_uid, NULL);
+				flags = (info->info.flags & CAMEL_IMAPX_SERVER_FLAGS) & permanentflags;
+				sflags = (info->server_flags & CAMEL_IMAPX_SERVER_FLAGS) & permanentflags;
+				send = 0;
 
-	cache_stream = camel_data_cache_add (message_cache, "tmp", message_uid, error);
-	if (cache_stream == NULL) {
-		QUEUE_UNLOCK (is);
-		return NULL;
-	}
+				remove_deleted_flag =
+					remove_deleted_flags &&
+					(flags & CAMEL_MESSAGE_DELETED);
+
+				if (remove_deleted_flag) {
+					/* Remove the DELETED flag so the
+					 * message appears normally in the
+					 * real Trash folder when copied. */
+					flags &= ~CAMEL_MESSAGE_DELETED;
+				}
 
-	data = g_slice_new0 (GetMessageData);
-	data->uid = g_strdup (message_uid);
-	data->message_cache = g_object_ref (message_cache);
-	data->stream = g_object_ref (cache_stream);
-	data->size = ((CamelMessageInfoBase *) mi)->size;
-	if (data->size > MULTI_SIZE)
-		data->use_multi_fetch = TRUE;
-
-	job = camel_imapx_job_new (cancellable);
-	job->pri = pri;
-	job->type = IMAPX_JOB_GET_MESSAGE;
-	job->start = imapx_job_get_message_start;
-	job->matches = imapx_job_get_message_matches;
+				if ( (on && (((flags ^ sflags) & flags) & flag))
+				     || (!on && (((flags ^ sflags) & ~flags) & flag))) {
+					if (ic == NULL) {
+						ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_SYNC_CHANGES, "UID STORE ");
+					}
+					send = imapx_uidset_add (&uidset, ic, camel_message_info_uid (info));
+				}
+				if (send == 1 || (i == changed_uids->len - 1 && ic && imapx_uidset_done (&uidset, ic))) {
+					camel_imapx_command_add (ic, " %tFLAGS.SILENT (%t)", on ? "+" : "-", flags_table[jj].name);
 
-	camel_imapx_job_set_mailbox (job, mailbox);
+					success = camel_imapx_server_process_command_sync (is, ic, _("Error syncing changes"), cancellable, error);
 
-	camel_imapx_job_set_data (
-		job, data, (GDestroyNotify) get_message_data_free);
+					camel_imapx_command_unref (ic);
+					ic = NULL;
 
-	g_clear_object (&cache_stream);
-	camel_message_info_unref (mi);
+					if (!success)
+						break;
+				}
+				if (flag == CAMEL_MESSAGE_SEEN) {
+					/* Remember how the server's unread count will change if this
+					 * command succeeds */
+					if (on)
+						unread_change--;
+					else
+						unread_change++;
+				}
 
-	registered = imapx_register_job (is, job, error);
+				/* The second round and the server doesn't support saving user flags,
+				   thus store them at least locally */
+				if (on && (permanentflags & CAMEL_MESSAGE_USER) == 0) {
+					camel_flag_list_copy (&info->server_user_flags, &info->info.user_flags);
+				}
 
-	QUEUE_UNLOCK (is);
+				camel_message_info_unref (info);
+			}
 
-	if (registered && camel_imapx_job_run (job, is, error))
-		stream = camel_stream_new (data->stream);
-	else if (registered)
-		imapx_unregister_job (is, job);
+			g_warn_if_fail (ic == NULL);
+		}
 
-	camel_imapx_job_unref (job);
+		if (user_set && (permanentflags & CAMEL_MESSAGE_USER) != 0 && success) {
+			CamelIMAPXCommand *ic = NULL;
 
-	return stream;
-}
+			for (jj = 0; jj < user_set->len && success; jj++) {
+				struct _imapx_flag_change *c = &g_array_index (user_set, struct _imapx_flag_change, jj);
 
-CamelStream *
-camel_imapx_server_get_message (CamelIMAPXServer *is,
-                                CamelIMAPXMailbox *mailbox,
-                                CamelFolderSummary *summary,
-                                CamelDataCache *message_cache,
-                                const gchar *message_uid,
-                                GCancellable *cancellable,
-                                GError **error)
-{
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), NULL);
-	g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
-	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), NULL);
-	g_return_val_if_fail (message_uid != NULL, NULL);
+				imapx_uidset_init (&uidset, 0, 100);
+				for (i = 0; i < c->infos->len; i++) {
+					CamelIMAPXMessageInfo *info = c->infos->pdata[i];
 
-	return imapx_server_get_message (
-		is, mailbox, summary,
-		message_cache, message_uid,
-		IMAPX_PRIORITY_GET_MESSAGE,
-		cancellable, error);
-}
+					if (ic == NULL)
+						ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_SYNC_CHANGES, "UID STORE ");
 
-gboolean
-camel_imapx_server_sync_message (CamelIMAPXServer *is,
-                                 CamelIMAPXMailbox *mailbox,
-                                 CamelFolderSummary *summary,
-                                 CamelDataCache *message_cache,
-                                 const gchar *message_uid,
-                                 GCancellable *cancellable,
-                                 GError **error)
-{
-	gchar *cache_file = NULL;
-	gboolean is_cached;
-	struct stat st;
-	gboolean success = TRUE;
+					if (imapx_uidset_add (&uidset, ic, camel_message_info_uid (info)) == 1
+					    || (i == c->infos->len - 1 && imapx_uidset_done (&uidset, ic))) {
+						gchar *utf7;
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
-	g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
-	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), FALSE);
-	g_return_val_if_fail (message_uid != NULL, FALSE);
+						utf7 = camel_utf8_utf7 (c->name);
 
-	/* Check if the cache file already exists and is non-empty. */
-	cache_file = camel_data_cache_get_filename (
-		message_cache, "cur", message_uid);
-	is_cached = (g_stat (cache_file, &st) == 0 && st.st_size > 0);
-	g_free (cache_file);
+						camel_imapx_command_add (ic, " %tFLAGS.SILENT (%t)", on ? "+" : "-", utf7 ? utf7 : c->name);
 
-	if (!is_cached) {
-		CamelStream *stream;
+						g_free (utf7);
 
-		stream = imapx_server_get_message (
-			is, mailbox, summary,
-			message_cache, message_uid,
-			IMAPX_PRIORITY_SYNC_MESSAGE,
-			cancellable, error);
+						success = camel_imapx_server_process_command_sync (is, ic, _("Error syncing changes"), cancellable, error);
 
-		success = (stream != NULL);
+						camel_imapx_command_unref (ic);
+						ic = NULL;
 
-		g_clear_object (&stream);
+						if (!success)
+							break;
+					}
+				}
+			}
+		}
 	}
 
-	return success;
-}
+	g_signal_handler_disconnect (folder->summary, changed_meanwhile_handler_id);
+
+	if (success) {
+		CamelStore *parent_store;
+		guint32 unseen;
+
+		parent_store = camel_folder_get_parent_store (folder);
 
-gboolean
-camel_imapx_server_copy_message (CamelIMAPXServer *is,
-                                 CamelIMAPXMailbox *mailbox,
-                                 CamelIMAPXMailbox *destination,
-                                 GPtrArray *uids,
-                                 gboolean delete_originals,
-                                 GCancellable *cancellable,
-                                 GError **error)
-{
-	CamelIMAPXJob *job;
-	CopyMessagesData *data;
-	gint ii;
-	gboolean success;
+		camel_folder_summary_lock (folder->summary);
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (destination), FALSE);
-	g_return_val_if_fail (uids != NULL, FALSE);
+		for (i = 0; i < changed_uids->len; i++) {
+			CamelIMAPXMessageInfo *xinfo = (CamelIMAPXMessageInfo *) camel_folder_summary_get (folder->summary,
+					changed_uids->pdata[i]);
 
-	/* That's okay if the "SELECT" fails here, as it can be due to
-	   the folder being write-only; just ignore the error and continue. */
-	imapx_ensure_mailbox_permanentflags (is, destination, cancellable, NULL);
+			if (!xinfo)
+				continue;
 
-	if (g_cancellable_set_error_if_cancelled (cancellable, error))
-		return FALSE;
+			xinfo->server_flags = xinfo->info.flags & CAMEL_IMAPX_SERVER_FLAGS;
+			if (!remove_deleted_flags ||
+			    !(xinfo->info.flags & CAMEL_MESSAGE_DELETED)) {
+				xinfo->info.flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED;
+			} else {
+				/* to stare back the \Deleted flag */
+				xinfo->server_flags &= ~CAMEL_MESSAGE_DELETED;
+				xinfo->info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
+			}
+			xinfo->info.dirty = TRUE;
+			if ((permanentflags & CAMEL_MESSAGE_USER) != 0 ||
+			    camel_flag_list_size (&xinfo->server_user_flags) == 0)
+				camel_flag_list_copy (&xinfo->server_user_flags, &xinfo->info.user_flags);
 
-	data = g_slice_new0 (CopyMessagesData);
-	data->destination = g_object_ref (destination);
-	data->uids = g_ptr_array_new ();
-	data->delete_originals = delete_originals;
+			if (g_hash_table_lookup (changed_meanwhile, changed_uids->pdata[i]))
+				xinfo->info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
 
-	/* If we're moving messages, prefer "UID MOVE" if supported. */
-	if (data->delete_originals) {
-		if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, MOVE)) {
-			data->delete_originals = FALSE;
-			data->use_move_command = TRUE;
+			camel_folder_summary_touch (folder->summary);
+			camel_message_info_unref (xinfo);
 		}
-	}
 
-	for (ii = 0; ii < uids->len; ii++)
-		g_ptr_array_add (data->uids, g_strdup (uids->pdata[ii]));
+		camel_folder_summary_unlock (folder->summary);
+
+		/* Apply the changes to server-side unread count; it won't tell
+		 * us of these changes, of course. */
+		unseen = camel_imapx_mailbox_get_unseen (mailbox);
+		unseen += unread_change;
+		camel_imapx_mailbox_set_unseen (mailbox, unseen);
 
-	job = camel_imapx_job_new (cancellable);
-	job->pri = IMAPX_PRIORITY_COPY_MESSAGE;
-	job->type = IMAPX_JOB_COPY_MESSAGE;
-	job->start = imapx_job_copy_messages_start;
-	job->matches = imapx_job_copy_messages_matches;
+		if (folder->summary && (folder->summary->flags & CAMEL_FOLDER_SUMMARY_DIRTY) != 0) {
+			CamelStoreInfo *si;
 
-	camel_imapx_job_set_mailbox (job, mailbox);
+			/* ... and store's summary when folder's summary is dirty */
+			si = camel_store_summary_path (CAMEL_IMAPX_STORE (parent_store)->summary, camel_folder_get_full_name (folder));
+			if (si) {
+				if (si->total != camel_folder_summary_get_saved_count (folder->summary) ||
+				    si->unread != camel_folder_summary_get_unread_count (folder->summary)) {
+					si->total = camel_folder_summary_get_saved_count (folder->summary);
+					si->unread = camel_folder_summary_get_unread_count (folder->summary);
+					camel_store_summary_touch (CAMEL_IMAPX_STORE (parent_store)->summary);
+				}
 
-	camel_imapx_job_set_data (
-		job, data, (GDestroyNotify) copy_messages_data_free);
+				camel_store_summary_info_unref (CAMEL_IMAPX_STORE (parent_store)->summary, si);
+			}
+		}
 
-	success = imapx_submit_job (is, job, error);
+		camel_folder_summary_save_to_db (folder->summary, NULL);
+		camel_store_summary_save (CAMEL_IMAPX_STORE (parent_store)->summary);
+	}
 
-	camel_imapx_job_unref (job);
+	imapx_sync_free_user (on_user);
+	imapx_sync_free_user (off_user);
+	camel_folder_free_uids (folder, changed_uids);
+	g_hash_table_destroy (changed_meanwhile);
+	g_object_unref (folder);
 
 	return success;
 }
 
 gboolean
-camel_imapx_server_append_message (CamelIMAPXServer *is,
-                                   CamelIMAPXMailbox *mailbox,
-                                   CamelFolderSummary *summary,
-                                   CamelDataCache *message_cache,
-                                   CamelMimeMessage *message,
-                                   const CamelMessageInfo *mi,
-                                   gchar **appended_uid,
-                                   GCancellable *cancellable,
-                                   GError **error)
+camel_imapx_server_expunge_sync (CamelIMAPXServer *is,
+				 CamelIMAPXMailbox *mailbox,
+				 GCancellable *cancellable,
+				 GError **error)
 {
-	gchar *uid = NULL, *path = NULL;
-	CamelMimeFilter *filter;
-	CamelIMAPXJob *job;
-	CamelMessageInfo *info;
-	GIOStream *base_stream;
-	GOutputStream *output_stream;
-	GOutputStream *filter_stream;
-	AppendMessageData *data;
-	gint res;
-	time_t date_time;
+	CamelFolder *folder;
 	gboolean success;
 
 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
 	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
-	g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
-	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), FALSE);
-	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
-	/* CamelMessageInfo can be NULL. */
 
-	/* That's okay if the "SELECT" fails here, as it can be due to
-	   the folder being write-only; just ignore the error and continue. */
-	imapx_ensure_mailbox_permanentflags (is, mailbox, cancellable, NULL);
+	folder = imapx_server_ref_folder (is, mailbox);
+	g_return_val_if_fail (folder != NULL, FALSE);
 
-	if (g_cancellable_set_error_if_cancelled (cancellable, error))
-		return FALSE;
+	success = camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error);
 
-	/* Append just assumes we have no/a dodgy connection.  We dump
-	 * stuff into the 'new' directory, and let the summary know it's
-	 * there.  Then we fire off a no-reply job which will asynchronously
-	 * upload the message at some point in the future, and fix up the
-	 * summary to match */
+	if (success) {
+		CamelIMAPXCommand *ic;
 
-	/* chen cleanup this later */
-	uid = imapx_get_temp_uid ();
-	base_stream = camel_data_cache_add (message_cache, "new", uid, error);
-	if (base_stream == NULL) {
-		g_prefix_error (error, _("Cannot create spool file: "));
-		g_free (uid);
-		return FALSE;
-	}
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_EXPUNGE, "EXPUNGE");
 
-	output_stream = g_io_stream_get_output_stream (base_stream);
-	filter = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_CRLF);
-	filter_stream = camel_filter_output_stream_new (output_stream, filter);
+		success = camel_imapx_server_process_command_sync (is, ic, _("Error expunging message"), cancellable, error);
+		if (success) {
+			GPtrArray *uids;
+			CamelStore *parent_store;
+			const gchar *full_name;
 
-	g_filter_output_stream_set_close_base_stream (
-		G_FILTER_OUTPUT_STREAM (filter_stream), FALSE);
+			full_name = camel_folder_get_full_name (folder);
+			parent_store = camel_folder_get_parent_store (folder);
 
-	res = camel_data_wrapper_write_to_output_stream_sync (
-		CAMEL_DATA_WRAPPER (message),
-		filter_stream, cancellable, error);
+			camel_folder_summary_lock (folder->summary);
 
-	g_object_unref (base_stream);
-	g_object_unref (filter_stream);
-	g_object_unref (filter);
+			camel_folder_summary_save_to_db (folder->summary, NULL);
+			uids = camel_db_get_folder_deleted_uids (parent_store->cdb_r, full_name, NULL);
 
-	if (res == -1) {
-		g_prefix_error (error, _("Cannot create spool file: "));
-		camel_data_cache_remove (message_cache, "new", uid, NULL);
-		g_free (uid);
-		return FALSE;
-	}
+			if (uids && uids->len) {
+				CamelFolderChangeInfo *changes;
+				GList *removed = NULL;
+				gint i;
+
+				changes = camel_folder_change_info_new ();
+				for (i = 0; i < uids->len; i++) {
+					camel_folder_change_info_remove_uid (changes, uids->pdata[i]);
+					removed = g_list_prepend (removed, (gpointer) uids->pdata[i]);
+				}
 
-	date_time = camel_mime_message_get_date (message, NULL);
-	path = camel_data_cache_get_filename (message_cache, "new", uid);
-	info = camel_folder_summary_info_new_from_message (
-		summary, message, NULL);
-	info->uid = camel_pstring_strdup (uid);
-	if (mi != NULL) {
-		CamelMessageInfoBase *base_info = (CamelMessageInfoBase *) info;
-		const CamelFlag *flag;
-		const CamelTag *tag;
+				camel_folder_summary_remove_uids (folder->summary, removed);
+				camel_folder_summary_save_to_db (folder->summary, NULL);
 
-		base_info->flags = camel_message_info_flags (mi);
-		base_info->size = camel_message_info_size (mi);
+				camel_folder_changed (folder, changes);
+				camel_folder_change_info_free (changes);
 
-		flag = camel_message_info_user_flags (mi);
-		while (flag != NULL) {
-			if (*flag->name != '\0')
-				camel_flag_set (
-					&base_info->user_flags,
-					flag->name, TRUE);
-			flag = flag->next;
-		}
+				g_list_free (removed);
+				g_ptr_array_foreach (uids, (GFunc) camel_pstring_free, NULL);
+			}
 
-		tag = camel_message_info_user_tags (mi);
-		while (tag != NULL) {
-			if (*tag->name != '\0')
-				camel_tag_set (
-					&base_info->user_tags,
-					tag->name, tag->value);
-			tag = tag->next;
+			if (uids)
+				g_ptr_array_free (uids, TRUE);
+
+			camel_folder_summary_unlock (folder->summary);
 		}
 
-		if (date_time <= 0)
-			date_time = camel_message_info_date_received (mi);
+		camel_imapx_command_unref (ic);
 	}
 
-	g_free (uid);
+	g_clear_object (&folder);
 
-	if (camel_mime_message_has_attachment (message))
-		((CamelMessageInfoBase *) info)->flags |= CAMEL_MESSAGE_ATTACHMENTS;
+	return success;
+}
+
+gboolean
+camel_imapx_server_list_sync (CamelIMAPXServer *is,
+			      const gchar *pattern,
+			      CamelStoreGetFolderInfoFlags flags,
+			      GCancellable *cancellable,
+			      GError **error)
+{
+	CamelIMAPXCommand *ic;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (pattern != NULL, FALSE);
 
-	/* So, we actually just want to let the server loop that
-	 * messages need appending, i think.  This is so the same
-	 * mechanism is used for normal uploading as well as
-	 * offline re-syncing when we go back online */
+	if (is->priv->list_return_opts != NULL) {
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LIST, "LIST \"\" %s RETURN (%t)",
+			pattern, is->priv->list_return_opts);
+	} else {
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LIST, "LIST \"\" %s",
+			pattern);
+	}
 
-	data = g_slice_new0 (AppendMessageData);
-	data->info = info;  /* takes ownership */
-	data->path = path;  /* takes ownership */
-	data->date_time = date_time;
-	data->appended_uid = NULL;
+	success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching folders"), cancellable, error);
 
-	job = camel_imapx_job_new (cancellable);
-	job->pri = IMAPX_PRIORITY_APPEND_MESSAGE;
-	job->type = IMAPX_JOB_APPEND_MESSAGE;
-	job->start = imapx_job_append_message_start;
-	job->noreply = FALSE;
+	camel_imapx_command_unref (ic);
 
-	camel_imapx_job_set_mailbox (job, mailbox);
+	if (!success)
+		return FALSE;
 
-	camel_imapx_job_set_data (
-		job, data, (GDestroyNotify) append_message_data_free);
+	if (!is->priv->list_return_opts) {
+		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LSUB, "LSUB \"\" %s",
+			pattern);
 
-	success = imapx_submit_job (is, job, error);
+		success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching subscribed folders"), cancellable, error);
 
-	if (appended_uid != NULL) {
-		*appended_uid = data->appended_uid;
-		data->appended_uid = NULL;
+		camel_imapx_command_unref (ic);
 	}
 
-	camel_imapx_job_unref (job);
-
 	return success;
 }
 
 gboolean
-camel_imapx_server_noop (CamelIMAPXServer *is,
-                         CamelIMAPXMailbox *mailbox,
-                         GCancellable *cancellable,
-                         GError **error)
+camel_imapx_server_create_mailbox_sync (CamelIMAPXServer *is,
+					const gchar *mailbox_name,
+					GCancellable *cancellable,
+					GError **error)
 {
-	CamelIMAPXJob *job;
+	CamelIMAPXCommand *ic;
 	gboolean success;
 
 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-	/* Mailbox may be NULL. */
-
-	job = camel_imapx_job_new (cancellable);
-	job->type = IMAPX_JOB_NOOP;
-	job->start = imapx_job_noop_start;
-	job->pri = IMAPX_PRIORITY_NOOP;
-
-	camel_imapx_job_set_mailbox (job, mailbox);
+	g_return_val_if_fail (mailbox_name != NULL, FALSE);
 
-	success = imapx_submit_job (is, job, error);
+	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_CREATE_MAILBOX, "CREATE %m", mailbox_name);
 
-	camel_imapx_job_unref (job);
+	success = camel_imapx_server_process_command_sync (is, ic, _("Error creating folder"), cancellable, error);
 
-	return success;
-}
+	camel_imapx_command_unref (ic);
 
-CamelFolderChangeInfo *
-camel_imapx_server_refresh_info (CamelIMAPXServer *is,
-                                 CamelIMAPXMailbox *mailbox,
-                                 GCancellable *cancellable,
-                                 GError **error)
-{
-	CamelIMAPXJob *job;
-	RefreshInfoData *data;
-	CamelFolderChangeInfo *changes = NULL;
-	gboolean registered = TRUE;
-	const gchar *mailbox_name;
+	if (success) {
+		gchar *utf7_pattern;
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), NULL);
+		utf7_pattern = camel_utf8_utf7 (mailbox_name);
 
-	/* Don't run concurrent refreshes on the same mailbox.
-	 * If a refresh is already in progress, let it finish
-	 * and return no changes for this refresh request. */
-	job = imapx_server_ref_job (is, mailbox, IMAPX_JOB_REFRESH_INFO, NULL);
+		/* List the new mailbox so we trigger our untagged
+		 * LIST handler.  This simulates being notified of
+		 * a newly-created mailbox, so we can just let the
+		 * callback functions handle the bookkeeping. */
+		success = camel_imapx_server_list_sync (is, utf7_pattern, 0, cancellable, error);
 
-	if (job != NULL) {
-		camel_imapx_job_unref (job);
-		return camel_folder_change_info_new ();
+		g_free (utf7_pattern);
 	}
 
-	if (!imapx_ensure_mailbox_permanentflags (is, mailbox, cancellable, error))
-		return NULL;
-
-	QUEUE_LOCK (is);
+	return success;
+}
 
-	data = g_slice_new0 (RefreshInfoData);
-	data->changes = camel_folder_change_info_new ();
+gboolean
+camel_imapx_server_delete_mailbox_sync (CamelIMAPXServer *is,
+					CamelIMAPXMailbox *mailbox,
+					GCancellable *cancellable,
+					GError **error)
+{
+	CamelIMAPXCommand *ic;
+	CamelIMAPXMailbox *inbox;
+	CamelIMAPXStore *imapx_store;
+	gboolean success;
 
-	job = camel_imapx_job_new (cancellable);
-	job->type = IMAPX_JOB_REFRESH_INFO;
-	job->start = imapx_job_refresh_info_start;
-	job->matches = imapx_job_refresh_info_matches;
-	job->pri = IMAPX_PRIORITY_REFRESH_INFO;
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
 
-	camel_imapx_job_set_mailbox (job, mailbox);
+	/* Avoid camel_imapx_job_set_mailbox() here.  We
+	 * don't want to select the mailbox to be deleted. */
 
-	mailbox_name = camel_imapx_mailbox_get_name (mailbox);
+	imapx_store = camel_imapx_server_ref_store (is);
+	/* Keep going, even if this returns NULL. */
+	inbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");
 
-	if (camel_imapx_mailbox_is_inbox (mailbox_name))
-		job->pri += 10;
+	/* Make sure the to-be-deleted folder is not
+	 * selected by selecting INBOX for this operation. */
+	success = camel_imapx_server_ensure_selected_sync (is, inbox, cancellable, error);
+	if (!success) {
+		g_clear_object (&inbox);
+		g_clear_object (&imapx_store);
+		return FALSE;
+	}
 
-	camel_imapx_job_set_data (
-		job, data, (GDestroyNotify) refresh_info_data_free);
+	/* Just to make sure it'll not disappeare before the end of this function */
+	g_object_ref (mailbox);
 
-	registered = imapx_register_job (is, job, error);
+	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_DELETE_MAILBOX, "DELETE %M", mailbox);
 
-	QUEUE_UNLOCK (is);
+	success = camel_imapx_server_process_command_sync (is, ic, _("Error deleting folder"), cancellable, error);
 
-	if (registered)
-		camel_imapx_job_guard_mailbox_update (job, mailbox);
+	camel_imapx_command_unref (ic);
 
-	if (registered && camel_imapx_job_run (job, is, error)) {
-		changes = data->changes;
-		data->changes = NULL;
-	} else if (registered) {
-		imapx_unregister_job (is, job);
+	if (success) {
+		camel_imapx_mailbox_deleted (mailbox);
+		camel_imapx_store_emit_mailbox_updated (imapx_store, mailbox);
 	}
 
-	camel_imapx_job_unref (job);
+	g_clear_object (&inbox);
+	g_clear_object (&imapx_store);
+	g_clear_object (&mailbox);
 
-	return changes;
+	return success;
 }
 
-static void
-imapx_sync_free_user (GArray *user_set)
+gboolean
+camel_imapx_server_rename_mailbox_sync (CamelIMAPXServer *is,
+					CamelIMAPXMailbox *mailbox,
+					const gchar *new_mailbox_name,
+					GCancellable *cancellable,
+					GError **error)
 {
-	gint i;
-
-	if (user_set == NULL)
-		return;
-
-	for (i = 0; i < user_set->len; i++) {
-		struct _imapx_flag_change *flag_change = &g_array_index (user_set, struct _imapx_flag_change, i);
-		GPtrArray *infos = flag_change->infos;
-		gint j;
-
-		for (j = 0; j < infos->len; j++) {
-			CamelMessageInfo *info = g_ptr_array_index (infos, j);
-			camel_message_info_unref (info);
-		}
+	CamelIMAPXCommand *ic;
+	CamelIMAPXMailbox *inbox;
+	CamelIMAPXStore *imapx_store;
+	gboolean success;
 
-		g_ptr_array_free (infos, TRUE);
-		g_free (flag_change->name);
-	}
-	g_array_free (user_set, TRUE);
-}
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+	g_return_val_if_fail (new_mailbox_name != NULL, FALSE);
 
-static void
-imapx_unset_folder_flagged_flag (CamelFolderSummary *summary,
-				 GPtrArray *changed_uids,
-				 gboolean except_deleted_messages)
-{
-	CamelMessageInfo *info;
-	gboolean changed = FALSE;
-	gint ii;
+	imapx_store = camel_imapx_server_ref_store (is);
+	inbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");
+	g_return_val_if_fail (inbox != NULL, FALSE);
 
-	g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
-	g_return_if_fail (changed_uids != NULL);
+	/* We don't want to select the mailbox to be renamed. */
+	success = camel_imapx_server_ensure_selected_sync (is, inbox, cancellable, error);
+	if (!success) {
+		g_clear_object (&inbox);
+		g_clear_object (&imapx_store);
+		return FALSE;
+	}
 
-	for (ii = 0; ii < changed_uids->len; ii++) {
-		info = camel_folder_summary_get (summary, changed_uids->pdata[ii]);
+	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_RENAME_MAILBOX, "RENAME %M %m", mailbox, new_mailbox_name);
 
-		if (info) {
-			CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
+	success = camel_imapx_server_process_command_sync (is, ic, _("Error renaming folder"), cancellable, error);
 
-			/* some infos could be only 'dirty' (needed to save into summary) */
-			if ((mi->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0 &&
-			   (!except_deleted_messages || (mi->flags & CAMEL_MESSAGE_DELETED) == 0)) {
-				mi->flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED;
-				mi->dirty = TRUE;
-				changed = TRUE;
-			}
+	camel_imapx_command_unref (ic);
 
-			camel_message_info_unref (info);
-		}
-	}
+	if (success) {
+		/* Perform the same processing as imapx_untagged_list()
+		 * would if the server notified us of a renamed mailbox. */
 
-	if (changed) {
-		camel_folder_summary_touch (summary);
-		camel_folder_summary_save_to_db (summary, NULL);
+		camel_imapx_store_handle_mailbox_rename (imapx_store, mailbox, new_mailbox_name);
 	}
+
+	g_clear_object (&inbox);
+	g_clear_object (&imapx_store);
+
+	return success;
 }
 
-static gboolean
-imapx_server_sync_changes (CamelIMAPXServer *is,
-                           CamelIMAPXMailbox *mailbox,
-                           guint32 job_type,
-                           gint pri,
-                           GCancellable *cancellable,
-                           GError **error)
+gboolean
+camel_imapx_server_subscribe_mailbox_sync (CamelIMAPXServer *is,
+					   CamelIMAPXMailbox *mailbox,
+					   GCancellable *cancellable,
+					   GError **error)
 {
-	guint i, on_orset, off_orset;
-	GPtrArray *changed_uids;
-	GArray *on_user = NULL, *off_user = NULL;
-	CamelFolder *folder;
-	CamelIMAPXMessageInfo *info;
-	CamelIMAPXJob *job;
-	CamelIMAPXSettings *settings;
-	SyncChangesData *data;
-	gboolean use_real_junk_path;
-	gboolean use_real_trash_path;
-	gboolean remove_deleted_flags;
-	gboolean nothing_to_do;
-	gboolean registered;
-	gboolean own_allocated_changed_uids = FALSE;
-	gboolean success = TRUE;
+	CamelIMAPXCommand *ic;
+	gboolean success;
 
-	folder = imapx_server_ref_folder (is, mailbox);
-	g_return_val_if_fail (folder != NULL, FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
 
-	if (!imapx_ensure_mailbox_permanentflags (is, mailbox, cancellable, error))
-		return FALSE;
+	/* We don't want to select the mailbox to be subscribed. */
+	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_SUBSCRIBE_MAILBOX, "SUBSCRIBE %M", mailbox);
 
-	/* We calculate two masks, a mask of all flags which have been
-	 * turned off and a mask of all flags which have been turned
-	 * on. If either of these aren't 0, then we have work to do,
-	 * and we fire off a job to do it.
-	 *
-	 * User flags are a bit more tricky, we rely on the user
-	 * flags being sorted, and then we create a bunch of lists;
-	 * one for each flag being turned off, including each
-	 * info being turned off, and one for each flag being turned on.
-	 */
-	changed_uids = camel_folder_summary_get_changed (folder->summary);
+	success = camel_imapx_server_process_command_sync (is, ic, _("Error subscribing to folder"), cancellable, error);
 
-	if (changed_uids->len == 0) {
-		camel_folder_free_uids (folder, changed_uids);
-		g_object_unref (folder);
-		return TRUE;
-	}
+	camel_imapx_command_unref (ic);
 
-	settings = camel_imapx_server_ref_settings (is);
-	use_real_junk_path =
-		camel_imapx_settings_get_use_real_junk_path (settings);
-	use_real_trash_path =
-		camel_imapx_settings_get_use_real_trash_path (settings);
-	g_object_unref (settings);
+	if (success) {
+		CamelIMAPXStore *imapx_store;
 
-	remove_deleted_flags = use_real_trash_path && (job_type != IMAPX_JOB_EXPUNGE) != 0;
+		/* Perform the same processing as imapx_untagged_list()
+		 * would if the server notified us of a subscription. */
 
-	off_orset = on_orset = 0;
-	for (i = 0; i < changed_uids->len; i++) {
-		guint32 flags, sflags;
-		CamelFlag *uflags, *suflags;
-		const gchar *uid;
-		gboolean move_to_real_junk;
-		gboolean move_to_real_trash;
-		guint j = 0;
+		imapx_store = camel_imapx_server_ref_store (is);
 
-		uid = g_ptr_array_index (changed_uids, i);
+		camel_imapx_mailbox_subscribed (mailbox);
+		camel_imapx_store_emit_mailbox_updated (imapx_store, mailbox);
 
-		info = (CamelIMAPXMessageInfo *)
-			camel_folder_summary_get (folder->summary, uid);
+		g_clear_object (&imapx_store);
+	}
 
-		if (info == NULL)
-			continue;
+	return success;
+}
 
-		if (!(info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED)) {
-			camel_message_info_unref (info);
-			continue;
-		}
+gboolean
+camel_imapx_server_unsubscribe_mailbox_sync (CamelIMAPXServer *is,
+					     CamelIMAPXMailbox *mailbox,
+					     GCancellable *cancellable,
+					     GError **error)
+{
+	CamelIMAPXCommand *ic;
+	gboolean success;
 
-		flags = info->info.flags & CAMEL_IMAPX_SERVER_FLAGS;
-		sflags = info->server_flags & CAMEL_IMAPX_SERVER_FLAGS;
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
 
-		move_to_real_junk =
-			use_real_junk_path &&
-			(flags & CAMEL_MESSAGE_JUNK);
-
-		move_to_real_trash =
-			use_real_trash_path &&
-			(flags & CAMEL_MESSAGE_DELETED);
-
-		if (move_to_real_junk)
-			camel_imapx_folder_add_move_to_real_junk (
-				CAMEL_IMAPX_FOLDER (folder), uid);
-
-		if (move_to_real_trash)
-			camel_imapx_folder_add_move_to_real_trash (
-				CAMEL_IMAPX_FOLDER (folder), uid);
+	/* We don't want to select the mailbox to be unsubscribed. */
+	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_UNSUBSCRIBE_MAILBOX, "UNSUBSCRIBE %M", mailbox);
 
-		if (flags != sflags) {
-			off_orset |= (flags ^ sflags) & ~flags;
-			on_orset |= (flags ^ sflags) & flags;
-		}
+	success = camel_imapx_server_process_command_sync (is, ic, _("Error unsubscribing from folder"), cancellable, error);
 
-		uflags = info->info.user_flags;
-		suflags = info->server_user_flags;
-		while (uflags || suflags) {
-			gint res;
+	camel_imapx_command_unref (ic);
 
-			if (uflags) {
-				if (suflags)
-					res = strcmp (uflags->name, suflags->name);
-				else if (*uflags->name)
-					res = -1;
-				else {
-					uflags = uflags->next;
-					continue;
-				}
-			} else {
-				res = 1;
-			}
+	if (success) {
+		CamelIMAPXStore *imapx_store;
 
-			if (res == 0) {
-				uflags = uflags->next;
-				suflags = suflags->next;
-			} else {
-				GArray *user_set;
-				CamelFlag *user_flag;
-				struct _imapx_flag_change *change = NULL, add = { 0 };
+		/* Perform the same processing as imapx_untagged_list()
+		 * would if the server notified us of an unsubscription. */
 
-				if (res < 0) {
-					if (on_user == NULL)
-						on_user = g_array_new (FALSE, FALSE, sizeof (struct _imapx_flag_change));
-					user_set = on_user;
-					user_flag = uflags;
-					uflags = uflags->next;
-				} else {
-					if (off_user == NULL)
-						off_user = g_array_new (FALSE, FALSE, sizeof (struct _imapx_flag_change));
-					user_set = off_user;
-					user_flag = suflags;
-					suflags = suflags->next;
-				}
+		imapx_store = camel_imapx_server_ref_store (is);
 
-				/* Could sort this and binary search */
-				for (j = 0; j < user_set->len; j++) {
-					change = &g_array_index (user_set, struct _imapx_flag_change, j);
-					if (strcmp (change->name, user_flag->name) == 0)
-						goto found;
-				}
-				add.name = g_strdup (user_flag->name);
-				add.infos = g_ptr_array_new ();
-				g_array_append_val (user_set, add);
-				change = &add;
-			found:
-				camel_message_info_ref (info);
-				g_ptr_array_add (change->infos, info);
-			}
-		}
+		camel_imapx_mailbox_unsubscribed (mailbox);
+		camel_imapx_store_emit_mailbox_updated (imapx_store, mailbox);
 
-		camel_message_info_unref (info);
+		g_clear_object (&imapx_store);
 	}
 
-	nothing_to_do =
-		(on_orset == 0) &&
-		(off_orset == 0) &&
-		(on_user == NULL) &&
-		(off_user == NULL);
+	return success;
+}
 
-	if (nothing_to_do) {
-		imapx_sync_free_user (on_user);
-		imapx_sync_free_user (off_user);
-		imapx_unset_folder_flagged_flag (folder->summary, changed_uids, remove_deleted_flags);
-		camel_folder_free_uids (folder, changed_uids);
-		g_object_unref (folder);
-		return TRUE;
-	}
+gboolean
+camel_imapx_server_update_quota_info_sync (CamelIMAPXServer *is,
+					   CamelIMAPXMailbox *mailbox,
+					   GCancellable *cancellable,
+					   GError **error)
+{
+	CamelIMAPXCommand *ic;
+	gboolean success;
 
-	/* TODO above code should go into changes_start */
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
 
-	job = imapx_server_ref_job (is, mailbox, IMAPX_JOB_SYNC_CHANGES, NULL);
+	g_mutex_lock (&is->priv->stream_lock);
 
-	if (job != NULL) {
-		GPtrArray *new_changed_uids;
-		GHashTable *known_uids;
-		GHashTableIter iter;
-		gpointer key, value;
-		gint ii;
+	if (CAMEL_IMAPX_LACK_CAPABILITY (is->priv->cinfo, QUOTA)) {
+		g_mutex_unlock (&is->priv->stream_lock);
 
-		known_uids = g_hash_table_new (g_str_hash, g_str_equal);
-		data = camel_imapx_job_get_data (job);
+		g_set_error_literal (
+			error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+			_("IMAP server does not support quotas"));
+		return FALSE;
+	} else {
+		g_mutex_unlock (&is->priv->stream_lock);
+	}
 
-		if (data && data->changed_uids) {
-			for (ii = 0; ii < changed_uids->len; ii++) {
-				g_hash_table_insert (known_uids, changed_uids->pdata[ii], GINT_TO_POINTER (1));
-			}
+	success = camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error);
+	if (!success)
+		return FALSE;
 
-			for (ii = 0; ii < data->changed_uids->len; ii++) {
-				g_hash_table_remove (known_uids, data->changed_uids->pdata[ii]);
-			}
-		}
+	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_UPDATE_QUOTA_INFO, "GETQUOTAROOT %M", mailbox);
 
-		if (g_hash_table_size (known_uids) == 0) {
-			/* The pending job stores changes for the same UIDs */
-			if (pri > job->pri)
-				job->pri = pri;
-
-			camel_imapx_job_unref (job);
-
-			imapx_sync_free_user (on_user);
-			imapx_sync_free_user (off_user);
-			camel_folder_free_uids (folder, changed_uids);
-			g_object_unref (folder);
-			g_hash_table_destroy (known_uids);
-			return TRUE;
-		}
+	success = camel_imapx_server_process_command_sync (is, ic, _("Error retrieving quota information"), cancellable, error);
 
-		new_changed_uids = g_ptr_array_sized_new (g_hash_table_size (known_uids));
+	camel_imapx_command_unref (ic);
 
-		/* What left in known_uids are message info changes which are not being
-		   saved in the pending job */
+	return success;
+}
 
-		g_hash_table_iter_init (&iter, known_uids);
-		while (g_hash_table_iter_next (&iter, &key, &value)) {
-			g_ptr_array_add (new_changed_uids, (gpointer) camel_pstring_strdup (key));
-		}
+GPtrArray *
+camel_imapx_server_uid_search_sync (CamelIMAPXServer *is,
+				    CamelIMAPXMailbox *mailbox,
+				    const gchar *criteria_prefix,
+				    const gchar *search_key,
+				    const gchar * const *words,
+				    GCancellable *cancellable,
+				    GError **error)
+{
+	CamelIMAPXCommand *ic;
+	GArray *uid_search_results;
+	GPtrArray *results = NULL;
+	gint ii;
+	gboolean need_charset = FALSE;
+	gboolean success;
 
-		g_hash_table_destroy (known_uids);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), NULL);
+	g_return_val_if_fail (criteria_prefix != NULL, NULL);
 
-		camel_folder_free_uids (folder, changed_uids);
-		changed_uids = new_changed_uids;
+	success = camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error);
+	if (!success)
+		return FALSE;
 
-		/* Why would anyone define a virtual function for the free on the folder? */
-		own_allocated_changed_uids = TRUE;
+	for (ii = 0; !need_charset && words && words[ii]; ii++) {
+		need_charset = !imapx_util_all_is_ascii (words[ii]);
 	}
 
-	QUEUE_LOCK (is);
+	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_UID_SEARCH, "UID SEARCH");
+	if (need_charset)
+		camel_imapx_command_add (ic, " CHARSET UTF-8");
+	if (criteria_prefix && *criteria_prefix)
+		camel_imapx_command_add (ic, " %t", criteria_prefix);
 
-	data = g_slice_new0 (SyncChangesData);
-	data->folder = g_object_ref (folder);
-	data->changed_uids = changed_uids;  /* takes ownership */
-	data->own_allocated_changed_uids = own_allocated_changed_uids;
-	data->on_set = on_orset;
-	data->off_set = off_orset;
-	data->on_user = on_user;  /* takes ownership */
-	data->off_user = off_user;  /* takes ownership */
-	data->remove_deleted_flags = remove_deleted_flags;
+	if (search_key && words) {
+		for (ii = 0; words[ii]; ii++) {
+			camel_imapx_command_add (ic, " %t %s", search_key, words[ii]);
+		}
+	}
 
-	job = camel_imapx_job_new (cancellable);
-	job->type = IMAPX_JOB_SYNC_CHANGES;
-	job->start = imapx_job_sync_changes_start;
-	job->matches = imapx_job_sync_changes_matches;
-	job->pri = pri;
+	success = camel_imapx_server_process_command_sync (is, ic, _("Search failed"), cancellable, error);
 
-	camel_imapx_job_set_mailbox (job, mailbox);
+	camel_imapx_command_unref (ic);
 
-	camel_imapx_job_set_data (
-		job, data, (GDestroyNotify) sync_changes_data_free);
+	g_mutex_lock (&is->priv->search_results_lock);
+	uid_search_results = is->priv->search_results;
+	is->priv->search_results = NULL;
+	g_mutex_unlock (&is->priv->search_results_lock);
 
-	registered = imapx_register_job (is, job, error);
+	if (success) {
+		guint ii;
 
-	QUEUE_UNLOCK (is);
+		/* Convert the numeric UIDs to strings. */
 
-	if (job_type == IMAPX_JOB_SYNC_CHANGES && registered)
-		camel_imapx_job_guard_mailbox_update (job, mailbox);
+		g_return_val_if_fail (uid_search_results != NULL, NULL);
 
-	success = registered && camel_imapx_job_run (job, is, error);
+		results = g_ptr_array_new_full (uid_search_results->len, (GDestroyNotify) camel_pstring_free);
 
-	if (!success && registered)
-		imapx_unregister_job (is, job);
+		for (ii = 0; ii < uid_search_results->len; ii++) {
+			const gchar *pooled_uid;
+			guint64 numeric_uid;
+			gchar *alloced_uid;
 
-	camel_imapx_job_unref (job);
+			numeric_uid = g_array_index (uid_search_results, guint64, ii);
+			alloced_uid = g_strdup_printf ("%" G_GUINT64_FORMAT, numeric_uid);
+			pooled_uid = camel_pstring_add (alloced_uid, TRUE);
+			g_ptr_array_add (results, (gpointer) pooled_uid);
+		}
+	}
 
-	g_object_unref (folder);
+	if (uid_search_results)
+		g_array_unref (uid_search_results);
 
-	return success;
+	return results;
 }
 
-gboolean
-camel_imapx_server_sync_changes (CamelIMAPXServer *is,
-                                 CamelIMAPXMailbox *mailbox,
-                                 GCancellable *cancellable,
-                                 GError **error)
+typedef struct _IdleThreadData {
+	CamelIMAPXServer *is;
+	GCancellable *idle_cancellable;
+	gint idle_stamp;
+} IdleThreadData;
+
+static gpointer
+imapx_server_idle_thread (gpointer user_data)
 {
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+	IdleThreadData *itd = user_data;
+	CamelIMAPXServer *is;
+	CamelIMAPXMailbox *mailbox;
+	CamelIMAPXCommand *ic;
+	CamelIMAPXCommandPart *cp;
+	GCancellable *idle_cancellable;
+	GError *local_error = NULL;
+	gint previous_timeout = -1;
+	gboolean success = FALSE;
+	gboolean rather_disconnect = FALSE;
 
-	return imapx_server_sync_changes (
-		is, mailbox,
-		IMAPX_JOB_SYNC_CHANGES,
-		IMAPX_PRIORITY_SYNC_CHANGES,
-		cancellable, error);
-}
+	g_return_val_if_fail (itd != NULL, NULL);
+
+	is = itd->is;
+	idle_cancellable = itd->idle_cancellable;
 
-/* expunge-uids? */
-gboolean
-camel_imapx_server_expunge (CamelIMAPXServer *is,
-                            CamelIMAPXMailbox *mailbox,
-                            GCancellable *cancellable,
-                            GError **error)
-{
-	CamelIMAPXJob *job;
-	gboolean registered;
-	gboolean success;
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
+	g_return_val_if_fail (G_IS_CANCELLABLE (idle_cancellable), NULL);
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+	g_mutex_lock (&is->priv->idle_lock);
 
-	/* Do we really care to wait for this one to finish? */
-	job = imapx_server_ref_job (is, mailbox, IMAPX_JOB_EXPUNGE, NULL);
+	if (g_cancellable_is_cancelled (idle_cancellable) ||
+	    is->priv->idle_stamp != itd->idle_stamp ||
+	    is->priv->idle_state != IMAPX_IDLE_STATE_SCHEDULED) {
+		g_cond_broadcast (&is->priv->idle_cond);
+		g_mutex_unlock (&is->priv->idle_lock);
+
+		g_clear_object (&itd->is);
+		g_clear_object (&itd->idle_cancellable);
+		g_free (itd);
 
-	if (job != NULL) {
-		camel_imapx_job_unref (job);
-		return TRUE;
+		return NULL;
 	}
 
-	QUEUE_LOCK (is);
+	is->priv->idle_state = IMAPX_IDLE_STATE_PREPARING;
+	g_cond_broadcast (&is->priv->idle_cond);
 
-	job = camel_imapx_job_new (cancellable);
-	job->type = IMAPX_JOB_EXPUNGE;
-	job->start = imapx_job_expunge_start;
-	job->matches = imapx_job_expunge_matches;
-	job->pri = IMAPX_PRIORITY_EXPUNGE;
+	mailbox = is->priv->idle_mailbox;
+	if (mailbox)
+		g_object_ref (mailbox);
 
-	camel_imapx_job_set_mailbox (job, mailbox);
+	g_mutex_unlock (&is->priv->idle_lock);
 
-	registered = imapx_register_job (is, job, error);
+	if (!mailbox)
+		mailbox = camel_imapx_server_ref_selected (is);
+
+	if (!mailbox)
+		goto exit;
 
-	QUEUE_UNLOCK (is);
+	success = camel_imapx_server_ensure_selected_sync (is, mailbox, idle_cancellable, &local_error);
+	if (!success) {
+		rather_disconnect = TRUE;
+		goto exit;
+	}
 
-	success = registered && camel_imapx_job_run (job, is, error);
+	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_IDLE, "IDLE");
+	camel_imapx_command_close (ic);
 
-	if (!success && registered)
-		imapx_unregister_job (is, job);
+	cp = g_queue_peek_head (&ic->parts);
+	cp->type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
 
-	camel_imapx_job_unref (job);
+	g_mutex_lock (&is->priv->stream_lock);
+	/* Set the connection timeout to one minute more than the inactivity timeout */
+	if (is->priv->connection)
+		previous_timeout = imapx_server_set_connection_timeout (is->priv->connection, INACTIVITY_TIMEOUT_SECONDS + 60);
+	g_mutex_unlock (&is->priv->stream_lock);
 
-	return success;
-}
+	g_mutex_lock (&is->priv->idle_lock);
+	if (is->priv->idle_stamp == itd->idle_stamp &&
+	    is->priv->idle_state == IMAPX_IDLE_STATE_PREPARING) {
+		g_mutex_unlock (&is->priv->idle_lock);
 
-gboolean
-camel_imapx_server_list (CamelIMAPXServer *is,
-                         const gchar *pattern,
-                         CamelStoreGetFolderInfoFlags flags,
-                         GCancellable *cancellable,
-                         GError **error)
-{
-	CamelIMAPXJob *job;
-	ListData *data;
-	gboolean success;
+		/* Blocks, until the DONE is issued or on inactivity timeout, error, ... */
+		success = camel_imapx_server_process_command_sync (is, ic, _("Error running IDLE"), idle_cancellable, &local_error);
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-	g_return_val_if_fail (pattern != NULL, FALSE);
+		rather_disconnect = rather_disconnect || !success || g_cancellable_is_cancelled (idle_cancellable);
+	} else {
+		g_mutex_unlock (&is->priv->idle_lock);
+	}
 
-	data = g_slice_new0 (ListData);
-	data->pattern = g_strdup (pattern);
+	if (previous_timeout >= 0) {
+		g_mutex_lock (&is->priv->stream_lock);
+		if (is->priv->connection)
+			imapx_server_set_connection_timeout (is->priv->connection, previous_timeout);
+		g_mutex_unlock (&is->priv->stream_lock);
+	}
 
-	job = camel_imapx_job_new (cancellable);
-	job->type = IMAPX_JOB_LIST;
-	job->start = imapx_job_list_start;
-	job->matches = imapx_job_list_matches;
-	job->pri = IMAPX_PRIORITY_LIST;
+	camel_imapx_command_unref (ic);
 
-	camel_imapx_job_set_data (
-		job, data, (GDestroyNotify) list_data_free);
+ exit:
+	g_mutex_lock (&is->priv->idle_lock);
+	g_clear_object (&is->priv->idle_cancellable);
+	is->priv->idle_state = IMAPX_IDLE_STATE_OFF;
+	g_cond_broadcast (&is->priv->idle_cond);
+	g_mutex_unlock (&is->priv->idle_lock);
+
+	if (success)
+		c (camel_imapx_server_get_tagprefix (is), "IDLE finished successfully\n");
+	else if (local_error)
+		c (camel_imapx_server_get_tagprefix (is), "IDLE finished with error: %s%s\n", local_error->message, rather_disconnect ? "; rather disconnect" : "");
+	else
+		c (camel_imapx_server_get_tagprefix (is), "IDLE finished without error%s\n", rather_disconnect ? "; rather disconnect" : "");
 
-	/* sync operation which is triggered by user */
-	if (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST)
-		job->pri += 300;
+	if (rather_disconnect) {
+		imapx_disconnect (is);
+	}
 
-	success = imapx_submit_job (is, job, error);
+	g_clear_object (&mailbox);
+	g_clear_error (&local_error);
 
-	camel_imapx_job_unref (job);
+	g_clear_object (&itd->is);
+	g_clear_object (&itd->idle_cancellable);
+	g_free (itd);
 
-	return success;
+	return NULL;
 }
 
-gboolean
-camel_imapx_server_create_mailbox (CamelIMAPXServer *is,
-                                   const gchar *mailbox_name,
-                                   GCancellable *cancellable,
-                                   GError **error)
+static gboolean
+imapx_server_run_idle_thread_cb (gpointer user_data)
 {
-	CamelIMAPXJob *job;
-	MailboxData *data;
-	gboolean success;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-	g_return_val_if_fail (mailbox_name != NULL, FALSE);
-
-	data = g_slice_new0 (MailboxData);
-	data->mailbox_name = g_strdup (mailbox_name);
+	GWeakRef *is_weakref = user_data;
+	CamelIMAPXServer *is;
 
-	job = camel_imapx_job_new (cancellable);
-	job->type = IMAPX_JOB_CREATE_MAILBOX;
-	job->start = imapx_job_create_mailbox_start;
-	job->pri = IMAPX_PRIORITY_MAILBOX_MGMT;
+	g_return_val_if_fail (is_weakref != NULL, FALSE);
 
-	camel_imapx_job_set_data (
-		job, data, (GDestroyNotify) mailbox_data_free);
+	is = g_weak_ref_get (is_weakref);
+	if (!is)
+		return FALSE;
 
-	success = imapx_submit_job (is, job, error);
+	g_mutex_lock (&is->priv->idle_lock);
 
-	if (success) {
-		gchar *utf7_pattern;
+	if (g_main_current_source () == is->priv->idle_pending) {
+		if (!g_source_is_destroyed (g_main_current_source ()) &&
+		    is->priv->idle_state == IMAPX_IDLE_STATE_SCHEDULED) {
+			IdleThreadData *itd;
+			GThread *thread;
+			GError *local_error = NULL;
+
+			itd = g_new0 (IdleThreadData, 1);
+			itd->is = g_object_ref (is);
+			itd->idle_cancellable = g_object_ref (is->priv->idle_cancellable);
+			itd->idle_stamp = is->priv->idle_stamp;
+
+			thread = g_thread_try_new (NULL, imapx_server_idle_thread, itd, &local_error);
+			if (thread) {
+				g_thread_unref (thread);
+			} else {
+				g_warning ("%s: Failed to create IDLE thread: %s", G_STRFUNC, local_error ? local_error->message : "Unknown error");
 
-		utf7_pattern = camel_utf8_utf7 (mailbox_name);
+				g_clear_object (&itd->is);
+				g_clear_object (&itd->idle_cancellable);
+				g_free (itd);
+			}
 
-		/* List the new mailbox so we trigger our untagged
-		 * LIST handler.  This simulates being notified of
-		 * a newly-created mailbox, so we can just let the
-		 * callback functions handle the bookkeeping. */
-		success = camel_imapx_server_list (
-			is, utf7_pattern, 0, cancellable, error);
+			g_clear_error (&local_error);
+		}
 
-		g_free (utf7_pattern);
+		g_source_unref (is->priv->idle_pending);
+		is->priv->idle_pending = NULL;
 	}
 
-	camel_imapx_job_unref (job);
+	g_mutex_unlock (&is->priv->idle_lock);
 
-	return success;
+	return FALSE;
 }
 
 gboolean
-camel_imapx_server_delete_mailbox (CamelIMAPXServer *is,
-                                   CamelIMAPXMailbox *mailbox,
-                                   GCancellable *cancellable,
-                                   GError **error)
+camel_imapx_server_can_use_idle (CamelIMAPXServer *is)
 {
-	CamelIMAPXJob *job;
-	MailboxData *data;
-	gboolean success;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+	gboolean use_idle = FALSE;
 
-	/* Avoid camel_imapx_job_set_mailbox() here.  We
-	 * don't want to select the mailbox to be deleted. */
+	g_mutex_lock (&is->priv->stream_lock);
 
-	data = g_slice_new0 (MailboxData);
-	data->mailbox = g_object_ref (mailbox);
+	/* No need for IDLE if the server supports NOTIFY. */
+	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, NOTIFY)) {
+		g_mutex_unlock (&is->priv->stream_lock);
 
-	job = camel_imapx_job_new (cancellable);
-	job->type = IMAPX_JOB_DELETE_MAILBOX;
-	job->start = imapx_job_delete_mailbox_start;
-	job->pri = IMAPX_PRIORITY_MAILBOX_MGMT;
+		return FALSE;
+	}
 
-	camel_imapx_job_set_data (
-		job, data, (GDestroyNotify) mailbox_data_free);
+	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, IDLE)) {
+		CamelIMAPXSettings *settings;
 
-	success = imapx_submit_job (is, job, error);
+		settings = camel_imapx_server_ref_settings (is);
+		use_idle = camel_imapx_settings_get_use_idle (settings);
+		g_object_unref (settings);
+	}
 
-	camel_imapx_job_unref (job);
+	g_mutex_unlock (&is->priv->stream_lock);
 
-	return success;
+	return use_idle;
 }
 
 gboolean
-camel_imapx_server_rename_mailbox (CamelIMAPXServer *is,
-                                   CamelIMAPXMailbox *mailbox,
-                                   const gchar *new_mailbox_name,
-                                   GCancellable *cancellable,
-                                   GError **error)
+camel_imapx_server_is_in_idle (CamelIMAPXServer *is)
 {
-	CamelIMAPXJob *job;
-	MailboxData *data;
-	gboolean success;
+	gboolean in_idle;
 
 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
-	g_return_val_if_fail (new_mailbox_name != NULL, FALSE);
-
-	/* Avoid camel_imapx_job_set_mailbox() here.  We
-	 * don't want to select the mailbox to be renamed. */
-
-	data = g_slice_new0 (MailboxData);
-	data->mailbox = g_object_ref (mailbox);
-	data->mailbox_name = g_strdup (new_mailbox_name);
-
-	job = camel_imapx_job_new (cancellable);
-	job->type = IMAPX_JOB_RENAME_MAILBOX;
-	job->start = imapx_job_rename_mailbox_start;
-	job->pri = IMAPX_PRIORITY_MAILBOX_MGMT;
-
-	camel_imapx_job_set_data (
-		job, data, (GDestroyNotify) mailbox_data_free);
 
-	success = imapx_submit_job (is, job, error);
+	g_mutex_lock (&is->priv->idle_lock);
+	in_idle = is->priv->idle_state != IMAPX_IDLE_STATE_OFF;
+	g_mutex_unlock (&is->priv->idle_lock);
 
-	camel_imapx_job_unref (job);
-
-	return success;
+	return in_idle;
 }
 
-gboolean
-camel_imapx_server_subscribe_mailbox (CamelIMAPXServer *is,
-                                      CamelIMAPXMailbox *mailbox,
-                                      GCancellable *cancellable,
-                                      GError **error)
+CamelIMAPXMailbox *
+camel_imapx_server_ref_idle_mailbox (CamelIMAPXServer *is)
 {
-	CamelIMAPXJob *job;
-	MailboxData *data;
-	gboolean success;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
-
-	/* Avoid camel_imapx_job_set_mailbox() here.  We
-	 * don't want to select the mailbox to be subscribed. */
-
-	data = g_slice_new0 (MailboxData);
-	data->mailbox = g_object_ref (mailbox);
+	CamelIMAPXMailbox *mailbox = NULL;
 
-	job = camel_imapx_job_new (cancellable);
-	job->type = IMAPX_JOB_SUBSCRIBE_MAILBOX;
-	job->start = imapx_job_subscribe_mailbox_start;
-	job->pri = IMAPX_PRIORITY_MAILBOX_MGMT;
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
 
-	camel_imapx_job_set_data (
-		job, data, (GDestroyNotify) mailbox_data_free);
+	g_mutex_lock (&is->priv->idle_lock);
 
-	success = imapx_submit_job (is, job, error);
+	if (is->priv->idle_state != IMAPX_IDLE_STATE_OFF) {
+		if (is->priv->idle_mailbox)
+			mailbox = g_object_ref (is->priv->idle_mailbox);
+		else
+			mailbox = camel_imapx_server_ref_selected (is);
+	}
 
-	camel_imapx_job_unref (job);
+	g_mutex_unlock (&is->priv->idle_lock);
 
-	return success;
+	return mailbox;
 }
 
 gboolean
-camel_imapx_server_unsubscribe_mailbox (CamelIMAPXServer *is,
-                                        CamelIMAPXMailbox *mailbox,
-                                        GCancellable *cancellable,
-                                        GError **error)
+camel_imapx_server_schedule_idle_sync (CamelIMAPXServer *is,
+				       CamelIMAPXMailbox *mailbox,
+				       GCancellable *cancellable,
+				       GError **error)
 {
-	CamelIMAPXJob *job;
-	MailboxData *data;
-	gboolean success;
-
 	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+	if (mailbox)
+		g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
 
-	/* Avoid camel_imapx_job_set_mailbox() here.  We
-	 * don't want to select the mailbox to be unsubscribed. */
-
-	data = g_slice_new0 (MailboxData);
-	data->mailbox = g_object_ref (mailbox);
+	if (!camel_imapx_server_stop_idle_sync (is, cancellable, error))
+		return FALSE;
 
-	job = camel_imapx_job_new (cancellable);
-	job->type = IMAPX_JOB_UNSUBSCRIBE_MAILBOX;
-	job->start = imapx_job_unsubscribe_mailbox_start;
-	job->pri = IMAPX_PRIORITY_MAILBOX_MGMT;
+	if (!camel_imapx_server_can_use_idle (is))
+		return TRUE;
 
-	camel_imapx_job_set_data (
-		job, data, (GDestroyNotify) mailbox_data_free);
+	g_mutex_lock (&is->priv->idle_lock);
 
-	success = imapx_submit_job (is, job, error);
+	if (is->priv->idle_state != IMAPX_IDLE_STATE_OFF) {
+		g_warn_if_fail (is->priv->idle_state == IMAPX_IDLE_STATE_OFF);
 
-	camel_imapx_job_unref (job);
+		g_mutex_unlock (&is->priv->idle_lock);
 
-	return success;
-}
+		return FALSE;
+	}
 
-gboolean
-camel_imapx_server_update_quota_info (CamelIMAPXServer *is,
-                                      CamelIMAPXMailbox *mailbox,
-                                      GCancellable *cancellable,
-                                      GError **error)
-{
-	CamelIMAPXJob *job;
-	gboolean success;
+	g_warn_if_fail (is->priv->idle_cancellable == NULL);
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
+	is->priv->idle_cancellable = g_cancellable_new ();
+	is->priv->idle_stamp++;
 
-	if (CAMEL_IMAPX_LACK_CAPABILITY (is->cinfo, QUOTA)) {
-		g_set_error_literal (
-			error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
-			_("IMAP server does not support quotas"));
-		return FALSE;
+	if (is->priv->idle_pending) {
+		g_source_destroy (is->priv->idle_pending);
+		g_source_unref (is->priv->idle_pending);
 	}
 
-	job = camel_imapx_job_new (cancellable);
-	job->type = IMAPX_JOB_UPDATE_QUOTA_INFO;
-	job->start = imapx_job_update_quota_info_start;
-	job->pri = IMAPX_PRIORITY_UPDATE_QUOTA_INFO;
-
-	camel_imapx_job_set_mailbox (job, mailbox);
+	g_clear_object (&is->priv->idle_mailbox);
+	if (mailbox)
+		is->priv->idle_mailbox = g_object_ref (mailbox);
 
-	success = imapx_submit_job (is, job, error);
+	is->priv->idle_state = IMAPX_IDLE_STATE_SCHEDULED;
+	is->priv->idle_pending = g_timeout_source_new_seconds (IMAPX_IDLE_WAIT_SECONDS);
+	g_source_set_callback (
+		is->priv->idle_pending, imapx_server_run_idle_thread_cb,
+		imapx_weak_ref_new (is), (GDestroyNotify) imapx_weak_ref_free);
+	g_source_attach (is->priv->idle_pending, NULL);
 
-	camel_imapx_job_unref (job);
+	g_mutex_unlock (&is->priv->idle_lock);
 
-	return success;
+	return TRUE;
 }
 
-GPtrArray *
-camel_imapx_server_uid_search (CamelIMAPXServer *is,
-                               CamelIMAPXMailbox *mailbox,
-                               const gchar *criteria,
-                               GCancellable *cancellable,
-                               GError **error)
+static void
+imapx_server_wait_idle_stop_cancelled_cb (GCancellable *cancellable,
+					  gpointer user_data)
 {
-	CamelIMAPXJob *job;
-	SearchData *data;
-	GPtrArray *results = NULL;
+	CamelIMAPXServer *is = user_data;
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
-	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), NULL);
-	g_return_val_if_fail (criteria != NULL, NULL);
-
-	data = g_slice_new0 (SearchData);
-	data->criteria = g_strdup (criteria);
-
-	job = camel_imapx_job_new (cancellable);
-	job->type = IMAPX_JOB_UID_SEARCH;
-	job->start = imapx_job_uid_search_start;
-	job->pri = IMAPX_PRIORITY_SEARCH;
+	g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is));
 
-	camel_imapx_job_set_mailbox (job, mailbox);
+	g_mutex_lock (&is->priv->idle_lock);
+	g_cond_broadcast (&is->priv->idle_cond);
+	g_mutex_unlock (&is->priv->idle_lock);
+}
 
-	camel_imapx_job_set_data (
-		job, data, (GDestroyNotify) search_data_free);
+gboolean
+camel_imapx_server_stop_idle_sync (CamelIMAPXServer *is,
+				   GCancellable *cancellable,
+				   GError **error)
+{
+	GCancellable *idle_cancellable;
+	gulong handler_id = 0;
+	gboolean success = TRUE;
 
-	if (imapx_submit_job (is, job, error)) {
-		guint ii;
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
 
-		/* Convert the numeric UIDs to strings. */
+	g_mutex_lock (&is->priv->idle_lock);
 
-		g_return_val_if_fail (data->results != NULL, NULL);
+	if (is->priv->idle_state == IMAPX_IDLE_STATE_OFF) {
+		g_mutex_unlock (&is->priv->idle_lock);
+		return TRUE;
+	} else if (is->priv->idle_state == IMAPX_IDLE_STATE_SCHEDULED) {
+		if (is->priv->idle_pending) {
+			g_source_destroy (is->priv->idle_pending);
+			g_source_unref (is->priv->idle_pending);
+			is->priv->idle_pending = NULL;
+		}
 
-		results = g_ptr_array_new_full (
-			data->results->len,
-			(GDestroyNotify) camel_pstring_free);
+		is->priv->idle_state = IMAPX_IDLE_STATE_OFF;
+		g_cond_broadcast (&is->priv->idle_cond);
+	}
 
-		for (ii = 0; ii < data->results->len; ii++) {
-			const gchar *pooled_uid;
-			guint64 numeric_uid;
-			gchar *alloced_uid;
+	idle_cancellable = is->priv->idle_cancellable ? g_object_ref (is->priv->idle_cancellable) : NULL;
 
-			numeric_uid = g_array_index (
-				data->results, guint64, ii);
-			alloced_uid = g_strdup_printf (
-				"%" G_GUINT64_FORMAT, numeric_uid);
-			pooled_uid = camel_pstring_add (alloced_uid, TRUE);
-			g_ptr_array_add (results, (gpointer) pooled_uid);
-		}
-	}
+	g_clear_object (&is->priv->idle_cancellable);
+	g_clear_object (&is->priv->idle_mailbox);
+	is->priv->idle_stamp++;
 
-	camel_imapx_job_unref (job);
+	if (cancellable) {
+		g_mutex_unlock (&is->priv->idle_lock);
 
-	return results;
-}
+		/* Do not hold the idle_lock here, because the callback can be called
+		   immediately, which leads to a deadlock inside it. */
+		handler_id = g_cancellable_connect (cancellable, G_CALLBACK (imapx_server_wait_idle_stop_cancelled_cb), is, NULL);
 
-gboolean
-camel_imapx_server_folder_name_in_jobs (CamelIMAPXServer *imapx_server,
-					const gchar *folder_path)
-{
-	gboolean res;
+		g_mutex_lock (&is->priv->idle_lock);
+	}
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), FALSE);
-	g_return_val_if_fail (folder_path != NULL, FALSE);
+	while (is->priv->idle_state == IMAPX_IDLE_STATE_PREPARING &&
+	       !g_cancellable_is_cancelled (cancellable)) {
+		g_cond_wait (&is->priv->idle_cond, &is->priv->idle_lock);
+	}
 
-	g_mutex_lock (&imapx_server->priv->jobs_prop_lock);
+	if (is->priv->idle_state == IMAPX_IDLE_STATE_RUNNING &&
+	    !g_cancellable_is_cancelled (cancellable)) {
+		is->priv->idle_state = IMAPX_IDLE_STATE_STOPPING;
+		g_cond_broadcast (&is->priv->idle_cond);
+		g_mutex_unlock (&is->priv->idle_lock);
 
-	res = GPOINTER_TO_INT (g_hash_table_lookup (imapx_server->priv->jobs_prop_folder_paths, folder_path)) > 0;
+		g_mutex_lock (&is->priv->stream_lock);
+		if (is->priv->output_stream) {
+			gint previous_timeout = -1;
 
-	g_mutex_unlock (&imapx_server->priv->jobs_prop_lock);
+			/* Set the connection timeout to some short time, no need to wait for it for too long */
+			if (is->priv->connection)
+				previous_timeout = imapx_server_set_connection_timeout (is->priv->connection, 5);
+
+			success = g_output_stream_flush (is->priv->output_stream, cancellable, error);
+			success = success && g_output_stream_write_all (is->priv->output_stream, "DONE\r\n", 6, NULL, cancellable, error);
+			success = success && g_output_stream_flush (is->priv->output_stream, cancellable, error);
 
-	return res;
-}
+			if (previous_timeout >= 0 && is->priv->connection)
+				imapx_server_set_connection_timeout (is->priv->connection, previous_timeout);
+		} else {
+			success = FALSE;
 
-gboolean
-camel_imapx_server_has_expensive_command (CamelIMAPXServer *imapx_server)
-{
-	gboolean res;
+			/* This message won't get into UI. */
+			g_set_error_literal (error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT,
+				"Reconnect after couldn't issue DONE command");
+		}
+		g_mutex_unlock (&is->priv->stream_lock);
+		g_mutex_lock (&is->priv->idle_lock);
+	}
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), FALSE);
+	while (success && is->priv->idle_state != IMAPX_IDLE_STATE_OFF &&
+	       !g_cancellable_is_cancelled (cancellable)) {
+		g_cond_wait (&is->priv->idle_cond, &is->priv->idle_lock);
+	}
 
-	g_mutex_lock (&imapx_server->priv->jobs_prop_lock);
+	g_mutex_unlock (&is->priv->idle_lock);
 
-	res = imapx_server->priv->jobs_prop_expensive_command_count > 0;
+	if (cancellable && handler_id)
+		g_cancellable_disconnect (cancellable, handler_id);
 
-	g_mutex_unlock (&imapx_server->priv->jobs_prop_lock);
+	if (success && g_cancellable_is_cancelled (cancellable)) {
+		g_clear_error (error);
 
-	return res;
-}
+		success = FALSE;
 
-gint
-camel_imapx_server_get_command_count (CamelIMAPXServer *imapx_server)
-{
-	guint32 res;
+		/* This message won't get into UI. */
+		g_set_error_literal (error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT,
+			"Reconnect after cancelled IDLE stop command");
+	}
 
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), -1);
+	if (!success) {
+		if (idle_cancellable)
+			g_cancellable_cancel (idle_cancellable);
 
-	g_mutex_lock (&imapx_server->priv->jobs_prop_lock);
+		g_mutex_lock (&is->priv->idle_lock);
+		is->priv->idle_state = IMAPX_IDLE_STATE_OFF;
+		g_mutex_unlock (&is->priv->idle_lock);
 
-	res = imapx_server->priv->jobs_prop_command_count;
+		imapx_disconnect (is);
+	}
 
-	g_mutex_unlock (&imapx_server->priv->jobs_prop_lock);
+	g_clear_object (&idle_cancellable);
 
-	return res;
+	return success;
 }
 
 /**
@@ -9670,210 +6561,77 @@ camel_imapx_server_register_untagged_han
 	return previous;
 }
 
-/**
- * camel_imapx_server_is_job_in_queue:
- * @imapx_server: a #CamelIMAPXServer instance
- * @mailbox: a mailbox to search job for
- * @job_type: a job type specifier to search for
- * @uid: optional message UID for which the job might be searched
- *
- * Searches queue of jobs for the particular job. The returned job
- * is referenced for thread safety, unref it with camel_imapx_job_unref().
- *
- * Returns: %NULL, if such job could not be found, or a referenced job.
- **/
-CamelIMAPXJob *
-camel_imapx_server_ref_job (CamelIMAPXServer *imapx_server,
-			    CamelIMAPXMailbox *mailbox,
-			    guint32 job_type,
-			    const gchar *uid)
+/* This function is not thread-safe. */
+const struct _capability_info *
+camel_imapx_server_get_capability_info (CamelIMAPXServer *is)
 {
-	GList *head, *link;
-	CamelIMAPXJob *job = NULL;
-	gboolean found = FALSE;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), NULL);
-
-	QUEUE_LOCK (imapx_server);
-
-	head = g_queue_peek_head_link (&imapx_server->jobs);
-
-	for (link = head; link != NULL; link = g_list_next (link)) {
-		job = (CamelIMAPXJob *) link->data;
-
-		if (!job || !(job->type & job_type))
-			continue;
-
-		if (camel_imapx_job_matches (job, mailbox, uid)) {
-			found = TRUE;
-			camel_imapx_job_ref (job);
-			break;
-		}
-	}
-
-	QUEUE_UNLOCK (imapx_server);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
 
-	return found ? job : NULL;
+	return is->priv->cinfo;
 }
 
-/**
- * camel_imapx_server_shutdown:
- * @is: a #CamelIMAPXServer
- * @error: a #GError with which cancel any pending jobs
- *
- * Signals the server to shut down command processing. A #CamelIMAPXStore
- * should call this immediately before unreferencing its server instance.
- * Note, the server instance may linger a short time after this function
- * returns as its own worker threads finish.
- *
- * Since: 3.12
- **/
-void
-camel_imapx_server_shutdown (CamelIMAPXServer *is,
-			     const GError *error)
+gboolean
+camel_imapx_server_have_capability (CamelIMAPXServer *is,
+				    guint32 capability)
 {
-	GCancellable *cancellable;
-	GError *shutdown_error_copy = NULL;
-
-	g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is));
-
-	QUEUE_LOCK (is);
-
-	is->state = IMAPX_SHUTDOWN;
-
-	cancellable = g_weak_ref_get (&is->priv->parser_cancellable);
-
-	QUEUE_UNLOCK (is);
-
-	if (!error) {
-		shutdown_error_copy = imapx_server_dup_shutdown_error (is);
-		error = shutdown_error_copy;
-	}
-
-	if (error) {
-		imapx_abort_all_commands (is, error);
-	} else {
-		GError *local_error = NULL;
+	gboolean have;
 
-		g_set_error (
-			&local_error, CAMEL_SERVICE_ERROR,
-			CAMEL_SERVICE_ERROR_UNAVAILABLE,
-			"Shutting down");
-
-		imapx_abort_all_commands (is, local_error);
-
-		g_clear_error (&local_error);
-	}
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
 
-	g_main_loop_quit (is->priv->idle_main_loop);
-	g_main_loop_quit (is->priv->parser_main_loop);
+	g_mutex_lock (&is->priv->stream_lock);
+	have = is->priv->cinfo != NULL && (is->priv->cinfo->capa & capability) != 0;
+	g_mutex_unlock (&is->priv->stream_lock);
 
-	g_cancellable_cancel (cancellable);
-	g_clear_object (&cancellable);
-	g_clear_error (&shutdown_error_copy);
+	return have;
 }
 
-static const gchar *
-imapx_server_get_job_type_name (CamelIMAPXJob *job)
+gboolean
+camel_imapx_server_lack_capability (CamelIMAPXServer *is,
+				    guint32 capability)
 {
-	if (!job)
-		return "[null]";
+	gboolean lack;
 
-	switch (job->type) {
-	case IMAPX_JOB_GET_MESSAGE:
-		return "GET_MESSAGE";
-	case IMAPX_JOB_APPEND_MESSAGE:
-		return "APPEND_MESSAGE";
-	case IMAPX_JOB_COPY_MESSAGE:
-		return "COPY_MESSAGE";
-	case IMAPX_JOB_FETCH_NEW_MESSAGES:
-		return "FETCH_NEW_MESSAGES";
-	case IMAPX_JOB_REFRESH_INFO:
-		return "REFRESH_INFO";
-	case IMAPX_JOB_SYNC_CHANGES:
-		return "SYNC_CHANGES";
-	case IMAPX_JOB_EXPUNGE:
-		return "EXPUNGE";
-	case IMAPX_JOB_NOOP:
-		return "NOOP";
-	case IMAPX_JOB_IDLE:
-		return "IDLE";
-	case IMAPX_JOB_LIST:
-		return "LIST";
-	case IMAPX_JOB_CREATE_MAILBOX:
-		return "CREATE_MAILBOX";
-	case IMAPX_JOB_DELETE_MAILBOX:
-		return "DELETE_MAILBOX";
-	case IMAPX_JOB_RENAME_MAILBOX:
-		return "RENAME_MAILBOX";
-	case IMAPX_JOB_SUBSCRIBE_MAILBOX:
-		return "SUBSCRIBE_MAILBOX";
-	case IMAPX_JOB_UNSUBSCRIBE_MAILBOX:
-		return "UNSUBSCRIBE_MAILBOX";
-	case IMAPX_JOB_UPDATE_QUOTA_INFO:
-		return "UPDATE_QUOTA_INFO";
-	case IMAPX_JOB_UID_SEARCH:
-		return "UID_SEARCH";
-	}
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+
+	g_mutex_lock (&is->priv->stream_lock);
+	lack = is->priv->cinfo != NULL && (is->priv->cinfo->capa & capability) == 0;
+	g_mutex_unlock (&is->priv->stream_lock);
 
-	return "???";
+	return lack;
 }
 
-static void
-imapx_server_dump_one_queue (CamelIMAPXCommandQueue *queue,
-			     const gchar *queue_name)
+gchar
+camel_imapx_server_get_tagprefix (CamelIMAPXServer *is)
 {
-	GList *iter;
-	gint ii;
-
-	g_return_if_fail (queue != NULL);
-	g_return_if_fail (queue_name != NULL);
-
-	if (camel_imapx_command_queue_is_empty (queue))
-		return;
-
-	printf ("      Content of '%s':\n", queue_name);
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), 0);
 
-	for (ii = 0, iter = camel_imapx_command_queue_peek_head_link (queue); iter != NULL; iter = g_list_next (iter), ii++) {
-		CamelIMAPXCommand *ic = iter->data;
-		CamelIMAPXJob *job = camel_imapx_command_get_job (ic);
-
-		printf ("         [%d] command:%p for job:%p (type:0x%x %s)\n", ii, ic, job, job ? job->type : 0, imapx_server_get_job_type_name (job));
-	}
+	return is->priv->tagprefix;
 }
 
-/* for debugging purposes only */
 void
-camel_imapx_server_dump_queue_status (CamelIMAPXServer *imapx_server)
+camel_imapx_server_set_tagprefix (CamelIMAPXServer *is,
+				  gchar tagprefix)
 {
-	g_return_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server));
-
-	QUEUE_LOCK (imapx_server);
+	g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is));
+	g_return_if_fail ((tagprefix >= 'A' && tagprefix <= 'Z') || (tagprefix >= 'a' && tagprefix <= 'z'));
 
-	printf ("   Queue status for server %p: jobs:%d queued:%d active:%d done:%d\n", imapx_server,
-		g_queue_get_length (&imapx_server->jobs),
-		camel_imapx_command_queue_get_length (imapx_server->queue),
-		camel_imapx_command_queue_get_length (imapx_server->active),
-		camel_imapx_command_queue_get_length (imapx_server->done));
+	is->priv->tagprefix = tagprefix;
+}
 
-	if (!g_queue_is_empty (&imapx_server->jobs)) {
-		GList *iter;
-		gint ii;
+CamelIMAPXCommand *
+camel_imapx_server_ref_current_command (CamelIMAPXServer *is)
+{
+	CamelIMAPXCommand *command;
 
-		printf ("      Content of 'jobs':\n");
+	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
 
-		for (ii = 0, iter = g_queue_peek_head_link (&imapx_server->jobs); iter != NULL; iter = g_list_next (iter), ii++) {
-			CamelIMAPXJob *job = iter->data;
+	COMMAND_LOCK (is);
 
-			printf ("         [%d] job:%p (type:0x%x %s) with pending commands:%d\n", ii, job, job ? job->type : 0,
-				imapx_server_get_job_type_name (job),
-				job ? g_atomic_int_get (&job->commands) : -1);
-		}
-	}
+	command = is->priv->current_command;
+	if (command)
+		camel_imapx_command_ref (command);
 
-	imapx_server_dump_one_queue (imapx_server->queue, "queue");
-	imapx_server_dump_one_queue (imapx_server->active, "active");
-	imapx_server_dump_one_queue (imapx_server->done, "done");
+	COMMAND_UNLOCK (is);
 
-	QUEUE_UNLOCK (imapx_server);
+	return command;
 }
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.h.imapx-update-to-upstream	2014-11-07 08:34:59.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.h	2016-08-15 13:52:41.974976329 +0200
@@ -2,17 +2,18 @@
 /*
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
- * This library is free software; you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
  */
 
 #ifndef CAMEL_IMAPX_SERVER_H
@@ -105,37 +106,14 @@ struct _CamelIMAPXUntaggedRespHandlerDes
 struct _CamelIMAPXServer {
 	GObject parent;
 	CamelIMAPXServerPrivate *priv;
-
-	/* Info about the current connection */
-	struct _capability_info *cinfo;
-
-	/* incoming jobs */
-	GQueue jobs;
-
-	gchar tagprefix;
-	gint state : 4;
-
-	/* Current command/work queue.  All commands are stored in one list,
-	 * all the time, so they can be cleaned up in exception cases */
-	GRecMutex queue_lock;
-	CamelIMAPXCommand *literal;
-	CamelIMAPXCommandQueue *queue;
-	CamelIMAPXCommandQueue *active;
-	CamelIMAPXCommandQueue *done;
-
-	gboolean use_qresync;
 };
 
 struct _CamelIMAPXServerClass {
 	GObjectClass parent_class;
 
 	/* Signals */
-	void		(*mailbox_select)	(CamelIMAPXServer *is,
+	void		(*refresh_mailbox)	(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox);
-	void		(*mailbox_closed)	(CamelIMAPXServer *is,
-						 CamelIMAPXMailbox *mailbox);
-	void		(*shutdown)		(CamelIMAPXServer *is,
-						 const GError *error);
 };
 
 GType		camel_imapx_server_get_type	(void);
@@ -151,57 +129,100 @@ GOutputStream *	camel_imapx_server_ref_o
 						(CamelIMAPXServer *is);
 CamelIMAPXMailbox *
 		camel_imapx_server_ref_selected	(CamelIMAPXServer *is);
-gboolean	camel_imapx_server_connect	(CamelIMAPXServer *is,
+CamelIMAPXMailbox *
+		camel_imapx_server_ref_pending_or_selected
+						(CamelIMAPXServer *is);
+const struct _capability_info *
+		camel_imapx_server_get_capability_info
+						(CamelIMAPXServer *is);
+gboolean	camel_imapx_server_have_capability
+						(CamelIMAPXServer *is,
+						 guint32 capability);
+gboolean	camel_imapx_server_lack_capability
+						(CamelIMAPXServer *is,
+						 guint32 capability);
+gchar		camel_imapx_server_get_tagprefix
+						(CamelIMAPXServer *is);
+void		camel_imapx_server_set_tagprefix
+						(CamelIMAPXServer *is,
+						 gchar tagprefix);
+CamelIMAPXCommand *
+		camel_imapx_server_ref_current_command
+						(CamelIMAPXServer *is);
+gboolean	camel_imapx_server_connect_sync	(CamelIMAPXServer *is,
 						 GCancellable *cancellable,
 						 GError **error);
-gboolean	imapx_connect_to_server		(CamelIMAPXServer *is,
+gboolean	camel_imapx_server_disconnect_sync
+						(CamelIMAPXServer *is,
 						 GCancellable *cancellable,
 						 GError **error);
 gboolean	camel_imapx_server_is_connected	(CamelIMAPXServer *imapx_server);
 CamelAuthenticationResult
-		camel_imapx_server_authenticate	(CamelIMAPXServer *is,
+		camel_imapx_server_authenticate_sync
+						(CamelIMAPXServer *is,
 						 const gchar *mechanism,
 						 GCancellable *cancellable,
 						 GError **error);
-void		camel_imapx_server_shutdown	(CamelIMAPXServer *is,
-						 const GError *error);
-gboolean	camel_imapx_server_list		(CamelIMAPXServer *is,
+gboolean	camel_imapx_server_query_auth_types_sync
+						(CamelIMAPXServer *is,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_server_mailbox_selected
+						(CamelIMAPXServer *is,
+						 CamelIMAPXMailbox *mailbox);
+gboolean	camel_imapx_server_ensure_selected_sync
+						(CamelIMAPXServer *is,
+						 CamelIMAPXMailbox *mailbox,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_server_process_command_sync
+						(CamelIMAPXServer *is,
+						 CamelIMAPXCommand *ic,
+						 const gchar *error_prefix,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_server_list_sync	(CamelIMAPXServer *is,
 						 const gchar *pattern,
 						 CamelStoreGetFolderInfoFlags flags,
 						 GCancellable *cancellable,
 						 GError **error);
-CamelFolderChangeInfo *
-		camel_imapx_server_refresh_info	(CamelIMAPXServer *is,
+gboolean	camel_imapx_server_refresh_info_sync
+						(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox,
 						 GCancellable *cancellable,
 						 GError **error);
-gboolean	camel_imapx_server_sync_changes	(CamelIMAPXServer *is,
+gboolean	camel_imapx_server_sync_changes_sync
+						(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox,
+						 gboolean can_influence_flags,
 						 GCancellable *cancellable,
 						 GError **error);
-gboolean	camel_imapx_server_expunge	(CamelIMAPXServer *is,
+gboolean	camel_imapx_server_expunge_sync	(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox,
 						 GCancellable *cancellable,
 						 GError **error);
-gboolean	camel_imapx_server_noop		(CamelIMAPXServer *is,
+gboolean	camel_imapx_server_noop_sync	(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox,
 						 GCancellable *cancellable,
 						 GError **error);
-CamelStream *	camel_imapx_server_get_message	(CamelIMAPXServer *is,
+CamelStream *	camel_imapx_server_get_message_sync
+						(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox,
 						 CamelFolderSummary *summary,
 						 CamelDataCache *message_cache,
 						 const gchar *message_uid,
 						 GCancellable *cancellable,
 						 GError **error);
-gboolean	camel_imapx_server_copy_message	(CamelIMAPXServer *is,
+gboolean	camel_imapx_server_copy_message_sync
+						(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox,
 						 CamelIMAPXMailbox *destination,
 						 GPtrArray *uids,
 						 gboolean delete_originals,
+						 gboolean remove_deleted_flags,
 						 GCancellable *cancellable,
 						 GError **error);
-gboolean	camel_imapx_server_append_message
+gboolean	camel_imapx_server_append_message_sync
 						(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox,
 						 CamelFolderSummary *summary,
@@ -211,70 +232,73 @@ gboolean	camel_imapx_server_append_messa
 						 gchar **append_uid,
 						 GCancellable *cancellable,
 						 GError **error);
-gboolean	camel_imapx_server_sync_message	(CamelIMAPXServer *is,
+gboolean	camel_imapx_server_sync_message_sync
+						(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox,
 						 CamelFolderSummary *summary,
 						 CamelDataCache *message_cache,
 						 const gchar *message_uid,
 						 GCancellable *cancellable,
 						 GError **error);
-gboolean	camel_imapx_server_create_mailbox
+gboolean	camel_imapx_server_create_mailbox_sync
 						(CamelIMAPXServer *is,
 						 const gchar *mailbox_name,
 						 GCancellable *cancellable,
 						 GError **error);
-gboolean	camel_imapx_server_delete_mailbox
+gboolean	camel_imapx_server_delete_mailbox_sync
 						(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox,
 						 GCancellable *cancellable,
 						 GError **error);
-gboolean	camel_imapx_server_rename_mailbox
+gboolean	camel_imapx_server_rename_mailbox_sync
 						(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox,
 						 const gchar *new_mailbox_name,
 						 GCancellable *cancellable,
 						 GError **error);
-gboolean	camel_imapx_server_subscribe_mailbox
+gboolean	camel_imapx_server_subscribe_mailbox_sync
 						(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox,
 						 GCancellable *cancellable,
 						 GError **error);
-gboolean	camel_imapx_server_unsubscribe_mailbox
+gboolean	camel_imapx_server_unsubscribe_mailbox_sync
 						(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox,
 						 GCancellable *cancellable,
 						 GError **error);
-gboolean	camel_imapx_server_update_quota_info
+gboolean	camel_imapx_server_update_quota_info_sync
 						(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox,
 						 GCancellable *cancellable,
 						 GError **error);
-GPtrArray *	camel_imapx_server_uid_search	(CamelIMAPXServer *is,
+GPtrArray *	camel_imapx_server_uid_search_sync
+						(CamelIMAPXServer *is,
 						 CamelIMAPXMailbox *mailbox,
-						 const gchar *criteria,
+						 const gchar *criteria_prefix,
+						 const gchar *search_key,
+						 const gchar * const *words,
 						 GCancellable *cancellable,
 						 GError **error);
-gboolean	camel_imapx_server_folder_name_in_jobs
-						(CamelIMAPXServer *imapx_server,
-						 const gchar *folder_path);
-gboolean	camel_imapx_server_has_expensive_command
-						(CamelIMAPXServer *imapx_server);
-gint		camel_imapx_server_get_command_count
-						(CamelIMAPXServer *imapx_server);
+gboolean	camel_imapx_server_can_use_idle	(CamelIMAPXServer *is);
+gboolean	camel_imapx_server_is_in_idle	(CamelIMAPXServer *is);
+CamelIMAPXMailbox *
+		camel_imapx_server_ref_idle_mailbox
+						(CamelIMAPXServer *is);
+gboolean	camel_imapx_server_schedule_idle_sync
+						(CamelIMAPXServer *is,
+						 CamelIMAPXMailbox *mailbox,
+						 GCancellable *cancellable,
+						 GError **error);
+gboolean	camel_imapx_server_stop_idle_sync
+						(CamelIMAPXServer *is,
+						 GCancellable *cancellable,
+						 GError **error);
+
 const CamelIMAPXUntaggedRespHandlerDesc *
 		camel_imapx_server_register_untagged_handler
 						(CamelIMAPXServer *is,
 						 const gchar *untagged_response,
 						 const CamelIMAPXUntaggedRespHandlerDesc *desc);
-struct _CamelIMAPXJob *
-		camel_imapx_server_ref_job	(CamelIMAPXServer *imapx_server,
-						 CamelIMAPXMailbox *mailbox,
-						 guint32 job_type,
-						 const gchar *uid);
-
-/* for debugging purposes only */
-void		camel_imapx_server_dump_queue_status
-						(CamelIMAPXServer *imapx_server);
 G_END_DECLS
 
 #endif /* CAMEL_IMAPX_SERVER_H */
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.c.imapx-update-to-upstream	2014-12-02 16:09:44.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.c	2016-08-15 13:52:41.974976329 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-settings.c
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -31,9 +31,9 @@ struct _CamelIMAPXSettingsPrivate {
 	gchar *real_trash_path;
 	gchar *shell_command;
 
-	guint batch_fetch_count;
 	guint concurrent_connections;
 
+	gboolean use_multi_fetch;
 	gboolean check_all;
 	gboolean check_subscribed;
 	gboolean filter_all;
@@ -55,7 +55,7 @@ struct _CamelIMAPXSettingsPrivate {
 enum {
 	PROP_0,
 	PROP_AUTH_MECHANISM,
-	PROP_BATCH_FETCH_COUNT,
+	PROP_USE_MULTI_FETCH,
 	PROP_CHECK_ALL,
 	PROP_CHECK_SUBSCRIBED,
 	PROP_CONCURRENT_CONNECTIONS,
@@ -102,10 +102,10 @@ imapx_settings_set_property (GObject *ob
 				g_value_get_string (value));
 			return;
 
-		case PROP_BATCH_FETCH_COUNT:
-			camel_imapx_settings_set_batch_fetch_count (
+		case PROP_USE_MULTI_FETCH:
+			camel_imapx_settings_set_use_multi_fetch (
 				CAMEL_IMAPX_SETTINGS (object),
-				g_value_get_uint (value));
+				g_value_get_boolean (value));
 			return;
 
 		case PROP_CHECK_ALL:
@@ -270,10 +270,10 @@ imapx_settings_get_property (GObject *ob
 				CAMEL_NETWORK_SETTINGS (object)));
 			return;
 
-		case PROP_BATCH_FETCH_COUNT:
-			g_value_set_uint (
+		case PROP_USE_MULTI_FETCH:
+			g_value_set_boolean (
 				value,
-				camel_imapx_settings_get_batch_fetch_count (
+				camel_imapx_settings_get_use_multi_fetch (
 				CAMEL_IMAPX_SETTINGS (object)));
 			return;
 
@@ -487,14 +487,12 @@ camel_imapx_settings_class_init (CamelIM
 
 	g_object_class_install_property (
 		object_class,
-		PROP_BATCH_FETCH_COUNT,
-		g_param_spec_uint (
-			"batch-fetch-count",
-			"Batch Fetch Count",
-			"Number of envelopes to fetch at once",
-			0,
-			G_MAXUINT,
-			500,
+		PROP_USE_MULTI_FETCH,
+		g_param_spec_boolean (
+			"use-multi-fetch",
+			"Use Multi Fetch",
+			"Whether allow downloading of large messages in chunks",
+			FALSE,
 			G_PARAM_READWRITE |
 			G_PARAM_CONSTRUCT |
 			G_PARAM_STATIC_STRINGS));
@@ -776,50 +774,46 @@ camel_imapx_settings_init (CamelIMAPXSet
 }
 
 /**
- * camel_imapx_settings_get_batch_fetch_count:
+ * camel_imapx_settings_get_use_multi_fetch:
  * @settings: a #CamelIMAPXSettings
  *
- * Returns the number of message envelopes to fetch at once.
- *
- * This is a tunable performance parameter and probably should not be
- * exposed in a graphical user interface.
+ * Returns whether large messages can be downloaded in chunks.
+ * The default is %TRUE, but some server can be slower when
+ * the messages are downloaded in parts, rather than in one call.
  *
- * Returns: number of message envelopes to fetch at once
+ * Returns: whether large messages can be downloaded in chunks
  *
- * Since: 3.2
+ * Since: 3.20
  **/
 guint
-camel_imapx_settings_get_batch_fetch_count (CamelIMAPXSettings *settings)
+camel_imapx_settings_get_use_multi_fetch (CamelIMAPXSettings *settings)
 {
 	g_return_val_if_fail (CAMEL_IS_IMAPX_SETTINGS (settings), 0);
 
-	return settings->priv->batch_fetch_count;
+	return settings->priv->use_multi_fetch;
 }
 
 /**
- * camel_imapx_settings_set_batch_fetch_count:
+ * camel_imapx_settings_set_use_multi_fetch:
  * @settings: a #CamelIMAPXSettings
- * @batch_fetch_count: number of message envelopes to fetch at once
+ * @use_multi_fetch: whether can download large messages in chunks
  *
- * Sets the number of message envelopes to fetch at once.
+ * Sets whether can download large messages in chunks.
  *
- * This is a tunable performance parameter and probably should not be
- * exposed in a graphical user interface.
- *
- * Since: 3.2
+ * Since: 3.20
  **/
 void
-camel_imapx_settings_set_batch_fetch_count (CamelIMAPXSettings *settings,
-                                            guint batch_fetch_count)
+camel_imapx_settings_set_use_multi_fetch (CamelIMAPXSettings *settings,
+					  guint use_multi_fetch)
 {
 	g_return_if_fail (CAMEL_IS_IMAPX_SETTINGS (settings));
 
-	if (settings->priv->batch_fetch_count == batch_fetch_count)
+	if (settings->priv->use_multi_fetch == use_multi_fetch)
 		return;
 
-	settings->priv->batch_fetch_count = batch_fetch_count;
+	settings->priv->use_multi_fetch = use_multi_fetch;
 
-	g_object_notify (G_OBJECT (settings), "batch-fetch-count");
+	g_object_notify (G_OBJECT (settings), "use-multi-fetch");
 }
 
 /**
@@ -915,7 +909,7 @@ camel_imapx_settings_set_check_subscribe
  *
  * Returns: the number of concurrent connections to use
  *
- * Since: 3.14
+ * Since: 3.16
  **/
 guint
 camel_imapx_settings_get_concurrent_connections (CamelIMAPXSettings *settings)
@@ -937,7 +931,7 @@ camel_imapx_settings_get_concurrent_conn
  * @concurrent_connections value will be clamped to these limits if
  * necessary.
  *
- * Since: 3.14
+ * Since: 3.16
  **/
 void
 camel_imapx_settings_set_concurrent_connections (CamelIMAPXSettings *settings,
@@ -1563,7 +1557,7 @@ camel_imapx_settings_set_use_namespace (
  *
  * Returns: whether to ignore namespace for other users
  *
- * Since: 3.12.9
+ * Since: 3.16
  **/
 gboolean
 camel_imapx_settings_get_ignore_other_users_namespace (CamelIMAPXSettings *settings)
@@ -1580,7 +1574,7 @@ camel_imapx_settings_get_ignore_other_us
  *
  * Sets whether to ignore other users namespace.
  *
- * Since: 3.12.9
+ * Since: 3.16
  **/
 void
 camel_imapx_settings_set_ignore_other_users_namespace (CamelIMAPXSettings *settings,
@@ -1604,7 +1598,7 @@ camel_imapx_settings_set_ignore_other_us
  *
  * Returns: whether to ignore namespace for shared folders
  *
- * Since: 3.12.9
+ * Since: 3.16
  **/
 gboolean
 camel_imapx_settings_get_ignore_shared_folders_namespace (CamelIMAPXSettings *settings)
@@ -1621,7 +1615,7 @@ camel_imapx_settings_get_ignore_shared_f
  *
  * Sets whether to ignore shared folders namespace.
  *
- * Since: 3.12.9
+ * Since: 3.16
  **/
 void
 camel_imapx_settings_set_ignore_shared_folders_namespace (CamelIMAPXSettings *settings,
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.h.imapx-update-to-upstream	2014-12-02 16:07:55.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.h	2016-08-15 13:52:41.974976329 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-settings.h
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -55,11 +55,11 @@ struct _CamelIMAPXSettingsClass {
 };
 
 GType		camel_imapx_settings_get_type	(void) G_GNUC_CONST;
-guint		camel_imapx_settings_get_batch_fetch_count
+guint		camel_imapx_settings_get_use_multi_fetch
 						(CamelIMAPXSettings *settings);
-void		camel_imapx_settings_set_batch_fetch_count
+void		camel_imapx_settings_set_use_multi_fetch
 						(CamelIMAPXSettings *settings,
-						 guint batch_fetch_count);
+						 guint use_multi_fetch);
 gboolean	camel_imapx_settings_get_check_all
 						(CamelIMAPXSettings *settings);
 void		camel_imapx_settings_set_check_all
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.c.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.c	2016-08-15 13:52:41.975976329 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-status-response.c
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -134,7 +134,7 @@ camel_imapx_status_response_new (CamelIM
 		goto fail;
 	if (tok != '(') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"status: expecting '('");
 		goto fail;
 	}
@@ -199,7 +199,7 @@ camel_imapx_status_response_new (CamelIM
 
 			default:
 				g_set_error (
-					error, CAMEL_IMAPX_ERROR, 1,
+					error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 					"unknown status attribute");
 				success = FALSE;
 				break;
@@ -218,7 +218,7 @@ camel_imapx_status_response_new (CamelIM
 
 	if (tok != ')') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"status: expecting ')' or attribute");
 		goto fail;
 	}
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.h.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.h	2016-08-15 13:52:41.975976329 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-status-response.h
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.c.imapx-update-to-upstream	2016-08-15 13:52:41.918976332 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.c	2016-08-15 13:52:41.977976329 +0200
@@ -1,21 +1,21 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* camel-imap-store.c : class for a imap store */
-/*
- * Authors: Michael Zucchi <notzed@ximian.com>
+/* camel-imap-store.c : class for a imap store
  *
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
- * This library is free software; you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Michael Zucchi <notzed@ximian.com>
  */
 
 #ifdef HAVE_CONFIG_H
@@ -59,7 +59,7 @@
 #define e(...) camel_imapx_debug(extra, __VA_ARGS__)
 
 struct _CamelIMAPXStorePrivate {
-	CamelIMAPXConnManager *con_man;
+	CamelIMAPXConnManager *conn_man;
 
 	CamelIMAPXServer *connecting_server;
 	gboolean is_concurrent_connection;
@@ -88,7 +88,8 @@ struct _CamelIMAPXStorePrivate {
 enum {
 	PROP_0,
 	PROP_CONNECTABLE,
-	PROP_HOST_REACHABLE
+	PROP_HOST_REACHABLE,
+	PROP_CONN_MANAGER
 };
 
 enum {
@@ -660,6 +661,13 @@ imapx_store_get_property (GObject *objec
 				camel_network_service_get_host_reachable (
 				CAMEL_NETWORK_SERVICE (object)));
 			return;
+
+		case PROP_CONN_MANAGER:
+			g_value_set_object (
+				value,
+				camel_imapx_store_get_conn_manager (
+				CAMEL_IMAPX_STORE (object)));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -672,9 +680,9 @@ imapx_store_dispose (GObject *object)
 
 	/* Force disconnect so we don't have it run later,
 	 * after we've cleaned up some stuff. */
-	if (imapx_store->priv->con_man != NULL) {
+	if (imapx_store->priv->conn_man != NULL) {
 		camel_service_disconnect_sync (CAMEL_SERVICE (imapx_store), FALSE, NULL, NULL);
-		g_clear_object (&imapx_store->priv->con_man);
+		g_clear_object (&imapx_store->priv->conn_man);
 	}
 
 	if (imapx_store->priv->settings_notify_handler_id > 0) {
@@ -771,17 +779,14 @@ imapx_connect_sync (CamelService *servic
                     GError **error)
 {
 	CamelIMAPXStore *imapx_store;
-	CamelIMAPXServer *imapx_server;
-	gboolean success;
-
-	imapx_store = CAMEL_IMAPX_STORE (service);
 
-	imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
-	success = imapx_server != NULL;
+	/* Chain up to parent's method. */
+	if (!CAMEL_SERVICE_CLASS (camel_imapx_store_parent_class)->connect_sync (service, cancellable, error))
+		return FALSE;
 
-	g_clear_object (&imapx_server);
+	imapx_store = CAMEL_IMAPX_STORE (service);
 
-	return success;
+	return camel_imapx_conn_manager_connect_sync (imapx_store->priv->conn_man, cancellable, error);
 }
 
 static gboolean
@@ -794,8 +799,8 @@ imapx_disconnect_sync (CamelService *ser
 
 	priv = CAMEL_IMAPX_STORE_GET_PRIVATE (service);
 
-	if (priv->con_man != NULL)
-		camel_imapx_conn_manager_close_connections (priv->con_man, NULL);
+	if (priv->conn_man != NULL)
+		camel_imapx_conn_manager_disconnect_sync (priv->conn_man, cancellable, error);
 
 	g_mutex_lock (&priv->server_lock);
 
@@ -803,7 +808,8 @@ imapx_disconnect_sync (CamelService *ser
 
 	g_mutex_unlock (&priv->server_lock);
 
-	return TRUE;
+	/* Chain up to parent's method. */
+	return CAMEL_SERVICE_CLASS (camel_imapx_store_parent_class)->disconnect_sync (service, clean, cancellable, error);
 }
 
 static CamelAuthenticationResult
@@ -818,12 +824,24 @@ imapx_authenticate_sync (CamelService *s
 
 	priv = CAMEL_IMAPX_STORE_GET_PRIVATE (service);
 
+	if (g_cancellable_set_error_if_cancelled (cancellable, error))
+		return CAMEL_AUTHENTICATION_ERROR;
+
 	/* This should have been set for us by connect_sync(). */
 	g_mutex_lock (&priv->server_lock);
+	if (!priv->connecting_server) {
+		g_mutex_unlock (&priv->server_lock);
+
+		g_set_error_literal (error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+			_("No IMAPx connection object provided"));
+
+		return CAMEL_AUTHENTICATION_ERROR;
+	}
+
 	imapx_server = g_object_ref (priv->connecting_server);
 	g_mutex_unlock (&priv->server_lock);
 
-	result = camel_imapx_server_authenticate (
+	result = camel_imapx_server_authenticate_sync (
 		imapx_server, mechanism, cancellable, error);
 
 	g_clear_object (&imapx_server);
@@ -851,28 +869,32 @@ imapx_query_auth_types_sync (CamelServic
 	GList *sasl_types = NULL;
 	GList *t, *next;
 	CamelIMAPXServer *server;
+	const struct _capability_info *cinfo;
 
 	imapx_store = CAMEL_IMAPX_STORE (service);
 
 	server = camel_imapx_server_new (imapx_store);
-	server->tagprefix = 'Z';
+	camel_imapx_server_set_tagprefix (server, 'Z');
+
+	g_signal_emit_by_name (imapx_store->priv->conn_man, "connection-created", 0, server);
 
-	if (!imapx_connect_to_server (server, cancellable, error))
+	if (!camel_imapx_server_query_auth_types_sync (server, cancellable, error))
 		goto exit;
 
+	cinfo = camel_imapx_server_get_capability_info (server);
+
 	sasl_types = camel_sasl_authtype_list (FALSE);
 	for (t = sasl_types; t; t = next) {
 		authtype = t->data;
 		next = t->next;
 
-		if (!server->cinfo || !g_hash_table_lookup (server->cinfo->auth_types, authtype->authproto)) {
+		if (!cinfo || !g_hash_table_lookup (cinfo->auth_types, authtype->authproto)) {
 			sasl_types = g_list_remove_link (sasl_types, t);
 			g_list_free_1 (t);
 		}
 	}
 
-	sasl_types = g_list_prepend (
-		sasl_types, &camel_imapx_password_authtype);
+	sasl_types = g_list_prepend (sasl_types, &camel_imapx_password_authtype);
 
 exit:
 	g_object_unref (server);
@@ -1003,6 +1025,7 @@ static CamelFolderInfo *
 get_folder_info_offline (CamelStore *store,
                          const gchar *top,
                          CamelStoreGetFolderInfoFlags flags,
+			 GCancellable *cancellable,
                          GError **error)
 {
 	CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (store);
@@ -1013,8 +1036,36 @@ get_folder_info_offline (CamelStore *sto
 	GPtrArray *folders;
 	GPtrArray *array;
 	gboolean use_subscriptions;
+	gint top_len;
 	guint ii;
 
+	if (g_strcmp0 (top, CAMEL_VTRASH_NAME) == 0 ||
+	    g_strcmp0 (top, CAMEL_VJUNK_NAME) == 0) {
+		CamelFolder *vfolder;
+
+		vfolder = camel_store_get_folder_sync (store, top, 0, cancellable, error);
+		if (!vfolder)
+			return NULL;
+
+		fi = imapx_store_build_folder_info (imapx_store, top, 0);
+		fi->unread = camel_folder_summary_get_unread_count (vfolder->summary);
+		fi->total = camel_folder_summary_get_saved_count (vfolder->summary);
+
+		if (g_strcmp0 (top, CAMEL_VTRASH_NAME) == 0)
+			fi->flags = (fi->flags & ~CAMEL_FOLDER_TYPE_MASK) |
+				CAMEL_FOLDER_VIRTUAL |
+				CAMEL_FOLDER_VTRASH |
+				CAMEL_FOLDER_TYPE_TRASH;
+		else
+			fi->flags = (fi->flags & ~CAMEL_FOLDER_TYPE_MASK) |
+				CAMEL_FOLDER_VIRTUAL |
+				CAMEL_FOLDER_TYPE_JUNK;
+
+		g_object_unref (vfolder);
+
+		return fi;
+	}
+
 	service = CAMEL_SERVICE (store);
 
 	settings = camel_service_ref_settings (service);
@@ -1033,6 +1084,8 @@ get_folder_info_offline (CamelStore *sto
 		top = "";
 	}
 
+	top_len = strlen (top);
+
 	/* folder_info_build will insert parent nodes as necessary and mark
 	 * them as noselect, which is information we actually don't have at
 	 * the moment. So let it do the right thing by bailing out if it's
@@ -1053,7 +1106,8 @@ get_folder_info_offline (CamelStore *sto
 		/* Filter by folder path. */
 		si_is_match =
 			(include_inbox && si_is_inbox) ||
-			g_str_has_prefix (folder_path, top);
+			(g_str_has_prefix (folder_path, top) && (top_len == 0 ||
+			!folder_path[top_len] || folder_path[top_len] == '/'));
 
 		if (!si_is_match)
 			continue;
@@ -1093,8 +1147,7 @@ get_folder_info_offline (CamelStore *sto
 			g_clear_object (&mailbox);
 		}
 
-		fi = imapx_store_build_folder_info (
-			imapx_store, folder_path, 0);
+		fi = imapx_store_build_folder_info (imapx_store, folder_path, 0);
 		fi->unread = si->unread;
 		fi->total = si->total;
 		if ((fi->flags & CAMEL_FOLDER_TYPE_MASK) != 0)
@@ -1118,6 +1171,19 @@ get_folder_info_offline (CamelStore *sto
 		if (!fi->child)
 			fi->flags |= CAMEL_FOLDER_NOCHILDREN;
 
+		if (fi->unread == -1 && fi->total == -1) {
+			CamelIMAPXMailbox *mailbox;
+
+			mailbox = camel_imapx_store_ref_mailbox (imapx_store, ((CamelIMAPXStoreInfo *) si)->mailbox_name);
+
+			if (mailbox) {
+				fi->unread = camel_imapx_mailbox_get_unseen (mailbox);
+				fi->total = camel_imapx_mailbox_get_messages (mailbox);
+			}
+
+			g_clear_object (&mailbox);
+		}
+
 		g_ptr_array_add (folders, fi);
 	}
 
@@ -1155,7 +1221,7 @@ collect_folder_info_for_list (CamelIMAPX
 }
 
 static gboolean
-fetch_folder_info_for_pattern (CamelIMAPXServer *server,
+fetch_folder_info_for_pattern (CamelIMAPXConnManager *conn_man,
                                CamelIMAPXNamespace *namespace,
                                const gchar *pattern,
                                CamelStoreGetFolderInfoFlags flags,
@@ -1168,22 +1234,9 @@ fetch_folder_info_for_pattern (CamelIMAP
 	GError *local_error = NULL;
 	gboolean success;
 
-	g_object_ref (server);
-
-	imapx_store = camel_imapx_server_ref_store (server);
-
-	success = camel_imapx_server_list (server, pattern, flags, cancellable, &local_error);
-
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		g_clear_error (&local_error);
-		g_clear_object (&server);
-
-		server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
-		if (server)
-			success = camel_imapx_server_list (server, pattern, flags, cancellable, &local_error);
-	}
+	imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
 
-	g_clear_object (&server);
+	success = camel_imapx_conn_manager_list_sync (conn_man, pattern, flags, cancellable, &local_error);
 
 	if (!success) {
 		g_clear_object (&imapx_store);
@@ -1220,34 +1273,18 @@ fetch_folder_info_for_pattern (CamelIMAP
 }
 
 static gboolean
-fetch_folder_info_for_inbox (CamelIMAPXServer *server,
+fetch_folder_info_for_inbox (CamelIMAPXConnManager *conn_man,
                              CamelStoreGetFolderInfoFlags flags,
                              GHashTable *folder_info_results,
                              GCancellable *cancellable,
                              GError **error)
 {
 	CamelIMAPXStore *imapx_store;
-	GError *local_error = NULL;
 	gboolean success;
 
-	g_object_ref (server);
-	imapx_store = camel_imapx_server_ref_store (server);
-
-	success = camel_imapx_server_list (server, "INBOX", flags, cancellable, &local_error);
+	imapx_store = camel_imapx_conn_manager_ref_store (conn_man);
 
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		g_clear_error (&local_error);
-		g_clear_object (&server);
-
-		server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
-		if (server)
-			success = camel_imapx_server_list (server, "INBOX", flags, cancellable, &local_error);
-	}
-
-	g_clear_object (&server);
-
-	if (local_error)
-		g_propagate_error (error, local_error);
+	success = camel_imapx_conn_manager_list_sync (conn_man, "INBOX", flags, cancellable, error);
 
 	if (success) {
 		CamelIMAPXMailbox *mailbox;
@@ -1266,7 +1303,7 @@ fetch_folder_info_for_inbox (CamelIMAPXS
 
 static gboolean
 fetch_folder_info_for_namespace_category (CamelIMAPXStore *imapx_store,
-					  CamelIMAPXServer *server,
+					  CamelIMAPXConnManager *conn_man,
                                           CamelIMAPXNamespaceCategory category,
                                           CamelStoreGetFolderInfoFlags flags,
                                           GHashTable *folder_info_results,
@@ -1298,7 +1335,7 @@ fetch_folder_info_for_namespace_category
 		pattern = g_strdup_printf ("%s*", ns_prefix);
 
 		success = fetch_folder_info_for_pattern (
-			server, namespace, pattern, flags,
+			conn_man, namespace, pattern, flags,
 			folder_info_results, cancellable, error);
 
 		g_free (pattern);
@@ -1316,7 +1353,7 @@ fetch_folder_info_for_namespace_category
 
 static gboolean
 fetch_folder_info_from_folder_path (CamelIMAPXStore *imapx_store,
-				    CamelIMAPXServer *server,
+				    CamelIMAPXConnManager *conn_man,
                                     const gchar *folder_path,
                                     CamelStoreGetFolderInfoFlags flags,
                                     GHashTable *folder_info_results,
@@ -1354,7 +1391,7 @@ fetch_folder_info_from_folder_path (Came
 	pattern = g_strdup_printf ("%s*", utf7_mailbox_name);
 
 	success = fetch_folder_info_for_pattern (
-		server, namespace, pattern, flags,
+		conn_man, namespace, pattern, flags,
 		folder_info_results, cancellable, error);
 
 	g_free (pattern);
@@ -1490,14 +1527,12 @@ sync_folders (CamelIMAPXStore *imapx_sto
               GCancellable *cancellable,
               GError **error)
 {
-	CamelIMAPXServer *server;
+	CamelIMAPXConnManager *conn_man;
 	GHashTable *folder_info_results;
 	gboolean update_folder_list;
 	gboolean success;
 
-	server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
-	if (server == NULL)
-		return FALSE;
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
 
 	/* mailbox name -> CamelFolderInfo */
 	folder_info_results = g_hash_table_new_full (
@@ -1520,13 +1555,13 @@ sync_folders (CamelIMAPXStore *imapx_sto
 
 	if (root_folder_path != NULL && *root_folder_path != '\0') {
 		success = fetch_folder_info_from_folder_path (
-			imapx_store, server, root_folder_path, flags,
+			imapx_store, conn_man, root_folder_path, flags,
 			folder_info_results, cancellable, error);
 	} else {
 		gboolean have_folder_info_for_inbox;
 
 		success = fetch_folder_info_for_namespace_category (
-			imapx_store, server, CAMEL_IMAPX_NAMESPACE_PERSONAL, flags |
+			imapx_store, conn_man, CAMEL_IMAPX_NAMESPACE_PERSONAL, flags |
 			(update_folder_list ? CAMEL_STORE_FOLDER_INFO_SUBSCRIBED : 0),
 			folder_info_results, cancellable, error);
 
@@ -1538,7 +1573,7 @@ sync_folders (CamelIMAPXStore *imapx_sto
 		 *     then LIST it explicitly. */
 		if (success && !have_folder_info_for_inbox)
 			success = fetch_folder_info_for_inbox (
-				server, flags, folder_info_results,
+				conn_man, flags, folder_info_results,
 				cancellable, error);
 	}
 
@@ -1588,8 +1623,6 @@ sync_folders (CamelIMAPXStore *imapx_sto
 exit:
 	g_hash_table_destroy (folder_info_results);
 
-	g_object_unref (server);
-
 	return success;
 }
 
@@ -1606,7 +1639,7 @@ imapx_refresh_finfo (CamelSession *sessi
 	display_name = camel_service_get_display_name (service);
 
 	camel_operation_push_message (
-		cancellable, _("Retrieving folder list for %s"),
+		cancellable, _("Retrieving folder list for '%s'"),
 		display_name);
 
 	if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
@@ -1628,42 +1661,30 @@ static void
 discover_inbox (CamelIMAPXStore *imapx_store,
                 GCancellable *cancellable)
 {
-	CamelIMAPXServer *imapx_server;
+	CamelIMAPXConnManager *conn_man;
 	CamelIMAPXMailbox *mailbox = NULL;
 	const gchar *attribute;
 
-	imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, NULL);
-
-	if (imapx_server == NULL)
-		return;
-
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
 	mailbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");
 
 	if (mailbox == NULL)
-		goto exit;
+		return;
 
 	attribute = CAMEL_IMAPX_LIST_ATTR_SUBSCRIBED;
 	if (!camel_imapx_mailbox_has_attribute (mailbox, attribute)) {
 		GError *local_error = NULL;
 		gboolean success;
 
-		success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
-
-		while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-			g_clear_error (&local_error);
-			g_clear_object (&imapx_server);
-
-			imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
-			if (imapx_server)
-				success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
+		success = camel_imapx_conn_manager_subscribe_mailbox_sync (conn_man, mailbox, cancellable, &local_error);
+		if (!success && !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+			g_warning ("%s: Failed to subscribe INBOX: %s", G_STRFUNC, local_error ? local_error->message : "Unknown error");
 		}
 
 		g_clear_error (&local_error);
 	}
 
-exit:
 	g_clear_object (&mailbox);
-	g_clear_object (&imapx_server);
 }
 
 static gboolean
@@ -1699,6 +1720,16 @@ imapx_can_refresh_folder (CamelStore *st
 	res = store_class->can_refresh_folder (store, info, &local_error) ||
 		check_all || (check_subscribed && subscribed);
 
+	if (!res && !local_error) {
+		CamelFolder *folder;
+
+		folder = camel_store_get_folder_sync (store, info->full_name, 0, NULL, &local_error);
+		if (folder && CAMEL_IS_IMAPX_FOLDER (folder))
+			res = camel_imapx_folder_get_check_folder (CAMEL_IMAPX_FOLDER (folder));
+
+		g_clear_object (&folder);
+	}
+
 	if (local_error != NULL)
 		g_propagate_error (error, local_error);
 
@@ -1818,7 +1849,7 @@ imapx_store_get_folder_info_sync (CamelS
 	g_mutex_lock (&imapx_store->priv->get_finfo_lock);
 
 	if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
-		fi = get_folder_info_offline (store, top, flags, error);
+		fi = get_folder_info_offline (store, top, flags, cancellable, error);
 		goto exit;
 	}
 
@@ -1828,7 +1859,7 @@ imapx_store_get_folder_info_sync (CamelS
 	}
 
 	/* XXX I don't know why the SUBSCRIBED flag matters here. */
-	if (!initial_setup && flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) {
+	if (!initial_setup && (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) != 0) {
 		time_t time_since_last_refresh;
 
 		time_since_last_refresh =
@@ -1836,24 +1867,29 @@ imapx_store_get_folder_info_sync (CamelS
 
 		if (time_since_last_refresh > FINFO_REFRESH_INTERVAL) {
 			CamelSession *session;
+			gchar *description;
 
 			imapx_store->priv->last_refresh_time = time (NULL);
 
 			session = camel_service_ref_session (service);
+			if (session) {
+				description = g_strdup_printf (_("Retrieving folder list for '%s'"), camel_service_get_display_name (service));
 
-			camel_session_submit_job (
-				session, (CamelSessionCallback)
-				imapx_refresh_finfo,
-				g_object_ref (store),
-				(GDestroyNotify) g_object_unref);
+				camel_session_submit_job (
+					session, /*description,*/ (CamelSessionCallback)
+					imapx_refresh_finfo,
+					g_object_ref (store),
+					(GDestroyNotify) g_object_unref);
 
-			g_object_unref (session);
+				g_object_unref (session);
+				g_free (description);
+			}
 		}
 	}
 
 	/* Avoid server interaction if the FAST flag is set. */
 	if (!initial_setup && flags & CAMEL_STORE_FOLDER_INFO_FAST) {
-		fi = get_folder_info_offline (store, top, flags, error);
+		fi = get_folder_info_offline (store, top, flags, cancellable, error);
 		goto exit;
 	}
 
@@ -1866,7 +1902,7 @@ imapx_store_get_folder_info_sync (CamelS
 	if (initial_setup && use_subscriptions)
 		discover_inbox (imapx_store, cancellable);
 
-	fi = get_folder_info_offline (store, top, flags, error);
+	fi = get_folder_info_offline (store, top, flags, cancellable, error);
 
 exit:
 	g_mutex_unlock (&imapx_store->priv->get_finfo_lock);
@@ -1980,7 +2016,7 @@ imapx_store_create_folder_sync (CamelSto
 	CamelIMAPXNamespaceResponse *namespace_response;
 	CamelIMAPXNamespace *namespace;
 	CamelIMAPXStore *imapx_store;
-	CamelIMAPXServer *imapx_server;
+	CamelIMAPXConnManager *conn_man;
 	CamelFolder *folder;
 	CamelIMAPXMailbox *parent_mailbox = NULL;
 	CamelFolderInfo *fi = NULL;
@@ -1990,13 +2026,9 @@ imapx_store_create_folder_sync (CamelSto
 	gchar *mailbox_name = NULL;
 	gchar separator;
 	gboolean success;
-	GError *local_error = NULL;
 
 	imapx_store = CAMEL_IMAPX_STORE (store);
-	imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
-
-	if (imapx_server == NULL)
-		return NULL;
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
 
 	if (parent_name == NULL || *parent_name == '\0')
 		goto check_namespace;
@@ -2067,19 +2099,7 @@ check_separator:
 	/* This also LISTs the mailbox after creating it, which
 	 * triggers the CamelIMAPXStore::mailbox-created signal
 	 * and all the local processing that goes along with it. */
-	success = camel_imapx_server_create_mailbox (imapx_server, mailbox_name, cancellable, &local_error);
-
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		g_clear_error (&local_error);
-		g_clear_object (&imapx_server);
-
-		imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
-		if (imapx_server)
-			success = camel_imapx_server_create_mailbox (imapx_server, mailbox_name, cancellable, &local_error);
-	}
-
-	if (local_error)
-		g_propagate_error (error, local_error);
+	success = camel_imapx_conn_manager_create_mailbox_sync (conn_man, mailbox_name, cancellable, error);
 
 	if (success) {
 		fi = imapx_store_build_folder_info (
@@ -2090,8 +2110,6 @@ check_separator:
 exit:
 	g_free (mailbox_name);
 
-	g_clear_object (&imapx_server);
-
 	return fi;
 }
 
@@ -2103,10 +2121,9 @@ imapx_store_delete_folder_sync (CamelSto
 {
 	CamelFolder *folder;
 	CamelIMAPXStore *imapx_store;
-	CamelIMAPXServer *imapx_server;
+	CamelIMAPXConnManager *conn_man;
 	CamelIMAPXMailbox *mailbox = NULL;
 	gboolean success = FALSE;
-	GError *local_error = NULL;
 
 	folder = camel_store_get_folder_sync (
 		store, folder_name, 0, cancellable, error);
@@ -2115,29 +2132,14 @@ imapx_store_delete_folder_sync (CamelSto
 		return FALSE;
 
 	imapx_store = CAMEL_IMAPX_STORE (store);
-	imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
-
-	if (imapx_server == NULL)
-		goto exit;
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
 
 	mailbox = camel_imapx_folder_list_mailbox (
 		CAMEL_IMAPX_FOLDER (folder), cancellable, error);
 	if (mailbox == NULL)
 		goto exit;
 
-	success = camel_imapx_server_delete_mailbox (imapx_server, mailbox, cancellable, &local_error);
-
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		g_clear_error (&local_error);
-		g_clear_object (&imapx_server);
-
-		imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
-		if (imapx_server)
-			success = camel_imapx_server_delete_mailbox (imapx_server, mailbox, cancellable, &local_error);
-	}
-
-	if (local_error)
-		g_propagate_error (error, local_error);
+	success = camel_imapx_conn_manager_delete_mailbox_sync (conn_man, mailbox, cancellable, error);
 
 	if (success)
 		imapx_delete_folder_from_cache (imapx_store, folder_name, TRUE);
@@ -2145,7 +2147,6 @@ imapx_store_delete_folder_sync (CamelSto
 exit:
 	g_clear_object (&folder);
 	g_clear_object (&mailbox);
-	g_clear_object (&imapx_server);
 
 	return success;
 }
@@ -2161,7 +2162,7 @@ imapx_store_rename_folder_sync (CamelSto
 	CamelService *service;
 	CamelSettings *settings;
 	CamelIMAPXStore *imapx_store;
-	CamelIMAPXServer *imapx_server;
+	CamelIMAPXConnManager *conn_man;
 	CamelIMAPXMailbox *mailbox = NULL;
 	CamelIMAPXMailbox *cloned_mailbox;
 	gchar *new_mailbox_name = NULL;
@@ -2184,10 +2185,7 @@ imapx_store_rename_folder_sync (CamelSto
 	 * in imapx_store_process_mailbox_attributes(). */
 	g_atomic_int_inc (&imapx_store->priv->syncing_folders);
 
-	imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
-
-	if (imapx_server == NULL)
-		goto exit;
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
 
 	folder = camel_store_get_folder_sync (
 		store, old, 0, cancellable, error);
@@ -2208,30 +2206,11 @@ imapx_store_rename_folder_sync (CamelSto
 	new_mailbox_name = camel_imapx_folder_path_to_mailbox (new, separator);
 
 	if (use_subscriptions) {
-		success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
-
-		while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-			g_clear_error (&local_error);
-			g_clear_object (&imapx_server);
-
-			imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
-			if (imapx_server)
-				success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
-		}
-
+		camel_imapx_conn_manager_unsubscribe_mailbox_sync (conn_man, mailbox, cancellable, &local_error);
 		g_clear_error (&local_error);
 	}
 
-	success = camel_imapx_server_rename_mailbox (imapx_server, mailbox, new_mailbox_name, cancellable, &local_error);
-
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		g_clear_error (&local_error);
-		g_clear_object (&imapx_server);
-
-		imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
-		if (imapx_server)
-			success = camel_imapx_server_rename_mailbox (imapx_server, mailbox, new_mailbox_name, cancellable, &local_error);
-	}
+	success = camel_imapx_conn_manager_rename_mailbox_sync (conn_man, mailbox, new_mailbox_name, cancellable, &local_error);
 
 	if (!success) {
 		if (local_error)
@@ -2241,19 +2220,14 @@ imapx_store_rename_folder_sync (CamelSto
 		if (use_subscriptions) {
 			gboolean success_2;
 
-			success_2 = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
-
-			while (!success_2 && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-				g_clear_error (&local_error);
-				g_clear_object (&imapx_server);
-
-				imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
-				if (imapx_server)
-					success_2 = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
+			success_2 = camel_imapx_conn_manager_subscribe_mailbox_sync (conn_man, mailbox, cancellable, &local_error);
+			if (!success_2) {
+				g_warning ("%s: Failed to subscribe '%s': %s", G_STRFUNC, camel_imapx_mailbox_get_name (mailbox),
+					local_error ? local_error->message : "Unknown error");
 			}
-
 			g_clear_error (&local_error);
 		}
+
 		goto exit;
 	}
 
@@ -2264,23 +2238,10 @@ imapx_store_rename_folder_sync (CamelSto
 	/* Create a cloned CamelIMAPXMailbox with the new mailbox name. */
 	cloned_mailbox = camel_imapx_mailbox_clone (mailbox, new_mailbox_name);
 
-	camel_imapx_folder_set_mailbox (
-		CAMEL_IMAPX_FOLDER (folder), cloned_mailbox);
+	camel_imapx_folder_set_mailbox (CAMEL_IMAPX_FOLDER (folder), cloned_mailbox);
 
 	if (use_subscriptions) {
-		success = camel_imapx_server_subscribe_mailbox (imapx_server, cloned_mailbox, cancellable, &local_error);
-
-		while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-			g_clear_error (&local_error);
-			g_clear_object (&imapx_server);
-
-			imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
-			if (imapx_server)
-				success = camel_imapx_server_subscribe_mailbox (imapx_server, cloned_mailbox, cancellable, &local_error);
-		}
-
-		if (local_error)
-			g_propagate_error (error, local_error);
+		success = camel_imapx_conn_manager_subscribe_mailbox_sync (conn_man, cloned_mailbox, cancellable, error);
 	}
 
 	g_clear_object (&cloned_mailbox);
@@ -2289,15 +2250,322 @@ exit:
 	g_free (new_mailbox_name);
 
 	g_clear_object (&mailbox);
-	g_clear_object (&imapx_server);
 
-	/* This enabled CamelStore signal emissions
+	/* This enables CamelStore signal emissions
 	 * in imapx_store_process_mailbox_attributes() again. */
 	g_atomic_int_dec_and_test (&imapx_store->priv->syncing_folders);
 
 	return success;
 }
 
+static gboolean
+imapx_is_gmail_server (CamelService *service)
+{
+	CamelSettings *settings;
+	gboolean is_gmail = FALSE;
+
+	g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
+
+	settings = camel_service_ref_settings (service);
+	if (CAMEL_IS_NETWORK_SETTINGS (settings)) {
+		gchar *host;
+
+		host = camel_network_settings_dup_host (CAMEL_NETWORK_SETTINGS (settings));
+
+		is_gmail = host && (
+			camel_strstrcase (host, ".gmail.com") != NULL ||
+			camel_strstrcase (host, ".googlemail.com") != NULL);
+
+		g_free (host);
+	}
+
+	g_clear_object (&settings);
+
+	return is_gmail;
+}
+
+static gchar *
+imapx_find_folder_for_initial_setup (CamelFolderInfo *root,
+				     const gchar *path)
+{
+	CamelFolderInfo *finfo, *next;
+	gchar *folder_fullname = NULL;
+	gchar **path_parts;
+	gint ii;
+
+	if (!root || !path)
+		return NULL;
+
+	path_parts = g_strsplit (path, "/", -1);
+	if (!path_parts)
+		return NULL;
+
+	finfo = root;
+
+	for (ii = 0; path_parts[ii] && finfo; ii++) {
+		gchar *folded_path;
+
+		folded_path = g_utf8_casefold (path_parts[ii], -1);
+		if (!folded_path)
+			break;
+
+		for (next = NULL; finfo; finfo = finfo->next) {
+			gchar *folded_display_name;
+			gint cmp;
+
+			if ((finfo->flags & (CAMEL_FOLDER_NOSELECT | CAMEL_FOLDER_VIRTUAL)) != 0)
+				continue;
+
+			folded_display_name = g_utf8_casefold (finfo->display_name, -1);
+			if (!folded_display_name)
+				continue;
+
+			cmp = g_utf8_collate (folded_path, folded_display_name);
+
+			g_free (folded_display_name);
+
+			if (cmp == 0) {
+				next = finfo;
+				break;
+			}
+		}
+
+		g_free (folded_path);
+
+		finfo = next;
+		if (finfo) {
+			if (path_parts[ii + 1])
+				finfo = finfo->child;
+			else
+				folder_fullname = g_strdup (finfo->full_name);
+		}
+	}
+
+	g_strfreev (path_parts);
+
+	return folder_fullname;
+}
+
+static void
+imapx_check_initial_setup_group (CamelIMAPXStore *imapx_store,
+				 CamelFolderInfo *finfo,
+				 GHashTable *save_setup,
+				 const gchar *list_attribute,
+				 const gchar *main_key,
+				 const gchar *additional_key,
+				 const gchar *additional_key_value,
+				 const gchar **names,
+				 guint n_names)
+{
+	gchar *folder_fullname = NULL;
+	gint ii;
+
+	g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
+	g_return_if_fail (finfo != NULL);
+	g_return_if_fail (save_setup != NULL);
+	g_return_if_fail (main_key != NULL);
+	g_return_if_fail (names != NULL);
+	g_return_if_fail (n_names > 0);
+
+	/* Prefer RFC 6154 "SPECIAL-USE" Flags, which are not locale sensitive */
+	if (list_attribute) {
+		CamelIMAPXNamespaceResponse *namespace_response;
+
+		namespace_response = camel_imapx_store_ref_namespaces (imapx_store);
+		if (namespace_response) {
+			GList *namespaces, *mailboxes, *link;
+			CamelIMAPXNamespace *user_namespace = NULL;
+
+			namespaces = camel_imapx_namespace_response_list (namespace_response);
+			for (link = namespaces; link && !user_namespace; link = g_list_next (link)) {
+				CamelIMAPXNamespace *candidate = link->data;
+
+				if (!candidate || camel_imapx_namespace_get_category (candidate) != CAMEL_IMAPX_NAMESPACE_PERSONAL)
+					continue;
+
+				user_namespace = candidate;
+			}
+
+			if (user_namespace) {
+				mailboxes = camel_imapx_store_list_mailboxes (imapx_store, user_namespace, NULL);
+
+				for (link = mailboxes; link && !folder_fullname; link = g_list_next (link)) {
+					CamelIMAPXMailbox *mailbox = link->data;
+
+					if (!mailbox || !camel_imapx_mailbox_has_attribute (mailbox, list_attribute))
+						continue;
+
+					folder_fullname = camel_imapx_mailbox_dup_folder_path (mailbox);
+				}
+
+				g_list_free_full (mailboxes, g_object_unref);
+			}
+
+			g_list_free_full (namespaces, g_object_unref);
+			g_object_unref (namespace_response);
+		}
+	}
+
+	/* First check the folder names in the user's locale */
+	for (ii = 0; ii < n_names && !folder_fullname; ii++) {
+		gchar *name;
+
+		/* In the same level as the Inbox */
+		folder_fullname = imapx_find_folder_for_initial_setup (finfo, g_dpgettext2 (GETTEXT_PACKAGE, "IMAPDefaults", names[ii]));
+
+		if (folder_fullname)
+			break;
+
+		/* as a subfolder of the Inbox */
+		name = g_strconcat ("INBOX/", g_dpgettext2 (GETTEXT_PACKAGE, "IMAPDefaults", names[ii]), NULL);
+		folder_fullname = imapx_find_folder_for_initial_setup (finfo, name);
+		g_free (name);
+	}
+
+	/* Then repeat with the english name (as written in the code) */
+	for (ii = 0; ii < n_names && !folder_fullname; ii++) {
+		gchar *name;
+
+		/* Do not try the same folder name twice */
+		if (g_strcmp0 (g_dpgettext2 (GETTEXT_PACKAGE, "IMAPDefaults", names[ii]), names[ii]) == 0)
+			continue;
+
+		folder_fullname = imapx_find_folder_for_initial_setup (finfo, names[ii]);
+
+		if (folder_fullname)
+			break;
+
+		name = g_strconcat ("INBOX/", names[ii], NULL);
+		folder_fullname = imapx_find_folder_for_initial_setup (finfo, name);
+		g_free (name);
+	}
+
+	if (folder_fullname) {
+		g_hash_table_insert (save_setup,
+			g_strdup (main_key),
+			g_strdup (folder_fullname));
+
+		if (additional_key) {
+			g_hash_table_insert (save_setup,
+				g_strdup (additional_key),
+				g_strdup (additional_key_value));
+		}
+
+		g_free (folder_fullname);
+	}
+}
+
+static gboolean
+imapx_initial_setup_sync (CamelStore *store,
+			  GHashTable *save_setup,
+			  GCancellable *cancellable,
+			  GError **error)
+{
+	/* Translators: The strings in "IMAPDefaults" context are folder names as can be presented
+	   by the server; There's checked for the localized version of it and for the non-localized
+	   version as well. It's always the folder name (eventually path) as provided by the server,
+	   when returned in given localization. it can be checked semi-easily in the case of
+	   the GMail variants, by changing the GMail interface language in the GMail Preferences. */
+	const gchar *draft_names[] = {
+		NC_("IMAPDefaults", "[Gmail]/Drafts"),
+		NC_("IMAPDefaults", "Drafts"),
+		NC_("IMAPDefaults", "Draft")
+	};
+	const gchar *templates_names[] = {
+		NC_("IMAPDefaults", "Templates")
+	};
+	#ifdef CAMEL_STORE_SETUP_ARCHIVE_FOLDER
+	const gchar *archive_names[] = {
+		NC_("IMAPDefaults", "Archive")
+	};
+	#endif
+	const gchar *sent_names[] = {
+		NC_("IMAPDefaults", "[Gmail]/Sent Mail"),
+		NC_("IMAPDefaults", "Sent"),
+		NC_("IMAPDefaults", "Sent Items"),
+		NC_("IMAPDefaults", "Sent Messages")
+	};
+	const gchar *junk_names[] = {
+		NC_("IMAPDefaults", "[Gmail]/Spam"),
+		NC_("IMAPDefaults", "Junk"),
+		NC_("IMAPDefaults", "Junk E-mail"),
+		NC_("IMAPDefaults", "Junk Email"),
+		NC_("IMAPDefaults", "Spam"),
+		NC_("IMAPDefaults", "Bulk Mail")
+	};
+	const gchar *trash_names[] = {
+		NC_("IMAPDefaults", "[Gmail]/Trash"),
+		NC_("IMAPDefaults", "Trash"),
+		NC_("IMAPDefaults", "Deleted Items"),
+		NC_("IMAPDefaults", "Deleted Messages")
+	};
+
+	CamelIMAPXStore *imapx_store;
+	CamelFolderInfo *finfo;
+	GError *local_error = NULL;
+
+	g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), FALSE);
+	g_return_val_if_fail (save_setup != NULL, FALSE);
+
+	finfo = camel_store_get_folder_info_sync (store, NULL,
+		CAMEL_STORE_FOLDER_INFO_RECURSIVE | CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL,
+		cancellable, &local_error);
+
+	if (!finfo) {
+		if (local_error) {
+			g_propagate_error (error, local_error);
+			return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	imapx_store = CAMEL_IMAPX_STORE (store);
+
+	imapx_check_initial_setup_group (imapx_store, finfo, save_setup,
+		CAMEL_IMAPX_LIST_ATTR_DRAFTS,
+		CAMEL_STORE_SETUP_DRAFTS_FOLDER, NULL, NULL,
+		draft_names, G_N_ELEMENTS (draft_names));
+
+	imapx_check_initial_setup_group (imapx_store, finfo, save_setup, NULL,
+		CAMEL_STORE_SETUP_TEMPLATES_FOLDER, NULL, NULL,
+		templates_names, G_N_ELEMENTS (templates_names));
+
+	#ifdef CAMEL_STORE_SETUP_ARCHIVE_FOLDER
+	imapx_check_initial_setup_group (imapx_store, finfo, save_setup,
+		CAMEL_IMAPX_LIST_ATTR_ARCHIVE,
+		CAMEL_STORE_SETUP_ARCHIVE_FOLDER, NULL, NULL,
+		archive_names, G_N_ELEMENTS (archive_names));
+	#endif
+
+	/* Skip changing Sent folder for GMail, because GMail stores sent messages
+	   automatically, thus it would make doubled copies on the server. */
+	if (!imapx_is_gmail_server (CAMEL_SERVICE (store))) {
+		imapx_check_initial_setup_group (imapx_store, finfo, save_setup,
+			CAMEL_IMAPX_LIST_ATTR_SENT,
+			CAMEL_STORE_SETUP_SENT_FOLDER, NULL, NULL,
+			sent_names, G_N_ELEMENTS (sent_names));
+	}
+
+	/* It's a folder path inside the account, thus not use the 'f' type, but the 's' type. */
+	imapx_check_initial_setup_group (imapx_store, finfo, save_setup,
+		CAMEL_IMAPX_LIST_ATTR_JUNK,
+		"Backend:Imapx Backend:real-junk-path:s",
+		"Backend:Imapx Backend:use-real-junk-path:b", "true",
+		junk_names, G_N_ELEMENTS (junk_names));
+
+	/* It's a folder path inside the account, thus not use the 'f' type, but the 's' type. */
+	imapx_check_initial_setup_group (imapx_store, finfo, save_setup,
+		CAMEL_IMAPX_LIST_ATTR_TRASH,
+		"Backend:Imapx Backend:real-trash-path:s",
+		"Backend:Imapx Backend:use-real-trash-path:b", "true",
+		trash_names, G_N_ELEMENTS (trash_names));
+
+	camel_folder_info_free (finfo);
+
+	return TRUE;
+}
+
 static void
 imapx_migrate_to_user_cache_dir (CamelService *service)
 {
@@ -2341,7 +2609,7 @@ imapx_store_initable_init (GInitable *in
 	store = CAMEL_STORE (initable);
 	service = CAMEL_SERVICE (initable);
 
-	store->flags |= CAMEL_STORE_USE_CACHE_DIR;
+	store->flags |= CAMEL_STORE_USE_CACHE_DIR | CAMEL_STORE_SUPPORTS_INITIAL_SETUP;
 	imapx_migrate_to_user_cache_dir (service);
 
 	/* Chain up to parent interface's init() method. */
@@ -2460,6 +2728,8 @@ imapx_ensure_parents_subscribed (CamelIM
 
 		/* Since this is a "fake" folder node, it is not selectable. */
 		fi->flags |= CAMEL_FOLDER_NOSELECT;
+		fi->unread = -1;
+		fi->total = -1;
 
 		parents = g_slist_prepend (parents, fi);
 	}
@@ -2470,6 +2740,9 @@ imapx_ensure_parents_subscribed (CamelIM
 		camel_subscribable_folder_subscribed (subscribable, fi);
 		camel_folder_info_free (fi);
 	}
+
+	g_slist_free (parents);
+	g_free (parent);
 }
 
 static gboolean
@@ -2480,16 +2753,12 @@ imapx_store_subscribe_folder_sync (Camel
 {
 	CamelFolder *folder;
 	CamelIMAPXStore *imapx_store;
-	CamelIMAPXServer *imapx_server;
+	CamelIMAPXConnManager *conn_man;
 	CamelIMAPXMailbox *mailbox = NULL;
 	gboolean success = FALSE;
-	GError *local_error = NULL;
 
 	imapx_store = CAMEL_IMAPX_STORE (subscribable);
-	imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
-
-	if (imapx_server == NULL)
-		goto exit;
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
 
 	folder = camel_store_get_folder_sync (
 		CAMEL_STORE (subscribable),
@@ -2504,19 +2773,7 @@ imapx_store_subscribe_folder_sync (Camel
 	if (mailbox == NULL)
 		goto exit;
 
-	success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
-
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		g_clear_error (&local_error);
-		g_clear_object (&imapx_server);
-
-		imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
-		if (imapx_server)
-			success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
-	}
-
-	if (local_error)
-		g_propagate_error (error, local_error);
+	success = camel_imapx_conn_manager_subscribe_mailbox_sync (conn_man, mailbox, cancellable, error);
 
 	if (success) {
 		CamelFolderInfo *fi;
@@ -2532,7 +2789,6 @@ imapx_store_subscribe_folder_sync (Camel
 
 exit:
 	g_clear_object (&mailbox);
-	g_clear_object (&imapx_server);
 
 	return success;
 }
@@ -2545,16 +2801,12 @@ imapx_store_unsubscribe_folder_sync (Cam
 {
 	CamelFolder *folder;
 	CamelIMAPXStore *imapx_store;
-	CamelIMAPXServer *imapx_server;
+	CamelIMAPXConnManager *conn_man;
 	CamelIMAPXMailbox *mailbox = NULL;
 	gboolean success = FALSE;
-	GError *local_error = NULL;
 
 	imapx_store = CAMEL_IMAPX_STORE (subscribable);
-	imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
-
-	if (imapx_server == NULL)
-		goto exit;
+	conn_man = camel_imapx_store_get_conn_manager (imapx_store);
 
 	folder = camel_store_get_folder_sync (
 		CAMEL_STORE (subscribable),
@@ -2569,19 +2821,7 @@ imapx_store_unsubscribe_folder_sync (Cam
 	if (mailbox == NULL)
 		goto exit;
 
-	success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
-
-	while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
-		g_clear_error (&local_error);
-		g_clear_object (&imapx_server);
-
-		imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
-		if (imapx_server)
-			success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
-	}
-
-	if (local_error)
-		g_propagate_error (error, local_error);
+	success = camel_imapx_conn_manager_unsubscribe_mailbox_sync (conn_man, mailbox, cancellable, error);
 
 	if (success) {
 		CamelFolderInfo *fi;
@@ -2594,7 +2834,6 @@ imapx_store_unsubscribe_folder_sync (Cam
 
 exit:
 	g_clear_object (&mailbox);
-	g_clear_object (&imapx_server);
 
 	return success;
 }
@@ -2677,11 +2916,23 @@ camel_imapx_store_class_init (CamelIMAPX
 	store_class->create_folder_sync = imapx_store_create_folder_sync;
 	store_class->delete_folder_sync = imapx_store_delete_folder_sync;
 	store_class->rename_folder_sync = imapx_store_rename_folder_sync;
+	store_class->initial_setup_sync = imapx_initial_setup_sync;
 
 	class->mailbox_created = imapx_store_mailbox_created;
 	class->mailbox_renamed = imapx_store_mailbox_renamed;
 	class->mailbox_updated = imapx_store_mailbox_updated;
 
+	g_object_class_install_property (
+		object_class,
+		PROP_CONN_MANAGER,
+		g_param_spec_object (
+			"conn-manager",
+			"Connection Manager",
+			"The Connection Manager being used for remote operations",
+			CAMEL_TYPE_IMAPX_CONN_MANAGER,
+			G_PARAM_READABLE |
+			G_PARAM_STATIC_STRINGS));
+
 	/* Inherited from CamelNetworkService. */
 	g_object_class_override_property (
 		object_class,
@@ -2751,7 +3002,7 @@ camel_imapx_store_init (CamelIMAPXStore
 {
 	store->priv = CAMEL_IMAPX_STORE_GET_PRIVATE (store);
 
-	store->priv->con_man = camel_imapx_conn_manager_new (CAMEL_STORE (store));
+	store->priv->conn_man = camel_imapx_conn_manager_new (CAMEL_STORE (store));
 
 	g_mutex_init (&store->priv->get_finfo_lock);
 
@@ -2782,65 +3033,12 @@ camel_imapx_store_init (CamelIMAPXStore
 		G_CALLBACK (imapx_store_update_store_flags), NULL);
 }
 
-/**
- * camel_imapx_store_ref_server:
- * @store: a #CamelIMAPXStore
- * @folder_name: name of a folder, for which it'll be used; can be %NULL
- * @cancellable: a #GCancellable to use ofr possible new connection creation, or %NULL
- * @error: return location for a #GError, or %NULL
- *
- * Returns the #CamelIMAPXServer for @store, if available.
- *
- * As a convenience, if the @store is not currently connected to an IMAP
- * server, the function sets @error to %CAMEL_SERVER_ERROR_UNAVAILABLE and
- * returns %NULL.  If an operation can possibly be executed while offline,
- * pass %NULL for @error.
- *
- * The returned #CamelIMAPXServer is referenced for thread-safety and must
- * be unreferenced with g_object_unref() when finished with it.
- *
- * Returns: a #CamelIMAPXServer, or %NULL
- *
- * Since: 3.10
- **/
-CamelIMAPXServer *
-camel_imapx_store_ref_server (CamelIMAPXStore *store,
-			      const gchar *folder_name,
-			      gboolean for_expensive_job,
-			      GCancellable *cancellable,
-                              GError **error)
+CamelIMAPXConnManager *
+camel_imapx_store_get_conn_manager (CamelIMAPXStore *store)
 {
-	CamelIMAPXServer *server = NULL;
-	CamelSession *session;
-	GError *local_error = NULL;
-
 	g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), NULL);
 
-	session = camel_service_ref_session (CAMEL_SERVICE (store));
-
-	if (camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)) &&
-	    camel_session_get_online (session))
-		server = camel_imapx_conn_manager_get_connection (
-			store->priv->con_man, folder_name, for_expensive_job, cancellable, &local_error);
-
-	g_clear_object (&session);
-
-	if (!server && (!local_error || local_error->domain == G_RESOLVER_ERROR)) {
-		if (!local_error) {
-			g_set_error (
-				&local_error, CAMEL_SERVICE_ERROR,
-				CAMEL_SERVICE_ERROR_UNAVAILABLE,
-				_("You must be working online to complete this operation"));
-		} else {
-			local_error->domain = CAMEL_SERVICE_ERROR;
-			local_error->code = CAMEL_SERVICE_ERROR_UNAVAILABLE;
-		}
-	}
-
-	if (local_error)
-		g_propagate_error (error, local_error);
-
-	return server;
+	return store->priv->conn_man;
 }
 
 /* The caller should hold the store->priv->server_lock already, when calling this */
@@ -2881,19 +3079,6 @@ camel_imapx_store_is_connecting_concurre
 	return res;
 }
 
-void
-camel_imapx_store_folder_op_done (CamelIMAPXStore *store,
-				  CamelIMAPXServer *server,
-				  const gchar *folder_name)
-{
-	g_return_if_fail (CAMEL_IS_IMAPX_STORE (store));
-	g_return_if_fail (CAMEL_IS_IMAPX_SERVER (server));
-	g_return_if_fail (folder_name != NULL);
-
-	camel_imapx_conn_manager_update_con_info (
-		store->priv->con_man, server, folder_name);
-}
-
 /**
  * camel_imapx_store_ref_namespaces:
  * @imapx_store: a #CamelIMAPXStore
@@ -2908,7 +3093,7 @@ camel_imapx_store_folder_op_done (CamelI
  *
  * Returns: a #CamelIMAPXNamespaceResponse
  *
- * Since: 3.12.2
+ * Since: 3.16
  **/
 CamelIMAPXNamespaceResponse *
 camel_imapx_store_ref_namespaces (CamelIMAPXStore *imapx_store)
@@ -3221,8 +3406,8 @@ imapx_store_rename_mailbox_unlocked (Cam
 		new_child_name = g_strconcat (
 			new_mailbox_name,
 			old_child_name + old_mailbox_name_length, NULL);
-		new_child = camel_imapx_mailbox_clone (
-			old_child, new_child_name);
+
+		new_child = camel_imapx_mailbox_clone (old_child, new_child_name);
 
 		/* Add the new mailbox, remove the old mailbox.
 		 * Note we still have a reference on the old mailbox. */
@@ -3254,7 +3439,7 @@ imapx_store_rename_mailbox_unlocked (Cam
  *
  * Returns: a #CamelIMAPXMailbox, or %NULL
  *
- * Since: 3.12.2
+ * Since: 3.16
  **/
 CamelIMAPXMailbox *
 camel_imapx_store_ref_mailbox (CamelIMAPXStore *imapx_store,
@@ -3297,7 +3482,7 @@ camel_imapx_store_ref_mailbox (CamelIMAP
  *
  * Returns: a list of #CamelIMAPXMailbox instances
  *
- * Since: 3.12.2
+ * Since: 3.16
  **/
 GList *
 camel_imapx_store_list_mailboxes (CamelIMAPXStore *imapx_store,
@@ -3377,7 +3562,7 @@ camel_imapx_store_handle_list_response (
 
 	/* Fabricate a CamelIMAPXNamespaceResponse if the server lacks the
 	 * NAMESPACE capability and this is the first LIST / LSUB response. */
-	if (CAMEL_IMAPX_LACK_CAPABILITY (imapx_server->cinfo, NAMESPACE)) {
+	if (camel_imapx_server_lack_capability (imapx_server, IMAPX_CAPABILITY_NAMESPACE)) {
 		g_mutex_lock (&imapx_store->priv->namespaces_lock);
 		if (imapx_store->priv->namespaces == NULL) {
 			imapx_store->priv->namespaces = camel_imapx_namespace_response_faux_new (response);
@@ -3441,7 +3626,7 @@ camel_imapx_store_handle_lsub_response (
 
 	/* Fabricate a CamelIMAPXNamespaceResponse if the server lacks the
 	 * NAMESPACE capability and this is the first LIST / LSUB response. */
-	if (CAMEL_IMAPX_LACK_CAPABILITY (imapx_server->cinfo, NAMESPACE)) {
+	if (camel_imapx_server_lack_capability (imapx_server, IMAPX_CAPABILITY_NAMESPACE)) {
 		g_mutex_lock (&imapx_store->priv->namespaces_lock);
 		if (imapx_store->priv->namespaces == NULL) {
 			imapx_store->priv->namespaces = camel_imapx_namespace_response_faux_new (response);
@@ -3512,41 +3697,11 @@ camel_imapx_store_set_quota_info (CamelI
 	g_mutex_unlock (&store->priv->quota_info_lock);
 }
 
-/* Tries to find matching job among all active connections.
-   See camel_imapx_server_ref_job() for more information on parameters
-   and return values.
-*/
-CamelIMAPXJob *
-camel_imapx_store_ref_job (CamelIMAPXStore *imapx_store,
-			   CamelIMAPXMailbox *mailbox,
-			   guint32 job_type,
-			   const gchar *uid)
-{
-	GList *servers, *siter;
-	CamelIMAPXJob *job = NULL;
-
-	g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), NULL);
-
-	servers = camel_imapx_conn_manager_get_connections (imapx_store->priv->con_man);
-
-	for (siter = servers; siter; siter = g_list_next (siter)) {
-		CamelIMAPXServer *imapx_server = siter->data;
-
-		job = camel_imapx_server_ref_job (imapx_server, mailbox, job_type, uid);
-		if (job)
-			break;
-	}
-
-	g_list_free_full (servers, g_object_unref);
-
-	return job;
-}
-
 /* for debugging purposes only */
 void
 camel_imapx_store_dump_queue_status (CamelIMAPXStore *imapx_store)
 {
 	g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
 
-	camel_imapx_conn_manager_dump_queue_status (imapx_store->priv->con_man);
+	camel_imapx_conn_manager_dump_queue_status (imapx_store->priv->conn_man);
 }
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.h.imapx-update-to-upstream	2014-05-22 08:45:46.000000000 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.h	2016-08-15 13:52:41.977976329 +0200
@@ -1,22 +1,21 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* camel-imap-store.h : class for an imap store */
-
-/*
- * Authors: Michael Zucchi <notzed@ximian.com>
+/* camel-imap-store.h : class for an imap store
  *
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
- * This library is free software; you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Michael Zucchi <notzed@ximian.com>
  */
 
 #ifndef CAMEL_IMAPX_STORE_H
@@ -24,6 +23,7 @@
 
 #include <camel/camel.h>
 
+#include "camel-imapx-conn-manager.h"
 #include "camel-imapx-server.h"
 
 /* Standard GObject macros */
@@ -75,22 +75,15 @@ struct _CamelIMAPXStoreClass {
 };
 
 GType		camel_imapx_store_get_type	(void);
-CamelIMAPXServer *
-		camel_imapx_store_ref_server	(CamelIMAPXStore *store,
-						 const gchar *folder_name,
-						 gboolean for_expensive_job,
-						 GCancellable *cancellable,
-						 GError **error);
+CamelIMAPXConnManager *
+		camel_imapx_store_get_conn_manager
+						(CamelIMAPXStore *store);
 void		camel_imapx_store_set_connecting_server
 						(CamelIMAPXStore *store,
 						 CamelIMAPXServer *server,
 						 gboolean is_concurrent_connection);
 gboolean	camel_imapx_store_is_connecting_concurrent_connection
 						(CamelIMAPXStore *imapx_store);
-void		camel_imapx_store_folder_op_done
-						(CamelIMAPXStore *store,
-						 CamelIMAPXServer *server,
-						 const gchar *folder_name);
 CamelIMAPXNamespaceResponse *
 		camel_imapx_store_ref_namespaces
 						(CamelIMAPXStore *imapx_store);
@@ -127,12 +120,6 @@ void		camel_imapx_store_set_quota_info
 						(CamelIMAPXStore *store,
 						 const gchar *quota_root_name,
 						 const CamelFolderQuotaInfo *info);
-struct _CamelIMAPXJob *
-		camel_imapx_store_ref_job	(CamelIMAPXStore *imapx_store,
-						 CamelIMAPXMailbox *mailbox,
-						 guint32 job_type,
-						 const gchar *uid);
-
 /* for debugging purposes only */
 void		camel_imapx_store_dump_queue_status
 						(CamelIMAPXStore *imapx_store);
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.c.imapx-update-to-upstream	2016-08-15 13:52:41.873976333 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.c	2016-08-15 13:52:41.977976329 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-store-summary.c
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -57,7 +57,7 @@ namespace_load (FILE *in)
 	 *     delete all this cruft. */
 
 	for (j = 0; j < 3; j++) {
-		gint32 i, n;
+		gint32 i, n = 0;
 
 		if (camel_file_util_decode_fixed_int32 (in, &n) == -1)
 			goto exit;
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.h.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.h	2016-08-15 13:52:41.977976329 +0200
@@ -1,17 +1,17 @@
 /*
  * camel-imapx-store-summary.h
  *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.c.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.c	2016-08-15 13:52:41.978976329 +0200
@@ -2,21 +2,20 @@
 /*
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
- * Authors:
- *    Michael Zucchi <notzed@ximian.com>
- *    Dan Winship <danw@ximian.com>
- *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *          Dan Winship <danw@ximian.com>
  */
 
 #ifdef HAVE_CONFIG_H
@@ -35,6 +34,13 @@
 
 #define CAMEL_IMAPX_SUMMARY_VERSION (4)
 
+enum {
+	INFO_CHANGED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
 G_DEFINE_TYPE (
 	CamelIMAPXSummary,
 	camel_imapx_summary,
@@ -250,6 +256,15 @@ imapx_summary_message_info_clone (CamelF
 	return copy;
 }
 
+static void
+imapx_summary_emit_info_changed (CamelMessageInfo *info)
+{
+	g_return_if_fail (info != NULL);
+	g_return_if_fail (CAMEL_IS_IMAPX_SUMMARY (info->summary));
+
+	g_signal_emit (info->summary, signals[INFO_CHANGED], 0, info);
+}
+
 static gboolean
 imapx_summary_info_set_user_flag (CamelMessageInfo *info,
                                   const gchar *id,
@@ -257,18 +272,43 @@ imapx_summary_info_set_user_flag (CamelM
 {
 	gboolean changed;
 
-	/* Chain up to parent's info_set_user_flag() method. */
-	changed = CAMEL_FOLDER_SUMMARY_CLASS (
-		camel_imapx_summary_parent_class)->
-		info_set_user_flag (info, id, state);
+	/* Chain up to parent's method. */
+	changed = CAMEL_FOLDER_SUMMARY_CLASS (camel_imapx_summary_parent_class)->info_set_user_flag (info, id, state);
 
-	/* there was a change, so do not forget to store it to server */
-	if (changed) {
-		CamelIMAPXMessageInfo *imapx_info;
+	if (changed)
+		imapx_summary_emit_info_changed (info);
 
-		imapx_info = (CamelIMAPXMessageInfo *) info;
-		imapx_info->info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
-	}
+	return changed;
+}
+
+static gboolean
+imapx_summary_info_set_user_tag (CamelMessageInfo *info,
+				 const gchar *name,
+				 const gchar *value)
+{
+	gboolean changed;
+
+	/* Chain up to parent's method. */
+	changed = CAMEL_FOLDER_SUMMARY_CLASS (camel_imapx_summary_parent_class)->info_set_user_tag (info, name, value);
+
+	if (changed)
+		imapx_summary_emit_info_changed (info);
+
+	return changed;
+}
+
+static gboolean
+imapx_summary_info_set_flags (CamelMessageInfo *info,
+			      guint32 flags,
+			      guint32 set)
+{
+	gboolean changed;
+
+	/* Chain up to parent's method. */
+	changed = CAMEL_FOLDER_SUMMARY_CLASS (camel_imapx_summary_parent_class)->info_set_flags (info, flags, set);
+
+	if (changed)
+		imapx_summary_emit_info_changed (info);
 
 	return changed;
 }
@@ -290,6 +330,17 @@ camel_imapx_summary_class_init (CamelIMA
 	folder_summary_class->message_info_free = imapx_summary_message_info_free;
 	folder_summary_class->message_info_clone = imapx_summary_message_info_clone;
 	folder_summary_class->info_set_user_flag = imapx_summary_info_set_user_flag;
+	folder_summary_class->info_set_user_tag = imapx_summary_info_set_user_tag;
+	folder_summary_class->info_set_flags = imapx_summary_info_set_flags;
+
+	signals[INFO_CHANGED] = g_signal_new (
+		"info-changed",
+		G_OBJECT_CLASS_TYPE (class),
+		G_SIGNAL_RUN_LAST,
+		0 /* G_STRUCT_OFFSET (CamelIMAPXSummaryClass, info_changed) */,
+		NULL, NULL, NULL,
+		G_TYPE_NONE, 1,
+		G_TYPE_POINTER /* CamelMessageInfo * */);
 }
 
 static void
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.h.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.h	2016-08-15 13:52:41.978976329 +0200
@@ -1,21 +1,20 @@
 /*
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
- * Authors:
- *    Michael Zucchi <notzed@ximian.com>
- *    Dan Winship <danw@ximian.com>
- *
- * This library is free software you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *          Dan Winship <danw@ximian.com>
  */
 
 #ifndef CAMEL_IMAPX_SUMMARY_H
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.c
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.c.imapx-update-to-upstream	2016-08-15 13:52:41.873976333 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.c	2016-08-15 14:38:22.158860220 +0200
@@ -2,17 +2,18 @@
 /*
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
- * This library is free software; you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
  */
 
 #ifdef HAVE_CONFIG_H
@@ -123,7 +124,7 @@ imapx_parse_flags (CamelIMAPXInputStream
 
 	if (tok != '(') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"expecting flag list");
 		return FALSE;
 	}
@@ -152,12 +153,21 @@ imapx_parse_flags (CamelIMAPXInputStream
 
 			if (!match_found && user_flagsp != NULL) {
 				const gchar *flag_name;
+				gchar *utf8;
 
 				flag_name = rename_label_flag (
 					(gchar *) token,
 					strlen ((gchar *) token), TRUE);
 
-				camel_flag_set (user_flagsp, flag_name, TRUE);
+				utf8 = camel_utf7_utf8 (flag_name);
+				if (utf8 && !g_utf8_validate (utf8, -1, NULL)) {
+					g_free (utf8);
+					utf8 = NULL;
+				}
+
+				camel_flag_set (user_flagsp, utf8 ? utf8 : flag_name, TRUE);
+
+				g_free (utf8);
 			}
 
 			g_free (upper);
@@ -239,6 +249,7 @@ imapx_write_flags (GString *string,
 
 	while (user_flags) {
 		const gchar *flag_name;
+		gchar *utf7;
 
 		flag_name = rename_label_flag (
 			user_flags->name, strlen (user_flags->name), FALSE);
@@ -246,7 +257,12 @@ imapx_write_flags (GString *string,
 		if (!first)
 			g_string_append_c (string, ' ');
 		first = FALSE;
-		g_string_append (string, flag_name);
+
+		utf7 = camel_utf8_utf7 (flag_name);
+
+		g_string_append (string, utf7 ? utf7 : flag_name);
+
+		g_free (utf7);
 
 		user_flags = user_flags->next;
 	}
@@ -261,17 +277,21 @@ imapx_update_user_flags (CamelMessageInf
 	gboolean changed = FALSE;
 	CamelMessageInfoBase *binfo = (CamelMessageInfoBase *) info;
 	CamelIMAPXMessageInfo *xinfo = (CamelIMAPXMessageInfo *) info;
-	gboolean set_cal = FALSE;
+	gboolean set_cal = FALSE, set_note = FALSE;
 
 	if (camel_flag_get (&binfo->user_flags, "$has_cal"))
 		set_cal = TRUE;
+	if (camel_flag_get (&binfo->user_flags, "$has_note"))
+		set_note = TRUE;
 
 	changed = camel_flag_list_copy (&binfo->user_flags, &server_user_flags);
 	camel_flag_list_copy (&xinfo->server_user_flags, &server_user_flags);
 
-	/* reset the calendar flag if it was set in messageinfo before */
+	/* reset the flags as they were set in messageinfo before */
 	if (set_cal)
 		camel_flag_set (&binfo->user_flags, "$has_cal", TRUE);
+	if (set_note)
+		camel_flag_set (&binfo->user_flags, "$has_note", TRUE);
 
 	return changed;
 }
@@ -312,16 +332,13 @@ imapx_update_message_info_flags (CamelMe
 		if (permanent_flags > 0)
 			server_cleared &= permanent_flags;
 
-		camel_message_info_set_flags ((
+		changed = camel_message_info_set_flags ((
 			CamelMessageInfo *) xinfo,
 			server_set | server_cleared,
 			(xinfo->info.flags | server_set) & ~server_cleared);
 
 		xinfo->server_flags = server_flags;
-		xinfo->info.flags = xinfo->info.flags & ~CAMEL_MESSAGE_FOLDER_FLAGGED;
 		xinfo->info.dirty = TRUE;
-
-		changed = TRUE;
 	}
 
 	if ((permanent_flags & CAMEL_MESSAGE_USER) != 0 && imapx_update_user_flags (info, server_user_flags))
@@ -503,8 +520,11 @@ imapx_parse_capability (CamelIMAPXInputS
 				/* Put it back so that imapx_untagged() isn't unhappy */
 				camel_imapx_input_stream_ungettoken (
 					stream, tok, token, len);
-				return cinfo;
+				break;
 			case 43:
+				/* the CAPABILITY shouldn't start with a '+', ignore it then */
+				if (!token)
+					break;
 				token = (guchar *) g_strconcat ((gchar *) token, "+", NULL);
 				free_token = TRUE;
 				/* coverity[fallthrough] */
@@ -530,11 +550,14 @@ imapx_parse_capability (CamelIMAPXInputS
 				break;
 			default:
 				g_set_error (
-					error, CAMEL_IMAPX_ERROR, 1,
+					error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 					"capability: expecting name");
 				break;
 		}
 
+		if (tok == ']')
+			break;
+
 		tok = camel_imapx_input_stream_token (
 			stream, &token, &len, cancellable, &local_error);
 	}
@@ -842,26 +865,28 @@ imapx_parse_ext_optional (CamelIMAPXInpu
 			dinfo->refcount = 1;
 			/* should be string */
 			if (!camel_imapx_input_stream_astring (stream, &token, cancellable, &local_error)) {
-				camel_content_disposition_unref (dinfo);
 				if (!local_error)
 					g_set_error (
 						&local_error,
-						CAMEL_IMAPX_ERROR, 1,
+						CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 						"expecting string");
-				g_propagate_error (error, local_error);
-
-				return NULL;
+				goto done;
 			}
 
 			dinfo->disposition = g_strdup ((gchar *) token);
 			imapx_parse_param_list (
-				stream, &dinfo->params, cancellable, NULL);
+				stream, &dinfo->params, cancellable,
+				&local_error);
+
+			if (local_error != NULL)
+				goto done;
+
 			break;
 		case IMAPX_TOK_TOKEN:
 			break;
 		default:
 			g_set_error (
-				error, CAMEL_IMAPX_ERROR, 1,
+				error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 				"body_fld_disp: expecting nil or list");
 			return NULL;
 	}
@@ -886,7 +911,7 @@ imapx_parse_ext_optional (CamelIMAPXInpu
 					g_clear_error (&local_error);
 					g_set_error (
 						&local_error,
-						CAMEL_IMAPX_ERROR, 1,
+						CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 						"expecting string");
 					break;
 				}
@@ -908,6 +933,7 @@ imapx_parse_ext_optional (CamelIMAPXInpu
 
 	}
 
+ done:
 	if (local_error != NULL) {
 		g_propagate_error (error, local_error);
 		if (dinfo)
@@ -1003,10 +1029,10 @@ imapx_parse_body_fields (CamelIMAPXInput
 error:
 	imapx_free_body (cinfo);
 
-	return cinfo;
+	return NULL;
 }
 
-struct _camel_header_address *
+CamelHeaderAddress *
 imapx_parse_address_list (CamelIMAPXInputStream *stream,
                           GCancellable *cancellable,
                           GError **error)
@@ -1015,7 +1041,7 @@ imapx_parse_address_list (CamelIMAPXInpu
 	guint len;
 	guchar *token, *host;
 	gchar *mbox;
-	struct _camel_header_address *list = NULL;
+	CamelHeaderAddress *list = NULL;
 	GError *local_error = NULL;
 
 	/* "(" 1*address ")" / nil */
@@ -1028,7 +1054,7 @@ imapx_parse_address_list (CamelIMAPXInpu
 	}
 
 	if (tok == '(') {
-		struct _camel_header_address *addr, *group = NULL;
+		CamelHeaderAddress *addr, *group = NULL;
 		while (1) {
 			/* address         ::= "(" addr_name SPACE addr_adl SPACE addr_mailbox
 			 * SPACE addr_host ")" */
@@ -1040,7 +1066,7 @@ imapx_parse_address_list (CamelIMAPXInpu
 				g_clear_error (&local_error);
 				camel_header_address_list_clear (&list);
 				g_set_error (
-					error, CAMEL_IMAPX_ERROR, 1,
+					error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 					"missing '(' for address");
 				return NULL;
 			}
@@ -1134,7 +1160,7 @@ imapx_parse_envelope (CamelIMAPXInputStr
 	gint tok;
 	guint len;
 	guchar *token;
-	struct _camel_header_address *addr, *addr_from;
+	CamelHeaderAddress *addr, *addr_from;
 	gchar *addrstr;
 	struct _CamelMessageInfoBase *minfo = NULL;
 	GError *local_error = NULL;
@@ -1155,7 +1181,7 @@ imapx_parse_envelope (CamelIMAPXInputStr
 	if (tok != '(') {
 		g_clear_error (&local_error);
 		camel_message_info_unref (minfo);
-		g_set_error (error, CAMEL_IMAPX_ERROR, 1, "envelope: expecting '('");
+		g_set_error (error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "envelope: expecting '('");
 		return NULL;
 	}
 
@@ -1262,7 +1288,7 @@ imapx_parse_envelope (CamelIMAPXInputStr
 	if (tok != ')') {
 		g_clear_error (&local_error);
 		camel_message_info_unref (minfo);
-		g_set_error (error, CAMEL_IMAPX_ERROR, 1, "expecting ')'");
+		g_set_error (error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "expecting ')'");
 		return NULL;
 	}
 
@@ -1297,7 +1323,7 @@ imapx_parse_body (CamelIMAPXInputStream
 		stream, &token, &len, cancellable, &local_error);
 	if (tok != '(') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"body: expecting '('");
 		return NULL;
 	}
@@ -1533,7 +1559,7 @@ imapx_parse_section (CamelIMAPXInputStre
 
 	if (tok != '[') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"section: expecting '['");
 		return NULL;
 	}
@@ -1548,7 +1574,7 @@ imapx_parse_section (CamelIMAPXInputStre
 		camel_imapx_input_stream_ungettoken (stream, tok, token, len);
 	} else {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"section: expecting token");
 		return NULL;
 	}
@@ -1569,7 +1595,7 @@ imapx_parse_section (CamelIMAPXInputStre
 				/* ?do something? */
 			} else if (tok != ')') {
 				g_set_error (
-					error, CAMEL_IMAPX_ERROR, 1,
+					error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 					"section: header fields: expecting string");
 				g_free (section);
 				return NULL;
@@ -1582,7 +1608,7 @@ imapx_parse_section (CamelIMAPXInputStre
 
 	if (tok != ']') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"section: expecting ']'");
 		g_free (section);
 		return NULL;
@@ -1610,7 +1636,7 @@ imapx_parse_modseq (CamelIMAPXInputStrea
 
 	if (tok != '(') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"fetch: expecting '('");
 		return 0;
 	}
@@ -1629,7 +1655,7 @@ imapx_parse_modseq (CamelIMAPXInputStrea
 
 	if (tok != ')') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"fetch: expecting '('");
 		return 0;
 	}
@@ -1680,19 +1706,19 @@ imapx_dump_fetch (struct _fetch_info *fi
 	if (finfo->body != NULL) {
 		g_print ("Body content:\n");
 		data = g_bytes_get_data (finfo->body, &size);
-		fwrite (data, size, 1, stdout);
+		fwrite (data, sizeof (gchar), size, stdout);
 	}
 
 	if (finfo->text != NULL) {
 		g_print ("Text content:\n");
 		data = g_bytes_get_data (finfo->text, &size);
-		fwrite (data, size, 1, stdout);
+		fwrite (data, sizeof (gchar), size, stdout);
 	}
 
 	if (finfo->header != NULL) {
 		g_print ("Header content:\n");
 		data = g_bytes_get_data (finfo->header, &size);
-		fwrite (data, size, 1, stdout);
+		fwrite (data, sizeof (gchar), size, stdout);
 	}
 
 	if (finfo->minfo != NULL) {
@@ -1786,7 +1812,7 @@ imapx_parse_fetch_body (CamelIMAPXInputS
 	}
 
 	g_set_error (
-		error, CAMEL_IMAPX_ERROR, 1,
+		error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 		"unknown body response");
 
 	return FALSE;
@@ -1957,7 +1983,7 @@ imapx_parse_fetch_uid (CamelIMAPXInputSt
 
 	if (tok != IMAPX_TOK_INT) {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"uid not integer");
 		return FALSE;
 	}
@@ -1988,7 +2014,7 @@ imapx_parse_fetch (CamelIMAPXInputStream
 
 	if (tok != '(') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"fetch: expecting '('");
 		goto fail;
 	}
@@ -2056,7 +2082,7 @@ imapx_parse_fetch (CamelIMAPXInputStream
 
 			default:
 				g_set_error (
-					error, CAMEL_IMAPX_ERROR, 1,
+					error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 					"unknown body response");
 				break;
 		}
@@ -2073,7 +2099,7 @@ imapx_parse_fetch (CamelIMAPXInputStream
 
 	if (tok != ')') {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 			"missing closing ')' on fetch response");
 		goto fail;
 	}
@@ -2094,7 +2120,7 @@ imapx_parse_uids (CamelIMAPXInputStream
                   GError **error)
 {
 	GArray *array;
-	guchar *token;
+	guchar *token = NULL;
 	gchar **splits;
 	guint len, str_len;
 	gint tok, ii;
@@ -2106,6 +2132,11 @@ imapx_parse_uids (CamelIMAPXInputStream
 	if (tok < 0)
 		return NULL;
 
+	if (!token) {
+		g_set_error (error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_IGNORE, "server response truncated");
+		return NULL;
+	}
+
 	array = g_array_new (FALSE, FALSE, sizeof (guint32));
 	splits = g_strsplit ((gchar *) token, ",", -1);
 	str_len = g_strv_length (splits);
@@ -2181,6 +2212,7 @@ imapx_parse_status_copyuid (CamelIMAPXIn
 	GArray *uids;
 	guint64 number;
 	gboolean success;
+	GError *local_error = NULL;
 
 	success = camel_imapx_input_stream_number (
 		stream, &number, cancellable, error);
@@ -2190,15 +2222,37 @@ imapx_parse_status_copyuid (CamelIMAPXIn
 
 	sinfo->u.copyuid.uidvalidity = number;
 
-	uids = imapx_parse_uids (stream, cancellable, error);
-	if (uids == NULL)
+	uids = imapx_parse_uids (stream, cancellable, &local_error);
+	if (uids == NULL) {
+		/* Some broken servers can return truncated response, like:
+		   B00083 OK [COPYUID 4154  ] COPY completed.
+		   Just ignore such server error.
+		*/
+		if (g_error_matches (local_error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_IGNORE)) {
+			g_clear_error (&local_error);
+			return TRUE;
+		}
+
+		if (local_error)
+			g_propagate_error (error, local_error);
+
 		return FALSE;
+	}
 
 	sinfo->u.copyuid.uids = uids;
 
 	uids = imapx_parse_uids (stream, cancellable, error);
-	if (uids == NULL)
+	if (uids == NULL) {
+		if (g_error_matches (local_error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_IGNORE)) {
+			g_clear_error (&local_error);
+			return TRUE;
+		}
+
+		if (local_error)
+			g_propagate_error (error, local_error);
+
 		return FALSE;
+	}
 
 	sinfo->u.copyuid.copied_uids = uids;
 
@@ -2376,7 +2430,7 @@ imapx_parse_status (CamelIMAPXInputStrea
 			break;
 		default:
 			g_set_error (
-				error, CAMEL_IMAPX_ERROR, 1,
+				error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 				"expecting OK/NO/BAD");
 			goto fail;
 	}
@@ -2486,7 +2540,7 @@ imapx_parse_status (CamelIMAPXInputStrea
 				stream, &token, &len, cancellable, NULL);
 			if (tok == '\n' || tok < 0) {
 				g_set_error (
-					error, CAMEL_IMAPX_ERROR, 1,
+					error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 					"server response truncated");
 				goto fail;
 			}
@@ -2502,6 +2556,10 @@ imapx_parse_status (CamelIMAPXInputStrea
 	if (!success)
 		goto fail;
 
+	if (sinfo->text) {
+		g_strstrip (sinfo->text);
+	}
+
 	goto exit;
 
 fail:
@@ -2915,7 +2973,7 @@ camel_imapx_parse_quota (CamelIMAPXInput
 			break;
 		default:
 			g_set_error (
-				error, CAMEL_IMAPX_ERROR, 1,
+				error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
 				"quota_response: expecting '('");
 			goto fail;
 	}
@@ -3228,3 +3286,18 @@ imapx_get_temp_uid (void)
 
 	return res;
 }
+
+gboolean
+imapx_util_all_is_ascii (const gchar *str)
+{
+	gint ii;
+	gboolean all_ascii = TRUE;
+
+	g_return_val_if_fail (str != NULL, FALSE);
+
+	for (ii = 0; str[ii] && all_ascii; ii++) {
+		all_ascii = str[ii] > 0;
+	}
+
+	return all_ascii;
+}
diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.h
--- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.h.imapx-update-to-upstream	2014-10-31 15:25:33.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.h	2016-08-15 13:52:41.979976329 +0200
@@ -2,17 +2,18 @@
 /*
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
- * This library is free software; you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
  */
 
 #ifndef CAMEL_IMAPX_UTILS_H
@@ -25,6 +26,8 @@
 
 G_BEGIN_DECLS
 
+#define CamelHeaderAddress struct _camel_header_address
+
 /* FIXME Split off a camel-imapx-types.h file with supplemental
  *       enum/struct definitions and helper macros, so we don't
  *       have these conflicting header dependencies. */
@@ -213,7 +216,7 @@ struct _CamelMessageContentInfo *
 		imapx_parse_body_fields		(CamelIMAPXInputStream *stream,
 						 GCancellable *cancellable,
 						 GError **error);
-struct _camel_header_address *
+CamelHeaderAddress *
 		imapx_parse_address_list	(CamelIMAPXInputStream *stream,
 						 GCancellable *cancellable,
 						 GError **error);
@@ -392,6 +395,8 @@ gchar *		imapx_path_to_physical		(const
 						 const gchar *vpath);
 gchar *		imapx_get_temp_uid		(void);
 
+gboolean	imapx_util_all_is_ascii		(const gchar *str);
+
 G_END_DECLS
 
 #endif /* CAMEL_IMAPX_UTILS_H */
diff -up evolution-data-server-3.12.11/camel/providers/imapx/Makefile.am.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/Makefile.am
--- evolution-data-server-3.12.11/camel/providers/imapx/Makefile.am.imapx-update-to-upstream	2014-04-23 14:37:03.000000000 +0200
+++ evolution-data-server-3.12.11/camel/providers/imapx/Makefile.am	2016-08-15 13:52:41.982976329 +0200
@@ -14,6 +14,7 @@ libcamelimapx_la_CPPFLAGS = \
 	-I$(top_builddir)/camel \
 	$(CAMEL_CFLAGS) \
 	$(GIO_UNIX_CFLAGS) \
+	$(EVOLUTION_CALENDAR_CFLAGS) \
 	-DG_LOG_DOMAIN=\"camel-imapx\" \
 	$(CODE_COVERAGE_CFLAGS) \
 	$(NULL)
@@ -63,6 +64,7 @@ libcamelimapx_la_LIBADD = \
 	$(top_builddir)/camel/libcamel-1.2.la \
 	$(CAMEL_LIBS) \
 	$(GIO_UNIX_LIBS) \
+	$(EVOLUTION_CALENDAR_LIBS) \
 	$(NULL)
 
 libcamelimapx_la_LDFLAGS = -avoid-version -module $(NO_UNDEFINED) \
diff -up evolution-data-server-3.12.11/camel/providers/imapx/test-imapx.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/test-imapx.c
--- evolution-data-server-3.12.11/camel/providers/imapx/test-imapx.c.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/camel/providers/imapx/test-imapx.c	2016-08-15 13:52:41.983976329 +0200
@@ -2,17 +2,18 @@
 /*
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
- * This library is free software; you can redistribute it and/or modify it
+ * This library is free software: you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
  */
 
 #include <camel/camel.h>
diff -up evolution-data-server-3.12.11/libedataserver/e-source-mail-account.c.imapx-update-to-upstream evolution-data-server-3.12.11/libedataserver/e-source-mail-account.c
--- evolution-data-server-3.12.11/libedataserver/e-source-mail-account.c.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/libedataserver/e-source-mail-account.c	2016-08-15 13:52:41.983976329 +0200
@@ -47,11 +47,13 @@
 struct _ESourceMailAccountPrivate {
 	GMutex property_lock;
 	gchar *identity_uid;
+	gboolean needs_initial_setup;
 };
 
 enum {
 	PROP_0,
-	PROP_IDENTITY_UID
+	PROP_IDENTITY_UID,
+	PROP_NEEDS_INITIAL_SETUP
 };
 
 G_DEFINE_TYPE (
@@ -71,6 +73,12 @@ source_mail_account_set_property (GObjec
 				E_SOURCE_MAIL_ACCOUNT (object),
 				g_value_get_string (value));
 			return;
+
+		case PROP_NEEDS_INITIAL_SETUP:
+			e_source_mail_account_set_needs_initial_setup (
+				E_SOURCE_MAIL_ACCOUNT (object),
+				g_value_get_boolean (value));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -89,6 +97,13 @@ source_mail_account_get_property (GObjec
 				e_source_mail_account_dup_identity_uid (
 				E_SOURCE_MAIL_ACCOUNT (object)));
 			return;
+
+		case PROP_NEEDS_INITIAL_SETUP:
+			g_value_set_boolean (
+				value,
+				e_source_mail_account_get_needs_initial_setup (
+				E_SOURCE_MAIL_ACCOUNT (object)));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -137,6 +152,19 @@ e_source_mail_account_class_init (ESourc
 			G_PARAM_CONSTRUCT |
 			G_PARAM_STATIC_STRINGS |
 			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_NEEDS_INITIAL_SETUP,
+		g_param_spec_boolean (
+			"needs-initial-setup",
+			"Needs Initial Setup",
+			"Whether the account needs to do an initial setup",
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
 }
 
 static void
@@ -227,3 +255,43 @@ e_source_mail_account_set_identity_uid (
 	g_object_notify (G_OBJECT (extension), "identity-uid");
 }
 
+/**
+ * e_source_mail_account_get_needs_initial_setup:
+ * @extension: an #ESourceMailAccount
+ *
+ * Check whether the mail account needs to do its initial setup.
+ *
+ * Returns: %TRUE, when the account needs to run its initial setup
+ *
+ * Since: 3.12.11-25 (3.20)
+ **/
+gboolean
+e_source_mail_account_get_needs_initial_setup (ESourceMailAccount *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_ACCOUNT (extension), FALSE);
+
+	return extension->priv->needs_initial_setup;
+}
+
+/**
+ * e_source_mail_account_set_needs_initial_setup:
+ * @extension: an #ESourceMailAccount
+ * @needs_initial_setup: value to set
+ *
+ * Sets whether the account needs to run its initial setup.
+ *
+ * Since: 3.12.11-25 (3.20)
+ **/
+void
+e_source_mail_account_set_needs_initial_setup (ESourceMailAccount *extension,
+					       gboolean needs_initial_setup)
+{
+	g_return_if_fail (E_IS_SOURCE_MAIL_ACCOUNT (extension));
+
+	if ((extension->priv->needs_initial_setup ? 1 : 0) == (needs_initial_setup ? 1 : 0))
+		return;
+
+	extension->priv->needs_initial_setup = needs_initial_setup;
+
+	g_object_notify (G_OBJECT (extension), "needs-initial-setup");
+}
diff -up evolution-data-server-3.12.11/libedataserver/e-source-mail-account.h.imapx-update-to-upstream evolution-data-server-3.12.11/libedataserver/e-source-mail-account.h
--- evolution-data-server-3.12.11/libedataserver/e-source-mail-account.h.imapx-update-to-upstream	2014-03-24 10:07:52.000000000 +0100
+++ evolution-data-server-3.12.11/libedataserver/e-source-mail-account.h	2016-08-15 13:52:41.983976329 +0200
@@ -85,6 +85,11 @@ gchar *		e_source_mail_account_dup_ident
 void		e_source_mail_account_set_identity_uid
 					(ESourceMailAccount *extension,
 					 const gchar *identity_uid);
+gboolean	e_source_mail_account_get_needs_initial_setup
+					(ESourceMailAccount *extension);
+void		e_source_mail_account_set_needs_initial_setup
+					(ESourceMailAccount *extension,
+					 gboolean needs_initial_setup);
 
 G_END_DECLS
 
diff -up evolution-data-server-3.12.11/po/POTFILES.in.imapx-update-to-upstream evolution-data-server-3.12.11/po/POTFILES.in
--- evolution-data-server-3.12.11/po/POTFILES.in.imapx-update-to-upstream	2014-11-04 13:57:25.000000000 +0100
+++ evolution-data-server-3.12.11/po/POTFILES.in	2016-08-15 13:52:41.984976329 +0200
@@ -134,6 +134,7 @@ camel/camel-vee-store.c
 camel/camel-vee-summary.c
 camel/camel-vtrash-folder.c
 camel/providers/imapx/camel-imapx-command.c
+camel/providers/imapx/camel-imapx-conn-manager.c
 camel/providers/imapx/camel-imapx-folder.c
 camel/providers/imapx/camel-imapx-input-stream.c
 camel/providers/imapx/camel-imapx-provider.c