Blob Blame History Raw
diff -up evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.c.imapx-error-cancelled-message-download evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.c
--- evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.c.imapx-error-cancelled-message-download	2014-05-16 11:36:17.872612953 +0200
+++ evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.c	2014-05-16 11:36:17.884612846 +0200
@@ -883,3 +883,21 @@ camel_imapx_conn_manager_close_connectio
 	g_list_free_full (connections, (GDestroyNotify) connection_info_cancel_and_unref);
 }
 
+/* for debugging purposes only */
+void
+camel_imapx_conn_manager_dump_queue_status (CamelIMAPXConnManager *con_man)
+{
+	GList *list, *link;
+
+	g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man));
+
+	list = imapx_conn_manager_list_info (con_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);
+	}
+
+	g_list_free (list);
+}
diff -up evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.h.imapx-error-cancelled-message-download evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.h
--- evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.h.imapx-error-cancelled-message-download	2014-05-16 11:36:17.873612944 +0200
+++ evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.h	2014-05-16 11:36:17.884612846 +0200
@@ -84,6 +84,10 @@ void		camel_imapx_conn_manager_update_co
 						 CamelIMAPXServer *server,
 						 const gchar *folder_name);
 
+/* for debugging purposes only */
+void		camel_imapx_conn_manager_dump_queue_status
+						(CamelIMAPXConnManager *con_man);
+
 G_END_DECLS
 
 #endif /* _CAMEL_IMAPX_SERVER_H */
diff -up evolution-data-server-3.8.5/camel/camel-imapx-job.h.imapx-error-cancelled-message-download evolution-data-server-3.8.5/camel/camel-imapx-job.h
--- evolution-data-server-3.8.5/camel/camel-imapx-job.h.imapx-error-cancelled-message-download	2014-05-16 11:36:17.798613612 +0200
+++ evolution-data-server-3.8.5/camel/camel-imapx-job.h	2014-05-16 11:36:17.884612846 +0200
@@ -55,7 +55,7 @@ struct _CamelIMAPXJob {
 	guint noreply:1;	/* dont wait for reply */
 	guint32 type;		/* operation type */
 	gint pri;		/* the command priority */
-	gshort commands;	/* counts how many commands are outstanding */
+	volatile gint commands;	/* counts how many commands are outstanding */
 };
 
 CamelIMAPXJob *	camel_imapx_job_new		(GCancellable *cancellable);
diff -up evolution-data-server-3.8.5/camel/camel-imapx-server.c.imapx-error-cancelled-message-download evolution-data-server-3.8.5/camel/camel-imapx-server.c
--- evolution-data-server-3.8.5/camel/camel-imapx-server.c.imapx-error-cancelled-message-download	2014-05-16 11:36:17.879612890 +0200
+++ evolution-data-server-3.8.5/camel/camel-imapx-server.c	2014-05-16 11:36:17.885612837 +0200
@@ -989,9 +989,7 @@ imapx_uidset_add (struct _uidset_state *
 /* Must hold QUEUE_LOCK */
 static gboolean
 imapx_command_start (CamelIMAPXServer *is,
-                     CamelIMAPXCommand *ic,
-                     GCancellable *cancellable,
-                     GError **error)
+                     CamelIMAPXCommand *ic)
 {
 	CamelIMAPXStream *stream = NULL;
 	CamelIMAPXCommandPart *cp;
@@ -1002,6 +1000,7 @@ imapx_command_start (CamelIMAPXServer *i
 	gboolean success = FALSE;
 	gchar *string;
 	gint retval;
+	GCancellable *cancellable;
 	GError *local_error = NULL;
 
 	camel_imapx_command_close (ic);
@@ -1024,7 +1023,11 @@ imapx_command_start (CamelIMAPXServer *i
 	imapx_server_command_added (is, ic);
 
 	job = camel_imapx_command_get_job (ic);
-	if (job && g_cancellable_set_error_if_cancelled (camel_imapx_job_get_cancellable (job), &local_error)) {
+	if (job)
+		cancellable = camel_imapx_job_get_cancellable (job);
+	else
+		cancellable = NULL;
+	if (job && g_cancellable_set_error_if_cancelled (cancellable, &local_error)) {
 		camel_imapx_job_set_error (job, local_error);
 		g_clear_error (&local_error);
 		goto err;
@@ -1034,7 +1037,7 @@ imapx_command_start (CamelIMAPXServer *i
 
 	if (stream == NULL) {
 		g_set_error (
-			error, CAMEL_IMAPX_ERROR, 1,
+			&local_error, CAMEL_IMAPX_ERROR, 1,
 			"Cannot issue command, no stream available");
 		goto err;
 	}
@@ -1052,7 +1055,7 @@ imapx_command_start (CamelIMAPXServer *i
 	string = g_strdup_printf (
 		"%c%05u %s\r\n", is->tagprefix, ic->tag, cp->data);
 	retval = camel_stream_write_string (
-		CAMEL_STREAM (stream), string, cancellable, error);
+		CAMEL_STREAM (stream), string, cancellable, &local_error);
 	g_free (string);
 
 	if (retval == -1)
@@ -1060,7 +1063,7 @@ imapx_command_start (CamelIMAPXServer *i
 
 	while (is->literal == ic && cp_literal_plus) {
 		/* Sent LITERAL+ continuation immediately */
-		if (!imapx_continuation (is, stream, TRUE, cancellable, error))
+		if (!imapx_continuation (is, stream, TRUE, cancellable, &local_error))
 			goto err;
 	}
 
@@ -1080,6 +1083,10 @@ err:
 	if (ic->status->result == IMAPX_OK)
 		ic->status->result = IMAPX_UNKNOWN;
 
+	if (job && local_error) {
+		camel_imapx_job_set_error (job, local_error);
+		g_clear_error (&local_error);
+	}
 	/* Send a NULL GError since we've already set a
 	 * GError to get here, and we're not interested
 	 * in individual command errors. */
@@ -1134,9 +1141,7 @@ duplicate_fetch_or_refresh (CamelIMAPXSe
  * must have QUEUE lock */
 
 static gboolean
-imapx_command_start_next (CamelIMAPXServer *is,
-                          GCancellable *cancellable,
-                          GError **error)
+imapx_command_start_next (CamelIMAPXServer *is)
 {
 	CamelIMAPXCommand *first_ic;
 	CamelFolder *folder;
@@ -1151,7 +1156,7 @@ imapx_command_start_next (CamelIMAPXServ
 
 	folder = g_weak_ref_get (&is->select_pending);
 	if (folder != NULL) {
-		GQueue start = G_QUEUE_INIT;
+		CamelIMAPXCommand *start_ic = NULL;
 		GList *head, *link;
 
 		c (is->tagprefix, "-- Checking job queue for non-folder jobs\n");
@@ -1159,7 +1164,7 @@ imapx_command_start_next (CamelIMAPXServ
 		head = camel_imapx_command_queue_peek_head_link (is->queue);
 
 		/* Tag which commands in the queue to start. */
-		for (link = head; link != NULL; link = g_list_next (link)) {
+		for (link = head; link != NULL && !start_ic; link = g_list_next (link)) {
 			CamelIMAPXCommand *ic = link->data;
 
 			if (ic->pri < min_pri)
@@ -1169,37 +1174,23 @@ imapx_command_start_next (CamelIMAPXServ
 			if (!ic->select) {
 				c (is->tagprefix, "--> starting '%s'\n", ic->name);
 				min_pri = ic->pri;
-				g_queue_push_tail (&start, link);
-			}
 
-			if (g_queue_get_length (&start) == MAX_COMMANDS)
-				break;
+				/* 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);
+			}
 		}
 
-		if (g_queue_is_empty (&start))
+		if (!start_ic)
 			c (is->tagprefix, "* no, waiting for pending select '%s'\n", camel_folder_get_full_name (folder));
 
-		/* Start the tagged commands.
-		 *
-		 * Each command must be removed from 'is->queue' before
-		 * starting it, so we temporarily reference the command
-		 * to avoid accidentally finalizing it. */
-		while ((link = g_queue_pop_head (&start)) != NULL) {
-			CamelIMAPXCommand *ic;
-
-			ic = camel_imapx_command_ref (link->data);
-			camel_imapx_command_queue_delete_link (is->queue, link);
-			imapx_server_command_removed (is, ic);
-
-			success = imapx_command_start (
-				is, ic, cancellable, error);
-
-			camel_imapx_command_unref (ic);
-
-			if (!success) {
-				g_queue_clear (&start);
-				break;
-			}
+		/* 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);
 		}
 
 		g_clear_object (&folder);
@@ -1219,7 +1210,7 @@ imapx_command_start_next (CamelIMAPXServ
 
 			if (stream != NULL) {
 				stop_result = imapx_stop_idle (
-					is, stream, cancellable, error);
+					is, stream, is->cancellable, NULL);
 				g_object_unref (stream);
 			}
 
@@ -1255,9 +1246,8 @@ imapx_command_start_next (CamelIMAPXServ
 	/* See if any queued jobs on this select first */
 	folder = g_weak_ref_get (&is->select_folder);
 	if (folder != NULL) {
-		GQueue start = G_QUEUE_INIT;
+		CamelIMAPXCommand *start_ic = NULL;
 		GList *head, *link;
-		gboolean commands_started = FALSE;
 
 		c (
 			is->tagprefix, "- we're selected on '%s', current jobs?\n",
@@ -1284,7 +1274,7 @@ imapx_command_start_next (CamelIMAPXServ
 		head = camel_imapx_command_queue_peek_head_link (is->queue);
 
 		/* Tag which commands in the queue to start. */
-		for (link = head; link != NULL; link = g_list_next (link)) {
+		for (link = head; link != NULL && !start_ic; link = g_list_next (link)) {
 			CamelIMAPXCommand *ic = link->data;
 
 			if (is->literal != NULL)
@@ -1299,48 +1289,29 @@ imapx_command_start_next (CamelIMAPXServ
 					    !duplicate_fetch_or_refresh (is, ic))) {
 				c (is->tagprefix, "--> starting '%s'\n", ic->name);
 				min_pri = ic->pri;
-				g_queue_push_tail (&start, link);
+				/* 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 folder, but we don't want to
 				 * consider jobs with _lower_ priority than this, even if they
 				 * are for the selected folder. */
 				min_pri = ic->pri;
 			}
-
-			if (g_queue_get_length (&start) == MAX_COMMANDS)
-				break;
 		}
 
 		g_clear_object (&folder);
 
-		/* Start the tagged commands.
-		 *
-		 * Each command must be removed from 'is->queue' before
-		 * starting it, so we temporarily reference the command
-		 * to avoid accidentally finalizing it. */
-		while ((link = g_queue_pop_head (&start)) != NULL) {
-			CamelIMAPXCommand *ic;
-			gboolean success;
+		/* 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);
 
-			ic = camel_imapx_command_ref (link->data);
-			camel_imapx_command_queue_delete_link (is->queue, link);
-			imapx_server_command_removed (is, ic);
-
-			success = imapx_command_start (
-				is, ic, cancellable, error);
-
-			camel_imapx_command_unref (ic);
-
-			if (!success) {
-				g_queue_clear (&start);
-				return FALSE;
-			}
-
-			commands_started = TRUE;
-		}
-
-		if (commands_started)
 			return TRUE;
+		}
 	}
 
 	/* This won't be NULL because we checked for an empty queue above. */
@@ -1349,13 +1320,19 @@ imapx_command_start_next (CamelIMAPXServ
 	/* If we need to select a folder for the first command, do it now,
 	 * once it is complete it will re-call us if it succeeded. */
 	if (first_ic->select) {
+		GError *local_error = NULL;
+		CamelIMAPXJob *job;
 		c (
 			is->tagprefix, "Selecting folder '%s' for command '%s'(%p)\n",
 			camel_folder_get_full_name (first_ic->select),
 			first_ic->name, first_ic);
-		imapx_select (is, first_ic->select, FALSE, cancellable, error);
+		job = camel_imapx_command_get_job (first_ic);
+		imapx_select (is, first_ic->select, FALSE, job ? camel_imapx_job_get_cancellable (job) : NULL, &local_error);
+		if (local_error && job)
+			camel_imapx_job_set_error (job, local_error);
+		g_clear_error (&local_error);
 	} else {
-		GQueue start = G_QUEUE_INIT;
+		CamelIMAPXCommand *start_ic = NULL;
 		GList *head, *link;
 
 		min_pri = first_ic->pri;
@@ -1365,7 +1342,7 @@ imapx_command_start_next (CamelIMAPXServ
 		head = camel_imapx_command_queue_peek_head_link (is->queue);
 
 		/* Tag which commands in the queue to start. */
-		for (link = head; link != NULL; link = g_list_next (link)) {
+		for (link = head; link != NULL && !start_ic; link = g_list_next (link)) {
 			CamelIMAPXCommand *ic = link->data;
 
 			if (is->literal != NULL)
@@ -1378,37 +1355,21 @@ imapx_command_start_next (CamelIMAPXServ
 					    !duplicate_fetch_or_refresh (is, ic))) {
 				c (is->tagprefix, "* queueing job %3d '%s'\n", (gint) ic->pri, ic->name);
 				min_pri = ic->pri;
-				g_queue_push_tail (&start, link);
+				/* 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);
 			}
-
-			if (g_queue_get_length (&start) == MAX_COMMANDS)
-				break;
 		}
 
 		g_clear_object (&folder);
 
-		/* Start the tagged commands.
-		 *
-		 * Each command must be removed from 'is->queue' before
-		 * starting it, so we temporarily reference the command
-		 * to avoid accidentally finalizing it. */
-		while ((link = g_queue_pop_head (&start)) != NULL) {
-			CamelIMAPXCommand *ic;
-			gboolean success;
-
-			ic = camel_imapx_command_ref (link->data);
-			camel_imapx_command_queue_delete_link (is->queue, link);
-			imapx_server_command_removed (is, ic);
-
-			success = imapx_command_start (
-				is, ic, cancellable, error);
-
-			camel_imapx_command_unref (ic);
-
-			if (!success) {
-				g_queue_clear (&start);
-				return FALSE;
-			}
+		/* 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);
 		}
 	}
 
@@ -1430,7 +1391,6 @@ imapx_is_command_queue_empty (CamelIMAPX
 static gboolean
 imapx_command_queue (CamelIMAPXServer *is,
                      CamelIMAPXCommand *ic,
-                     GCancellable *cancellable,
                      GError **error)
 {
 	CamelIMAPXJob *job;
@@ -1472,7 +1432,7 @@ imapx_command_queue (CamelIMAPXServer *i
 	camel_imapx_command_queue_insert_sorted (is->queue, ic);
 	imapx_server_command_added (is, ic);
 
-	success = imapx_command_start_next (is, cancellable, error);
+	success = imapx_command_start_next (is);
 
 	QUEUE_UNLOCK (is);
 
@@ -2808,7 +2768,7 @@ imapx_continuation (CamelIMAPXServer *is
 
 		QUEUE_LOCK (is);
 		is->literal = NULL;
-		success = imapx_command_start_next (is, cancellable, error);
+		success = imapx_command_start_next (is);
 		QUEUE_UNLOCK (is);
 
 		return success;
@@ -2950,7 +2910,7 @@ noskip:
 	is->literal = newliteral;
 
 	if (!litplus)
-		success = imapx_command_start_next (is, cancellable, error);
+		success = imapx_command_start_next (is);
 	QUEUE_UNLOCK (is);
 
 	return success;
@@ -3063,7 +3023,7 @@ imapx_completion (CamelIMAPXServer *is,
 	camel_imapx_command_unref (ic);
 
 	QUEUE_LOCK (is);
-	success = imapx_command_start_next (is, cancellable, error);
+	success = imapx_command_start_next (is);
 	QUEUE_UNLOCK (is);
 
 	return success;
@@ -3129,7 +3089,7 @@ imapx_command_run (CamelIMAPXServer *is,
 	camel_imapx_command_close (ic);
 
 	QUEUE_LOCK (is);
-	imapx_command_start (is, ic, cancellable, error);
+	imapx_command_start (is, ic);
 	QUEUE_UNLOCK (is);
 
 	while (success && ic->status == NULL)
@@ -3200,7 +3160,7 @@ imapx_command_run_sync (CamelIMAPXServer
 	/* Unref'ed in imapx_command_complete(). */
 	camel_imapx_command_ref (ic);
 
-	success = imapx_command_queue (is, ic, cancellable, error);
+	success = imapx_command_queue (is, ic, error);
 
 	if (success)
 		camel_imapx_command_wait (ic);
@@ -3252,10 +3212,14 @@ imapx_unregister_job (CamelIMAPXServer *
 		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);
 }
 
@@ -3361,7 +3325,7 @@ imapx_job_idle_start (CamelIMAPXJob *job
 	/* Don't issue it if the idle was cancelled already */
 	if (is->idle->state == IMAPX_IDLE_PENDING) {
 		is->idle->state = IMAPX_IDLE_ISSUED;
-		success = imapx_command_start (is, ic, cancellable, error);
+		success = imapx_command_start (is, ic);
 	} else {
 		imapx_unregister_job (is, job);
 		camel_imapx_command_unref (ic);
@@ -3903,7 +3867,7 @@ imapx_select (CamelIMAPXServer *is,
 		camel_imapx_command_add_qresync_parameter (ic, folder);
 
 	ic->complete = imapx_command_select_done;
-	imapx_command_start (is, ic, cancellable, error);
+	imapx_command_start (is, ic);
 
 	return TRUE;
 }
@@ -4579,6 +4543,7 @@ imapx_command_fetch_message_done (CamelI
 	CamelFolder *folder;
 	GetMessageData *data;
 	CamelIMAPXFolder *ifolder;
+	GError *local_error = NULL;
 	gboolean success = TRUE;
 
 	job = camel_imapx_command_get_job (ic);
@@ -4594,11 +4559,11 @@ imapx_command_fetch_message_done (CamelI
 	 * or we failed.  Failure is handled in the fetch code, so
 	 * we just return the job, or keep it alive with more requests */
 
-	job->commands--;
+	g_atomic_int_add (&job->commands, -1);
 
-	if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) {
+	if (camel_imapx_command_set_error_if_failed (ic, cancellable, &local_error)) {
 		g_prefix_error (
-			error, "%s: ",
+			&local_error, "%s: ",
 			_("Error fetching message"));
 		data->body_len = -1;
 		success = FALSE;
@@ -4627,18 +4592,24 @@ imapx_command_fetch_message_done (CamelI
 			camel_imapx_command_set_job (new_ic, job);
 			new_ic->pri = job->pri - 1;
 			data->fetch_offset += MULTI_SIZE;
-			job->commands++;
+			g_atomic_int_add (&job->commands, 1);
 
 			success = imapx_command_queue (
-				is, new_ic, cancellable, error);
+				is, new_ic, &local_error);
 
 			goto exit;
 		}
 	}
 
 	/* If we have more messages to fetch, skip the rest. */
-	if (job->commands > 0)
+	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. */
 
@@ -4646,24 +4617,24 @@ imapx_command_fetch_message_done (CamelI
 
 	if (success) {
 		success = camel_stream_flush (
-			data->stream, cancellable, error) == 0;
+			data->stream, cancellable, &local_error) == 0;
 		g_prefix_error (
-			error, "%s: ",
+			&local_error, "%s: ",
 			_("Failed to close the tmp stream"));
 	}
 
 	if (success) {
 		success = camel_stream_close (
-			data->stream, cancellable, error) == 0;
+			data->stream, cancellable, &local_error) == 0;
 		g_prefix_error (
-			error, "%s: ",
+			&local_error, "%s: ",
 			_("Failed to close the tmp stream"));
 	}
 
-	if (success && g_cancellable_set_error_if_cancelled (cancellable, error)) {
+	if (success && g_cancellable_set_error_if_cancelled (cancellable, &local_error)) {
 		success = FALSE;
 		g_prefix_error (
-			error, "%s: ",
+			&local_error, "%s: ",
 			_("Error fetching message"));
 	}
 
@@ -4686,11 +4657,11 @@ imapx_command_fetch_message_done (CamelI
 			/* Exchange the "tmp" stream for the "cur" stream. */
 			g_clear_object (&data->stream);
 			data->stream = camel_data_cache_get (
-				ifolder->cache, "cur", data->uid, error);
+				ifolder->cache, "cur", data->uid, &local_error);
 			success = (data->stream != NULL);
 		} else {
 			g_set_error (
-				error, G_FILE_ERROR,
+				&local_error, G_FILE_ERROR,
 				g_file_error_from_errno (errno),
 				"%s: %s",
 				_("Failed to copy the tmp file"),
@@ -4702,7 +4673,20 @@ imapx_command_fetch_message_done (CamelI
 		g_free (tmp_filename);
 	}
 
-	camel_data_cache_remove (ifolder->cache, "tmp", data->uid, NULL);
+	/* Delete the 'tmp' file only if the operation wasn't cancelled. 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 (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		camel_data_cache_remove (ifolder->cache, "tmp", data->uid, NULL);
+
+	/* Avoid possible use-after-free when the imapx_unregister_job() can
+	   also free the 'job' structure. */
+	if (local_error != NULL)
+		camel_imapx_job_set_error (job, local_error);
+
 	imapx_unregister_job (is, job);
 
 exit:
@@ -4710,6 +4694,9 @@ exit:
 
 	camel_imapx_command_unref (ic);
 
+	if (local_error)
+		g_propagate_error (error, local_error);
+
 	return success;
 }
 
@@ -4743,10 +4730,10 @@ imapx_job_get_message_start (CamelIMAPXJ
 			camel_imapx_command_set_job (ic, job);
 			ic->pri = job->pri;
 			data->fetch_offset += MULTI_SIZE;
-			job->commands++;
+			g_atomic_int_add (&job->commands, 1);
 
 			success = imapx_command_queue (
-				is, ic, cancellable, error);
+				is, ic, error);
 			if (!success)
 				break;
 		}
@@ -4758,9 +4745,9 @@ imapx_job_get_message_start (CamelIMAPXJ
 		ic->complete = imapx_command_fetch_message_done;
 		camel_imapx_command_set_job (ic, job);
 		ic->pri = job->pri;
-		job->commands++;
+		g_atomic_int_add (&job->commands, 1);
 
-		success = imapx_command_queue (is, ic, cancellable, error);
+		success = imapx_command_queue (is, ic, error);
 	}
 
 	g_object_unref (folder);
@@ -4903,14 +4890,14 @@ imapx_command_copy_messages_step_start (
 		if (res == 1) {
 			camel_imapx_command_add (ic, " %f", data->dest);
 			data->index = i + 1;
-			return imapx_command_queue (is, ic, cancellable, error);
+			return imapx_command_queue (is, ic, error);
 		}
 	}
 
 	data->index = i;
 	if (imapx_uidset_done (&data->uidset, ic)) {
 		camel_imapx_command_add (ic, " %f", data->dest);
-		return imapx_command_queue (is, ic, cancellable, error);
+		return imapx_command_queue (is, ic, error);
 	}
 
 	return TRUE;
@@ -5055,11 +5042,11 @@ imapx_job_append_message_start (CamelIMA
 	ic->complete = imapx_command_append_message_done;
 	camel_imapx_command_set_job (ic, job);
 	ic->pri = job->pri;
-	job->commands++;
+	g_atomic_int_add (&job->commands, 1);
 
 	g_object_unref (folder);
 
-	return imapx_command_queue (is, ic, cancellable, error);
+	return imapx_command_queue (is, ic, error);
 }
 
 /* ********************************************************************** */
@@ -5233,7 +5220,7 @@ imapx_command_step_fetch_done (CamelIMAP
 
 					g_object_unref (folder);
 
-					return imapx_command_queue (is, ic, cancellable, error);
+					return imapx_command_queue (is, ic, error);
 				}
 			}
 		}
@@ -5245,7 +5232,7 @@ imapx_command_step_fetch_done (CamelIMAP
 
 			g_object_unref (folder);
 
-			return imapx_command_queue (is, ic, cancellable, error);
+			return imapx_command_queue (is, ic, error);
 		}
 	}
 
@@ -5556,7 +5543,7 @@ imapx_job_scan_changes_start (CamelIMAPX
 
 	g_object_unref (folder);
 
-	return imapx_command_queue (is, ic, cancellable, error);
+	return imapx_command_queue (is, ic, error);
 }
 
 static gboolean
@@ -5730,7 +5717,7 @@ imapx_job_fetch_new_messages_start (Came
 
 	g_object_unref (folder);
 
-	return imapx_command_queue (is, ic, cancellable, error);
+	return imapx_command_queue (is, ic, error);
 }
 
 static gboolean
@@ -5863,7 +5850,7 @@ imapx_job_fetch_messages_start (CamelIMA
 
 	g_object_unref (folder);
 
-	return imapx_command_queue (is, ic, cancellable, error);
+	return imapx_command_queue (is, ic, error);
 }
 
 static gboolean
@@ -6208,7 +6195,7 @@ imapx_job_expunge_start (CamelIMAPXJob *
 		ic->pri = job->pri;
 		ic->complete = imapx_command_expunge_done;
 
-		success = imapx_command_queue (is, ic, cancellable, error);
+		success = imapx_command_queue (is, ic, error);
 	}
 
 	g_object_unref (folder);
@@ -6279,7 +6266,7 @@ imapx_job_list_start (CamelIMAPXJob *job
 	camel_imapx_command_set_job (ic, job);
 	ic->complete = imapx_command_list_done;
 
-	return imapx_command_queue (is, ic, cancellable, error);
+	return imapx_command_queue (is, ic, error);
 }
 
 static gboolean
@@ -6368,7 +6355,7 @@ imapx_job_manage_subscription_start (Cam
 
 	g_object_unref (store);
 
-	return imapx_command_queue (is, ic, cancellable, error);
+	return imapx_command_queue (is, ic, error);
 }
 
 /* ********************************************************************** */
@@ -6422,7 +6409,7 @@ imapx_job_create_folder_start (CamelIMAP
 
 	g_free (encoded_fname);
 
-	return imapx_command_queue (is, ic, cancellable, error);
+	return imapx_command_queue (is, ic, error);
 }
 
 /* ********************************************************************** */
@@ -6487,7 +6474,7 @@ imapx_job_delete_folder_start (CamelIMAP
 		camel_imapx_command_set_job (ic, job);
 		ic->complete = imapx_command_delete_folder_done;
 
-		success = imapx_command_queue (is, ic, cancellable, error);
+		success = imapx_command_queue (is, ic, error);
 
 		g_object_unref (folder);
 	}
@@ -6561,7 +6548,7 @@ imapx_job_rename_folder_start (CamelIMAP
 		camel_imapx_command_set_job (ic, job);
 		ic->complete = imapx_command_rename_folder_done;
 
-		success = imapx_command_queue (is, ic, cancellable, error);
+		success = imapx_command_queue (is, ic, error);
 
 		g_object_unref (folder);
 	}
@@ -6628,7 +6615,7 @@ imapx_job_update_quota_info_start (Camel
 	camel_imapx_command_set_job (ic, job);
 	ic->complete = imapx_command_update_quota_info_done;
 
-	success = imapx_command_queue (is, ic, cancellable, error);
+	success = imapx_command_queue (is, ic, error);
 
 	g_free (encoded_folder_name);
 
@@ -6699,7 +6686,7 @@ imapx_job_uid_search_start (CamelIMAPXJo
 
 	g_object_unref (folder);
 
-	return imapx_command_queue (is, ic, cancellable, error);
+	return imapx_command_queue (is, ic, error);
 }
 
 /* ********************************************************************** */
@@ -6752,7 +6739,7 @@ imapx_job_noop_start (CamelIMAPXJob *job
 		ic->pri = IMAPX_PRIORITY_NOOP;
 	}
 
-	return imapx_command_queue (is, ic, cancellable, error);
+	return imapx_command_queue (is, ic, error);
 }
 
 /* ********************************************************************** */
@@ -6810,7 +6797,7 @@ imapx_command_sync_changes_done (CamelIM
 	mobile_mode = camel_imapx_settings_get_mobile_mode (settings);
 	g_object_unref (settings);
 
-	job->commands--;
+	g_atomic_int_add (&job->commands, -1);
 
 	full_name = camel_folder_get_full_name (folder);
 	parent_store = camel_folder_get_parent_store (folder);
@@ -6862,7 +6849,7 @@ imapx_command_sync_changes_done (CamelIM
 		((CamelIMAPXFolder *) folder)->unread_on_server += data->unread_change;
 	}
 
-	if (job->commands == 0) {
+	if (g_atomic_int_get (&job->commands) == 0) {
 		if (folder->summary && (folder->summary->flags & CAMEL_FOLDER_SUMMARY_DIRTY) != 0) {
 			CamelStoreInfo *si;
 
@@ -6888,6 +6875,11 @@ imapx_command_sync_changes_done (CamelIM
 		camel_store_summary_save ((CamelStoreSummary *)((CamelIMAPXStore *) parent_store)->summary);
 
 		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_object_unref (folder);
@@ -6974,9 +6966,9 @@ imapx_job_sync_changes_start (CamelIMAPX
 					send = imapx_uidset_add (&ss, ic, camel_message_info_uid (info));
 				}
 				if (send == 1 || (i == uids->len - 1 && imapx_uidset_done (&ss, ic))) {
-					job->commands++;
+					g_atomic_int_add (&job->commands, 1);
 					camel_imapx_command_add (ic, " %tFLAGS.SILENT (%t)", on?"+":"-", flags_table[j].name);
-					if (!imapx_command_queue (is, ic, cancellable, error)) {
+					if (!imapx_command_queue (is, ic, error)) {
 						camel_message_info_free (info);
 						goto exit;
 					}
@@ -7015,9 +7007,9 @@ imapx_job_sync_changes_start (CamelIMAPX
 
 					if (imapx_uidset_add (&ss, ic, camel_message_info_uid (info)) == 1
 					    || (i == c->infos->len - 1 && imapx_uidset_done (&ss, ic))) {
-						job->commands++;
+						g_atomic_int_add (&job->commands, 1);
 						camel_imapx_command_add (ic, " %tFLAGS.SILENT (%t)", on?"+":"-", c->name);
-						if (!imapx_command_queue (is, ic, cancellable, error))
+						if (!imapx_command_queue (is, ic, error))
 							goto exit;
 						ic = NULL;
 					}
@@ -7029,11 +7021,14 @@ imapx_job_sync_changes_start (CamelIMAPX
 exit:
 	g_object_unref (folder);
 
-	/* Since this may start in another thread ... we need to
-	 * lock the commands count, ho hum */
-
-	if (job->commands == 0)
+	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);
+	}
 
 	return TRUE;
 }
@@ -7701,6 +7696,11 @@ imapx_server_get_message (CamelIMAPXServ
 		return NULL;
 	}
 
+	/* 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 (ifolder->cache, "tmp", uid, NULL);
+
 	data = g_slice_new0 (GetMessageData);
 	data->uid = g_strdup (uid);
 	data->stream = camel_data_cache_add (ifolder->cache, "tmp", uid, NULL);
@@ -8964,3 +8964,106 @@ camel_imapx_server_shutdown (CamelIMAPXS
 	g_clear_object (&cancellable);
 	g_clear_error (&shutdown_error_copy);
 }
+
+static const gchar *
+imapx_server_get_job_type_name (CamelIMAPXJob *job)
+{
+	if (!job)
+		return "[null]";
+
+	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_FOLDER:
+		return "CREATE_FOLDER";
+	case IMAPX_JOB_DELETE_FOLDER:
+		return "DELETE_FOLDER";
+	case IMAPX_JOB_RENAME_FOLDER:
+		return "RENAME_FOLDER";
+	case IMAPX_JOB_MANAGE_SUBSCRIPTION:
+		return "MANAGE_SUBSCRIPTION";
+	case IMAPX_JOB_UPDATE_QUOTA_INFO:
+		return "UPDATE_QUOTA_INFO";
+	case IMAPX_JOB_UID_SEARCH:
+		return "UID_SEARCH";
+	}
+
+	return "???";
+}
+
+static void
+imapx_server_dump_one_queue (CamelIMAPXCommandQueue *queue,
+			     const gchar *queue_name)
+{
+	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);
+
+	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));
+	}
+}
+
+/* for debugging purposes only */
+void
+camel_imapx_server_dump_queue_status (CamelIMAPXServer *imapx_server)
+{
+	g_return_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server));
+
+	QUEUE_LOCK (imapx_server);
+
+	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));
+
+	if (!g_queue_is_empty (&imapx_server->jobs)) {
+		GList *iter;
+		gint ii;
+
+		printf ("      Content of 'jobs':\n");
+
+		for (ii = 0, iter = g_queue_peek_head_link (&imapx_server->jobs); iter != NULL; iter = g_list_next (iter), ii++) {
+			CamelIMAPXJob *job = iter->data;
+
+			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);
+		}
+	}
+
+	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");
+
+	QUEUE_UNLOCK (imapx_server);
+}
diff -up evolution-data-server-3.8.5/camel/camel-imapx-server.h.imapx-error-cancelled-message-download evolution-data-server-3.8.5/camel/camel-imapx-server.h
--- evolution-data-server-3.8.5/camel/camel-imapx-server.h.imapx-error-cancelled-message-download	2014-05-16 11:36:17.875612926 +0200
+++ evolution-data-server-3.8.5/camel/camel-imapx-server.h	2014-05-16 11:36:17.885612837 +0200
@@ -312,6 +312,9 @@ struct _CamelIMAPXJob *
 						 CamelFolder *folder,
 						 guint32 job_type,
 						 const gchar *uid);
+/* for debugging purposes only */
+void		camel_imapx_server_dump_queue_status
+						(CamelIMAPXServer *imapx_server);
 
 G_END_DECLS
 
diff -up evolution-data-server-3.8.5/camel/camel-imapx-store.c.imapx-error-cancelled-message-download evolution-data-server-3.8.5/camel/camel-imapx-store.c
--- evolution-data-server-3.8.5/camel/camel-imapx-store.c.imapx-error-cancelled-message-download	2014-05-16 11:36:17.876612917 +0200
+++ evolution-data-server-3.8.5/camel/camel-imapx-store.c	2014-05-16 11:36:17.886612828 +0200
@@ -2226,3 +2226,12 @@ camel_imapx_store_ref_job (CamelIMAPXSto
 
 	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->con_man);
+}
diff -up evolution-data-server-3.8.5/camel/camel-imapx-store.h.imapx-error-cancelled-message-download evolution-data-server-3.8.5/camel/camel-imapx-store.h
--- evolution-data-server-3.8.5/camel/camel-imapx-store.h.imapx-error-cancelled-message-download	2014-05-16 11:36:17.876612917 +0200
+++ evolution-data-server-3.8.5/camel/camel-imapx-store.h	2014-05-16 11:36:17.886612828 +0200
@@ -120,6 +120,9 @@ struct _CamelIMAPXJob *
 						 CamelFolder *folder,
 						 guint32 job_type,
 						 const gchar *uid);
+/* for debugging purposes only */
+void		camel_imapx_store_dump_queue_status
+						(CamelIMAPXStore *imapx_store);
 
 G_END_DECLS