diff --git a/SOURCES/evolution-data-server-3.8.5-imapx-check-all-namespaces.patch b/SOURCES/evolution-data-server-3.8.5-imapx-check-all-namespaces.patch new file mode 100644 index 0000000..6e16289 --- /dev/null +++ b/SOURCES/evolution-data-server-3.8.5-imapx-check-all-namespaces.patch @@ -0,0 +1,68 @@ +diff -up evolution-data-server-3.8.5/camel/camel-imapx-store-summary.c.eds-1098036 evolution-data-server-3.8.5/camel/camel-imapx-store-summary.c +--- evolution-data-server-3.8.5/camel/camel-imapx-store-summary.c.eds-1098036 2013-07-23 13:57:54.000000000 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-store-summary.c 2014-05-19 15:39:42.145061474 +0200 +@@ -355,17 +355,26 @@ camel_imapx_store_summary_namespace_find + { + gint len; + CamelIMAPXStoreNamespace *ns; ++ CamelIMAPXStoreNamespace *namespaces[] = ++ { ++ s->namespaces->personal, ++ s->namespaces->other, ++ s->namespaces->shared, ++ NULL ++ }; ++ gint ii = 0; + +- /* NB: this currently only compares against 1 namespace, in future compare against others */ +- /* CHEN TODO */ +- ns = s->namespaces->personal; +- while (ns) { ++ for (ii = 0; ii < 3; ii++) { ++ ns = namespaces[ii]; ++ if (!ns) ++ continue; ++ ++ d ("find_path: comparing namespace '%s' to name '%s'\n", ns->path, path); + len = strlen (ns->path); + if (len == 0 + || (strncmp (ns->path, path, len) == 0 + && (path[len] == '/' || path[len] == 0))) + break; +- ns = NULL; + } + + /* have a default? */ +@@ -378,11 +387,20 @@ camel_imapx_store_summary_namespace_find + { + gint len = 0; + CamelIMAPXStoreNamespace *ns; ++ CamelIMAPXStoreNamespace *namespaces[] = ++ { ++ s->namespaces->personal, ++ s->namespaces->other, ++ s->namespaces->shared, ++ NULL ++ }; ++ gint ii = 0; ++ ++ for (ii = 0; ii < 3; ii++) { ++ ns = namespaces[ii]; ++ if (!ns) ++ continue; + +- /* NB: this currently only compares against 1 namespace, in future compare against others */ +- /* CHEN TODO */ +- ns = s->namespaces->personal; +- while (ns) { + if (ns->full_name) + len = strlen (ns->full_name); + d ("find_full: comparing namespace '%s' to name '%s'\n", ns->full_name, full); +@@ -390,7 +408,6 @@ camel_imapx_store_summary_namespace_find + || (strncmp (ns->full_name, full, len) == 0 + && (full[len] == ns->sep || full[len] == 0))) + break; +- ns = NULL; + } + + /* have a default? */ diff --git a/SOURCES/evolution-data-server-3.8.5-imapx-check-cancelled-jobs.patch b/SOURCES/evolution-data-server-3.8.5-imapx-check-cancelled-jobs.patch new file mode 100644 index 0000000..f628b53 --- /dev/null +++ b/SOURCES/evolution-data-server-3.8.5-imapx-check-cancelled-jobs.patch @@ -0,0 +1,221 @@ +diff -up evolution-data-server-3.8.5/camel/camel-imapx-command.c.imapx-check-cancelled-jobs evolution-data-server-3.8.5/camel/camel-imapx-command.c +--- evolution-data-server-3.8.5/camel/camel-imapx-command.c.imapx-check-cancelled-jobs 2013-08-02 16:57:28.000000000 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-command.c 2014-05-13 14:18:26.234981690 +0200 +@@ -579,8 +579,11 @@ camel_imapx_command_done (CamelIMAPXComm + + gboolean + camel_imapx_command_set_error_if_failed (CamelIMAPXCommand *ic, ++ GCancellable *cancellable, + GError **error) + { ++ CamelIMAPXJob *job; ++ + g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic), FALSE); + + if (ic->status != NULL && ic->status->result != IMAPX_OK) { +@@ -607,7 +610,11 @@ camel_imapx_command_set_error_if_failed + return TRUE; + } + +- return FALSE; ++ job = camel_imapx_command_get_job (ic); ++ ++ return (job && camel_imapx_job_propagate_error (job, error)) || ++ g_cancellable_set_error_if_cancelled (cancellable, error) || ++ (job && g_cancellable_set_error_if_cancelled (camel_imapx_job_get_cancellable (job), error)); + } + + CamelIMAPXCommandQueue * +diff -up evolution-data-server-3.8.5/camel/camel-imapx-command.h.imapx-check-cancelled-jobs evolution-data-server-3.8.5/camel/camel-imapx-command.h +--- evolution-data-server-3.8.5/camel/camel-imapx-command.h.imapx-check-cancelled-jobs 2013-07-23 13:57:42.000000000 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-command.h 2014-05-13 14:18:26.234981690 +0200 +@@ -122,6 +122,7 @@ void camel_imapx_command_wait (CamelIMA + void camel_imapx_command_done (CamelIMAPXCommand *ic); + gboolean camel_imapx_command_set_error_if_failed + (CamelIMAPXCommand *ic, ++ GCancellable *cancellable, + GError **error); + + /* These are simple GQueue wrappers for CamelIMAPXCommands. +diff -up evolution-data-server-3.8.5/camel/camel-imapx-server.c.imapx-check-cancelled-jobs evolution-data-server-3.8.5/camel/camel-imapx-server.c +--- evolution-data-server-3.8.5/camel/camel-imapx-server.c.imapx-check-cancelled-jobs 2014-05-13 14:18:26.230981690 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-server.c 2014-05-13 14:18:26.235981690 +0200 +@@ -3208,7 +3208,7 @@ imapx_command_run_sync (CamelIMAPXServer + if (cancel_id > 0) + g_cancellable_disconnect (cancellable, cancel_id); + +- if (camel_imapx_command_set_error_if_failed (ic, error)) ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) + return FALSE; + + return success; +@@ -3314,7 +3314,7 @@ imapx_command_idle_done (CamelIMAPXServe + job = camel_imapx_command_get_job (ic); + g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE); + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error performing IDLE")); +@@ -3690,7 +3690,7 @@ imapx_command_select_done (CamelIMAPXSer + gboolean success = TRUE; + GError *local_error = NULL; + +- if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, &local_error)) { + GQueue failed = G_QUEUE_INIT; + GQueue trash = G_QUEUE_INIT; + CamelFolder *folder; +@@ -4596,7 +4596,7 @@ imapx_command_fetch_message_done (CamelI + + job->commands--; + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error fetching message")); +@@ -4814,7 +4814,7 @@ imapx_command_copy_messages_step_done (C + uids = data->uids; + i = data->index; + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error copying messages")); +@@ -4983,7 +4983,7 @@ imapx_command_append_message_done (Camel + mi = camel_message_info_clone (data->info); + old_uid = g_strdup (data->info->uid); + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error appending message")); +@@ -5182,7 +5182,7 @@ imapx_command_step_fetch_done (CamelIMAP + i = data->index; + + //printf ("%s: Mobile mode: %d Fetch Count %d\n", camel_folder_get_display_name (folder), mobile_mode, batch_count); +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error fetching message headers")); +@@ -5333,12 +5333,15 @@ imapx_job_scan_changes_done (CamelIMAPXS + mobile_mode = camel_imapx_settings_get_mobile_mode (settings); + g_object_unref (settings); + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error retrieving message")); + success = FALSE; + ++ /*if (!ic->status || ic->status->result == IMAPX_OK) { ++ printf ("%s: might do something when the job for '%s' was cancelled/ended with error, but did not\n", __FUNCTION__, camel_folder_get_full_name (folder)); ++ }*/ + } else { + GCompareDataFunc uid_cmp = imapx_uid_cmp; + CamelMessageInfo *s_minfo = NULL; +@@ -5581,7 +5584,7 @@ imapx_command_fetch_new_messages_done (C + ifolder = CAMEL_IMAPX_FOLDER (folder); + isum = CAMEL_IMAPX_SUMMARY (folder->summary); + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error fetching new messages")); +@@ -6124,7 +6127,7 @@ imapx_command_expunge_done (CamelIMAPXSe + folder = camel_imapx_job_ref_folder (job); + g_return_val_if_fail (folder != NULL, FALSE); + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error expunging message")); +@@ -6235,7 +6238,7 @@ imapx_command_list_done (CamelIMAPXServe + job = camel_imapx_command_get_job (ic); + g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE); + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error fetching folders")); +@@ -6317,7 +6320,7 @@ imapx_command_subscription_done (CamelIM + job = camel_imapx_command_get_job (ic); + g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE); + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error subscribing to folder")); +@@ -6382,7 +6385,7 @@ imapx_command_create_folder_done (CamelI + job = camel_imapx_command_get_job (ic); + g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE); + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error creating folder")); +@@ -6436,7 +6439,7 @@ imapx_command_delete_folder_done (CamelI + job = camel_imapx_command_get_job (ic); + g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE); + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error deleting folder")); +@@ -6510,7 +6513,7 @@ imapx_command_rename_folder_done (CamelI + job = camel_imapx_command_get_job (ic); + g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE); + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error renaming folder")); +@@ -6585,7 +6588,7 @@ imapx_command_update_quota_info_done (Ca + job = camel_imapx_command_get_job (ic); + g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE); + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error retrieving quota information")); +@@ -6652,7 +6655,7 @@ imapx_command_uid_search_done (CamelIMAP + data = camel_imapx_job_get_data (job); + g_return_val_if_fail (data != NULL, FALSE); + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error (error, "%s: ", _("Search failed")); + success = FALSE; + } +@@ -6713,7 +6716,7 @@ imapx_command_noop_done (CamelIMAPXServe + job = camel_imapx_command_get_job (ic); + g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE); + +- if (camel_imapx_command_set_error_if_failed (ic, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error performing NOOP")); +@@ -6822,7 +6825,7 @@ imapx_command_sync_changes_done (CamelIM + * 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, error)) { ++ if (camel_imapx_command_set_error_if_failed (ic, cancellable, error)) { + g_prefix_error ( + error, "%s: ", + _("Error syncing changes")); diff --git a/SOURCES/evolution-data-server-3.8.5-imapx-conn-manager-ext.patch b/SOURCES/evolution-data-server-3.8.5-imapx-conn-manager-ext.patch new file mode 100644 index 0000000..f2fcd02 --- /dev/null +++ b/SOURCES/evolution-data-server-3.8.5-imapx-conn-manager-ext.patch @@ -0,0 +1,3284 @@ +diff -up evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.c.imapx-conn-manager-ext 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-conn-manager-ext 2013-07-23 13:57:46.000000000 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.c 2014-05-13 14:17:43.115983665 +0200 +@@ -47,6 +47,7 @@ struct _CamelIMAPXConnManagerPrivate { + GList *connections; + GWeakRef store; + GRWLock rw_lock; ++ guint limit_max_connections; + }; + + struct _ConnectionInfo { +@@ -54,6 +55,7 @@ struct _ConnectionInfo { + CamelIMAPXServer *is; + GHashTable *folder_names; + gchar *selected_folder; ++ GError *shutdown_error; + volatile gint ref_count; + }; + +@@ -68,7 +70,9 @@ G_DEFINE_TYPE ( + CAMEL_TYPE_OBJECT) + + static void +-imapx_conn_shutdown (CamelIMAPXServer *is, CamelIMAPXConnManager *con_man); ++imapx_conn_shutdown (CamelIMAPXServer *is, ++ const GError *error, ++ CamelIMAPXConnManager *con_man); + + static void + imapx_conn_update_select (CamelIMAPXServer *is, +@@ -91,6 +95,7 @@ connection_info_new (CamelIMAPXServer *i + 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; +@@ -114,11 +119,15 @@ 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_connect (cinfo->is, NULL, NULL); ++ 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_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); + } +@@ -132,6 +141,7 @@ connection_info_cancel_and_unref (Connec + + 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); ++ camel_imapx_server_shutdown (cinfo->is, cinfo->shutdown_error); + g_cancellable_cancel (cinfo->is->cancellable); + connection_info_unref (cinfo); + } +@@ -145,8 +155,9 @@ connection_info_is_available (Connection + + g_mutex_lock (&cinfo->lock); + +- /* Available means it's not tracking any folder names. */ +- available = (g_hash_table_size (cinfo->folder_names) == 0); ++ /* 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; + + g_mutex_unlock (&cinfo->lock); + +@@ -234,6 +245,23 @@ connection_info_set_selected_folder (Con + g_mutex_unlock (&cinfo->lock); + } + ++static void ++connection_info_set_shutdown_error (ConnectionInfo *cinfo, ++ const GError *shutdown_error) ++{ ++ g_return_if_fail (cinfo != NULL); ++ ++ g_mutex_lock (&cinfo->lock); ++ ++ if (cinfo->shutdown_error != shutdown_error) { ++ g_clear_error (&cinfo->shutdown_error); ++ if (shutdown_error) ++ cinfo->shutdown_error = g_error_copy (shutdown_error); ++ } ++ ++ g_mutex_unlock (&cinfo->lock); ++} ++ + static GList * + imapx_conn_manager_list_info (CamelIMAPXConnManager *con_man) + { +@@ -416,11 +444,9 @@ camel_imapx_conn_manager_init (CamelIMAP + g_rw_lock_init (&con_man->priv->rw_lock); + } + +-/* Static functions go here */ +- +-/* TODO destroy unused connections in a time-out loop */ + static void + imapx_conn_shutdown (CamelIMAPXServer *is, ++ const GError *error, + CamelIMAPXConnManager *con_man) + { + ConnectionInfo *cinfo; +@@ -432,6 +458,14 @@ imapx_conn_shutdown (CamelIMAPXServer *i + 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); ++ } + } + + static void +@@ -451,14 +485,10 @@ imapx_conn_update_select (CamelIMAPXServ + old_selected_folder = connection_info_dup_selected_folder (cinfo); + + if (old_selected_folder != NULL) { +- IMAPXJobQueueInfo *jinfo; +- +- jinfo = camel_imapx_server_get_job_queue_info (is); +- if (!g_hash_table_lookup (jinfo->folders, old_selected_folder)) { ++ 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); + } +- camel_imapx_destroy_job_queue_info (jinfo); + + g_free (old_selected_folder); + } +@@ -471,14 +501,15 @@ imapx_conn_update_select (CamelIMAPXServ + /* 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) ++ const gchar *folder_name, ++ gboolean for_expensive_job) + { + CamelStore *store; + CamelSettings *settings; + CamelIMAPXServer *is = NULL; + ConnectionInfo *cinfo = NULL; + GList *list, *link; +- guint concurrent_connections; ++ guint concurrent_connections, opened_connections, expensive_connections = 0; + guint min_jobs = G_MAXUINT; + + /* Caller must be holding CON_WRITE_LOCK. */ +@@ -492,24 +523,85 @@ imapx_find_connection_unlocked (CamelIMA + 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; ++ + g_object_unref (settings); + + /* XXX Have a dedicated connection for INBOX ? */ + ++ opened_connections = g_list_length (con_man->priv->connections); + list = con_man->priv->connections; + + /* If a folder was not given, find the least-busy connection. */ +- if (folder_name == NULL) ++ if (folder_name == NULL) { + goto least_busy; ++ } + + /* First try to find a connection already handling this folder. */ + for (link = list; link != NULL; link = g_list_next (link)) { + ConnectionInfo *candidate = link->data; + +- if (connection_info_has_folder_name (candidate, folder_name)) { ++ if (camel_imapx_server_has_expensive_command (candidate->is)) ++ expensive_connections++; ++ ++ if (connection_info_has_folder_name (candidate, folder_name) && ++ (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; ++ ++ if (!for_expensive_job && camel_imapx_server_get_command_count (cinfo->is) < camel_imapx_server_get_command_count (candidate->is)) ++ continue; ++ ++ connection_info_unref (cinfo); ++ } ++ + cinfo = connection_info_ref (candidate); ++ if (for_expensive_job && camel_imapx_server_has_expensive_command (cinfo->is)) ++ goto exit; ++ } ++ } ++ ++ 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; ++ ++ /* cinfo here doesn't have any expensive command, thus ignore it */ ++ if (cinfo) { ++ connection_info_unref (cinfo); ++ cinfo = NULL; + } ++ ++ /* 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; ++ ++ if (!camel_imapx_server_has_expensive_command (candidate->is)) ++ continue; ++ ++ jobs = camel_imapx_server_get_command_count (candidate->is); ++ ++ 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; ++ } ++ } ++ ++ if (cinfo) ++ goto exit; + } + + /* Next try to find a connection not handling any folders. */ +@@ -517,44 +609,59 @@ imapx_find_connection_unlocked (CamelIMA + ConnectionInfo *candidate = link->data; + + if (connection_info_is_available (candidate)) { ++ if (cinfo) ++ connection_info_unref (cinfo); + cinfo = connection_info_ref (candidate); + goto exit; + } + } + +-least_busy: ++ /* 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); ++ } ++ + /* 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; +- IMAPXJobQueueInfo *jinfo = NULL; +- +- jinfo = camel_imapx_server_get_job_queue_info (candidate->is); ++ gint n_commands = camel_imapx_server_get_command_count (candidate->is); + + if (cinfo == NULL) { + cinfo = connection_info_ref (candidate); +- min_jobs = jinfo->queue_len; ++ min_jobs = n_commands; + +- } else if (jinfo->queue_len < min_jobs) { ++ } else if (n_commands < min_jobs) { + connection_info_unref (cinfo); + cinfo = connection_info_ref (candidate); +- min_jobs = jinfo->queue_len; ++ min_jobs = n_commands; + } +- +- camel_imapx_destroy_job_queue_info (jinfo); + } + + exit: + if (cinfo != NULL && folder_name != NULL) + connection_info_insert_folder_name (cinfo, folder_name); + ++ 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; ++ ++ 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); ++ } ++ } ++ + if (cinfo != NULL) { + is = g_object_ref (cinfo->is); + connection_info_unref (cinfo); + } + +- if (camel_debug_flag (conman)) +- g_assert (!(concurrent_connections == g_list_length (con_man->priv->connections) && is == NULL)); +- + g_object_unref (store); + + return is; +@@ -601,9 +708,11 @@ imapx_create_new_connection_unlocked (Ca + * authenticate at once, so this should be thread-safe. + */ + imapx_store->authenticating_server = g_object_ref (is); ++ camel_imapx_store_set_authenticating_concurrent_connection (imapx_store, con_man->priv->connections != NULL); + success = camel_imapx_server_connect (is, cancellable, error); + g_object_unref (imapx_store->authenticating_server); + imapx_store->authenticating_server = NULL; ++ camel_imapx_store_set_authenticating_concurrent_connection (imapx_store, FALSE); + + if (!success) { + g_clear_object (&is); +@@ -626,7 +735,7 @@ imapx_create_new_connection_unlocked (Ca + con_man->priv->connections = g_list_prepend ( + con_man->priv->connections, cinfo); + +- c (is->tagprefix, "Created new connection for %s and total connections %d \n", folder_name, g_list_length (con_man->priv->connections)); ++ 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)); + + exit: + g_object_unref (store); +@@ -656,6 +765,7 @@ camel_imapx_conn_manager_ref_store (Came + CamelIMAPXServer * + camel_imapx_conn_manager_get_connection (CamelIMAPXConnManager *con_man, + const gchar *folder_name, ++ gboolean for_expensive_job, + GCancellable *cancellable, + GError **error) + { +@@ -669,10 +779,36 @@ camel_imapx_conn_manager_get_connection + + /* Check if we 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); +- if (is == NULL) +- is = imapx_create_new_connection_unlocked ( +- con_man, folder_name, 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); ++ } ++ } ++ } + } + + CON_WRITE_UNLOCK (con_man); +@@ -706,7 +842,6 @@ camel_imapx_conn_manager_update_con_info + const gchar *folder_name) + { + ConnectionInfo *cinfo; +- IMAPXJobQueueInfo *jinfo; + + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man)); + +@@ -716,28 +851,35 @@ camel_imapx_conn_manager_update_con_info + if (cinfo == NULL) + return; + +- jinfo = camel_imapx_server_get_job_queue_info (cinfo->is); +- if (!g_hash_table_lookup (jinfo->folders, folder_name)) { ++ 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); + } +- camel_imapx_destroy_job_queue_info (jinfo); + + connection_info_unref (cinfo); + } + + void +-camel_imapx_conn_manager_close_connections (CamelIMAPXConnManager *con_man) ++camel_imapx_conn_manager_close_connections (CamelIMAPXConnManager *con_man, ++ const GError *error) + { ++ GList *iter, *connections; ++ + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man)); + + CON_WRITE_LOCK (con_man); + +- g_list_free_full ( +- con_man->priv->connections, +- (GDestroyNotify) connection_info_cancel_and_unref); ++ c('*', "Closing all %d connections, with propagated error: %s\n", g_list_length (con_man->priv->connections), error ? error->message : "none"); ++ ++ connections = con_man->priv->connections; + con_man->priv->connections = NULL; + + CON_WRITE_UNLOCK (con_man); ++ ++ for (iter = connections; iter; iter = g_list_next (iter)) { ++ connection_info_set_shutdown_error (iter->data, error); ++ } ++ ++ g_list_free_full (connections, (GDestroyNotify) connection_info_cancel_and_unref); + } + +diff -up evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.h.imapx-conn-manager-ext 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-conn-manager-ext 2013-07-23 13:57:56.000000000 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-conn-manager.h 2014-05-13 14:17:43.116983665 +0200 +@@ -71,10 +71,12 @@ 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); ++ (CamelIMAPXConnManager *con_man, ++ const GError *error); + GList * camel_imapx_conn_manager_get_connections + (CamelIMAPXConnManager *con_man); + void camel_imapx_conn_manager_update_con_info +diff -up evolution-data-server-3.8.5/camel/camel-imapx-folder.c.imapx-conn-manager-ext evolution-data-server-3.8.5/camel/camel-imapx-folder.c +--- evolution-data-server-3.8.5/camel/camel-imapx-folder.c.imapx-conn-manager-ext 2013-07-23 14:01:51.000000000 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-folder.c 2014-05-13 14:17:43.116983665 +0200 +@@ -303,47 +303,27 @@ imapx_search_by_uids (CamelFolder *folde + { + CamelIMAPXFolder *ifolder; + CamelIMAPXSearch *isearch; +- CamelIMAPXServer *server = NULL; +- CamelStore *parent_store; + GPtrArray *matches; +- const gchar *folder_name; +- gboolean online; + + if (uids->len == 0) + return g_ptr_array_new (); + + ifolder = CAMEL_IMAPX_FOLDER (folder); +- folder_name = camel_folder_get_full_name (folder); +- parent_store = camel_folder_get_parent_store (folder); +- +- online = camel_offline_store_get_online ( +- CAMEL_OFFLINE_STORE (parent_store)); +- +- if (online) { +- /* do not panic when the server cannot be reached for whatever reason, +- * show offline data at least */ +- server = camel_imapx_store_get_server ( +- CAMEL_IMAPX_STORE (parent_store), +- folder_name, cancellable, NULL); +- } + + g_mutex_lock (&ifolder->search_lock); + + isearch = CAMEL_IMAPX_SEARCH (ifolder->search); +- camel_imapx_search_set_server (isearch, server); + + camel_folder_search_set_folder (ifolder->search, folder); ++ camel_imapx_search_set_cancellable_and_error (isearch, cancellable, error); + + matches = camel_folder_search_search ( + ifolder->search, expression, uids, cancellable, error); + +- camel_imapx_search_set_server (isearch, NULL); ++ camel_imapx_search_set_cancellable_and_error (isearch, NULL, NULL); + + g_mutex_unlock (&ifolder->search_lock); + +- if (server != NULL) +- g_object_unref (server); +- + return matches; + } + +@@ -355,44 +335,24 @@ imapx_count_by_expression (CamelFolder * + { + CamelIMAPXFolder *ifolder; + CamelIMAPXSearch *isearch; +- CamelIMAPXServer *server = NULL; +- CamelStore *parent_store; +- const gchar *folder_name; +- gboolean online; + guint32 matches; + + ifolder = CAMEL_IMAPX_FOLDER (folder); +- folder_name = camel_folder_get_full_name (folder); +- parent_store = camel_folder_get_parent_store (folder); +- +- online = camel_offline_store_get_online ( +- CAMEL_OFFLINE_STORE (parent_store)); +- +- if (online) { +- /* do not panic when the server cannot be reached for whatever reason, +- * show offline data at least */ +- server = camel_imapx_store_get_server ( +- CAMEL_IMAPX_STORE (parent_store), +- folder_name, cancellable, NULL); +- } + + g_mutex_lock (&ifolder->search_lock); + + isearch = CAMEL_IMAPX_SEARCH (ifolder->search); +- camel_imapx_search_set_server (isearch, server); + + camel_folder_search_set_folder (ifolder->search, folder); ++ camel_imapx_search_set_cancellable_and_error (isearch, cancellable, error); + + matches = camel_folder_search_count ( + ifolder->search, expression, cancellable, error); + +- camel_imapx_search_set_server (isearch, NULL); ++ camel_imapx_search_set_cancellable_and_error (isearch, NULL, NULL); + + g_mutex_unlock (&ifolder->search_lock); + +- if (server != NULL) +- g_object_unref (server); +- + return matches; + } + +@@ -404,44 +364,24 @@ imapx_search_by_expression (CamelFolder + { + CamelIMAPXFolder *ifolder; + CamelIMAPXSearch *isearch; +- CamelIMAPXServer *server = NULL; +- CamelStore *parent_store; + GPtrArray *matches; +- const gchar *folder_name; +- gboolean online; + + ifolder = CAMEL_IMAPX_FOLDER (folder); +- folder_name = camel_folder_get_full_name (folder); +- parent_store = camel_folder_get_parent_store (folder); +- +- online = camel_offline_store_get_online ( +- CAMEL_OFFLINE_STORE (parent_store)); +- +- if (online) { +- /* do not panic when the server cannot be reached for whatever reason, +- * show offline data at least */ +- server = camel_imapx_store_get_server ( +- CAMEL_IMAPX_STORE (parent_store), +- folder_name, cancellable, NULL); +- } + + g_mutex_lock (&ifolder->search_lock); + + isearch = CAMEL_IMAPX_SEARCH (ifolder->search); +- camel_imapx_search_set_server (isearch, server); + + camel_folder_search_set_folder (ifolder->search, folder); ++ camel_imapx_search_set_cancellable_and_error (isearch, cancellable, error); + + matches = camel_folder_search_search ( + ifolder->search, expression, NULL, cancellable, error); + +- camel_imapx_search_set_server (isearch, NULL); ++ camel_imapx_search_set_cancellable_and_error (isearch, NULL, NULL); + + g_mutex_unlock (&ifolder->search_lock); + +- if (server != NULL) +- g_object_unref (server); +- + return matches; + } + +@@ -466,8 +406,11 @@ imapx_append_message_sync (CamelFolder * + CamelStore *parent_store; + CamelIMAPXStore *istore; + CamelIMAPXServer *server; ++ const gchar *folder_name; ++ GError *local_error = NULL; + gboolean success = FALSE; + ++ folder_name = camel_folder_get_full_name (folder); + parent_store = camel_folder_get_parent_store (folder); + istore = CAMEL_IMAPX_STORE (parent_store); + +@@ -482,11 +425,27 @@ imapx_append_message_sync (CamelFolder * + if (appended_uid) + *appended_uid = NULL; + +- server = camel_imapx_store_get_server (istore, NULL, cancellable, error); ++ server = camel_imapx_store_get_server (istore, folder_name, FALSE, cancellable, error); + if (server) { + success = camel_imapx_server_append_message ( +- server, folder, message, info, appended_uid, cancellable, error); ++ server, folder, message, info, appended_uid, cancellable, &local_error); ++ camel_imapx_store_op_done (istore, server, folder_name); + g_object_unref (server); ++ ++ while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ g_clear_error (&local_error); ++ ++ server = camel_imapx_store_get_server (istore, folder_name, FALSE, cancellable, &local_error); ++ if (server) { ++ success = camel_imapx_server_append_message ( ++ server, folder, message, info, appended_uid, cancellable, &local_error); ++ camel_imapx_store_op_done (istore, server, folder_name); ++ g_object_unref (server); ++ } ++ } ++ ++ if (local_error) ++ g_propagate_error (error, local_error); + } + + return success; +@@ -501,6 +460,7 @@ imapx_expunge_sync (CamelFolder *folder, + CamelIMAPXStore *istore; + CamelIMAPXServer *server; + const gchar *folder_name; ++ GError *local_error = NULL; + gboolean success = FALSE; + + folder_name = camel_folder_get_full_name (folder); +@@ -515,13 +475,25 @@ imapx_expunge_sync (CamelFolder *folder, + return FALSE; + } + +- server = camel_imapx_store_get_server ( +- istore, folder_name, cancellable, error); ++ server = camel_imapx_store_get_server (istore, folder_name, FALSE, cancellable, error); + if (server != NULL) { +- success = camel_imapx_server_expunge ( +- server, folder, cancellable, error); ++ success = camel_imapx_server_expunge (server, folder, cancellable, &local_error); + camel_imapx_store_op_done (istore, server, folder_name); + g_object_unref (server); ++ ++ while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ g_clear_error (&local_error); ++ ++ server = camel_imapx_store_get_server (istore, folder_name, FALSE, cancellable, &local_error); ++ if (server != NULL) { ++ success = camel_imapx_server_expunge (server, folder, cancellable, &local_error); ++ camel_imapx_store_op_done (istore, server, folder_name); ++ g_object_unref (server); ++ } ++ } ++ ++ if (local_error) ++ g_propagate_error (error, local_error); + } + + return success; +@@ -539,6 +511,7 @@ imapx_fetch_messages_sync (CamelFolder * + CamelIMAPXStore *istore; + CamelIMAPXServer *server; + const gchar *folder_name; ++ GError *local_error = NULL; + gboolean success = FALSE; + + folder_name = camel_folder_get_full_name (folder); +@@ -557,19 +530,66 @@ imapx_fetch_messages_sync (CamelFolder * + if (!camel_service_connect_sync (service, cancellable, error)) + return FALSE; + +- server = camel_imapx_store_get_server ( +- istore, folder_name, cancellable, error); ++ server = camel_imapx_store_get_server (istore, folder_name, TRUE, cancellable, error); + if (server != NULL) { +- success = camel_imapx_server_fetch_messages ( +- server, folder, type, limit, cancellable, error); ++ success = camel_imapx_server_fetch_messages (server, folder, type, limit, cancellable, &local_error); + camel_imapx_store_op_done (istore, server, folder_name); + g_object_unref (server); ++ ++ while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ g_clear_error (&local_error); ++ ++ server = camel_imapx_store_get_server (istore, folder_name, TRUE, cancellable, &local_error); ++ if (server != NULL) { ++ success = camel_imapx_server_fetch_messages (server, folder, type, limit, cancellable, &local_error); ++ camel_imapx_store_op_done (istore, server, folder_name); ++ g_object_unref (server); ++ } ++ } ++ ++ if (local_error) ++ g_propagate_error (error, local_error); ++ + } + + return success; + } + + static CamelMimeMessage * ++imapx_get_message_cached (CamelFolder *folder, ++ const gchar *message_uid, ++ GCancellable *cancellable) ++{ ++ CamelIMAPXFolder *imapx_folder; ++ CamelMimeMessage *msg = NULL; ++ CamelStream *stream = NULL; ++ ++ g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), NULL); ++ g_return_val_if_fail (message_uid != NULL, NULL); ++ ++ imapx_folder = CAMEL_IMAPX_FOLDER (folder); ++ ++ stream = camel_data_cache_get (imapx_folder->cache, "cur", message_uid, NULL); ++ if (stream != NULL) { ++ gboolean success; ++ ++ msg = camel_mime_message_new (); ++ ++ g_mutex_lock (&imapx_folder->stream_lock); ++ success = camel_data_wrapper_construct_from_stream_sync ( ++ CAMEL_DATA_WRAPPER (msg), stream, cancellable, NULL); ++ if (!success) { ++ g_object_unref (msg); ++ msg = NULL; ++ } ++ g_mutex_unlock (&imapx_folder->stream_lock); ++ g_object_unref (stream); ++ } ++ ++ return msg; ++} ++ ++static CamelMimeMessage * + imapx_get_message_sync (CamelFolder *folder, + const gchar *uid, + GCancellable *cancellable, +@@ -583,6 +603,7 @@ imapx_get_message_sync (CamelFolder *fol + CamelIMAPXServer *server; + const gchar *folder_name; + const gchar *path = NULL; ++ GError *local_error = NULL; + gboolean offline_message = FALSE; + + folder_name = camel_folder_get_full_name (folder); +@@ -615,14 +636,27 @@ imapx_get_message_sync (CamelFolder *fol + } + + server = camel_imapx_store_get_server ( +- istore, folder_name, cancellable, error); ++ istore, folder_name, FALSE, cancellable, error); + if (server == NULL) + return NULL; + +- stream = camel_imapx_server_get_message ( +- server, folder, uid, cancellable, error); ++ stream = camel_imapx_server_get_message (server, folder, uid, cancellable, &local_error); + camel_imapx_store_op_done (istore, server, folder_name); + g_object_unref (server); ++ ++ while (!stream && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ g_clear_error (&local_error); ++ ++ server = camel_imapx_store_get_server (istore, folder_name, FALSE, cancellable, &local_error); ++ if (server) { ++ stream = camel_imapx_server_get_message (server, folder, uid, cancellable, &local_error); ++ camel_imapx_store_op_done (istore, server, folder_name); ++ g_object_unref (server); ++ } ++ } ++ ++ if (local_error) ++ g_propagate_error (error, local_error); + } + + if (stream != NULL) { +@@ -675,6 +709,7 @@ imapx_get_quota_info_sync (CamelFolder * + CamelFolderQuotaInfo *quota_info = NULL; + const gchar *folder_name; + gchar **quota_root_names; ++ GError *local_error = NULL; + gboolean success = FALSE; + + folder_name = camel_folder_get_full_name (folder); +@@ -682,14 +717,29 @@ imapx_get_quota_info_sync (CamelFolder * + + server = camel_imapx_store_get_server ( + CAMEL_IMAPX_STORE (parent_store), +- folder_name, cancellable, error); ++ folder_name, FALSE, cancellable, error); + + if (server != NULL) { +- success = camel_imapx_server_update_quota_info ( +- server, folder_name, cancellable, error); ++ success = camel_imapx_server_update_quota_info (server, folder_name, cancellable, &local_error); ++ camel_imapx_store_op_done (CAMEL_IMAPX_STORE (parent_store), server, folder_name); + g_object_unref (server); ++ ++ while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ g_clear_error (&local_error); ++ ++ server = camel_imapx_store_get_server (CAMEL_IMAPX_STORE (parent_store), folder_name, FALSE, cancellable, &local_error); ++ if (server) { ++ success = camel_imapx_server_update_quota_info (server, folder_name, cancellable, &local_error); ++ camel_imapx_store_op_done (CAMEL_IMAPX_STORE (parent_store), server, folder_name); ++ g_object_unref (server); ++ } ++ } ++ ++ if (local_error) ++ g_propagate_error (error, local_error); + } + ++ + if (!success) + return NULL; + +@@ -734,6 +784,7 @@ imapx_refresh_info_sync (CamelFolder *fo + CamelIMAPXStore *istore; + CamelIMAPXServer *server; + const gchar *folder_name; ++ GError *local_error = NULL; + gboolean success = FALSE; + + folder_name = camel_folder_get_full_name (folder); +@@ -752,13 +803,25 @@ imapx_refresh_info_sync (CamelFolder *fo + if (!camel_service_connect_sync (service, cancellable, error)) + return FALSE; + +- server = camel_imapx_store_get_server ( +- istore, folder_name, cancellable, error); ++ server = camel_imapx_store_get_server (istore, folder_name, TRUE, cancellable, error); + if (server != NULL) { +- success = camel_imapx_server_refresh_info ( +- server, folder, cancellable, error); ++ success = camel_imapx_server_refresh_info (server, folder, cancellable, &local_error); + camel_imapx_store_op_done (istore, server, folder_name); + g_object_unref (server); ++ ++ while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ g_clear_error (&local_error); ++ ++ server = camel_imapx_store_get_server (istore, folder_name, TRUE, cancellable, &local_error); ++ if (server != NULL) { ++ success = camel_imapx_server_refresh_info (server, folder, cancellable, &local_error); ++ camel_imapx_store_op_done (istore, server, folder_name); ++ g_object_unref (server); ++ } ++ } ++ ++ if (local_error) ++ g_propagate_error (error, local_error); + } + + return success; +@@ -920,6 +983,7 @@ imapx_synchronize_sync (CamelFolder *fol + CamelIMAPXStore *istore; + CamelIMAPXServer *server; + const gchar *folder_name; ++ GError *local_error = NULL; + gboolean success = FALSE; + + folder_name = camel_folder_get_full_name (folder); +@@ -934,13 +998,25 @@ imapx_synchronize_sync (CamelFolder *fol + return FALSE; + } + ++ /* while it can be expensive job, do not treat it as such, to avoid a blockage ++ by really expensive jobs */ + server = camel_imapx_store_get_server ( +- istore, folder_name, cancellable, error); ++ istore, folder_name, FALSE, cancellable, error); + if (server != NULL) { + gboolean need_to_expunge; + +- success = camel_imapx_server_sync_changes ( +- server, folder, cancellable, error); ++ success = camel_imapx_server_sync_changes (server, folder, cancellable, &local_error); ++ while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ camel_imapx_store_op_done (istore, server, folder_name); ++ ++ g_clear_error (&local_error); ++ g_clear_object (&server); ++ ++ server = camel_imapx_store_get_server (istore, folder_name, FALSE, cancellable, &local_error); ++ if (server) { ++ success = camel_imapx_server_sync_changes (server, folder, cancellable, &local_error); ++ } ++ } + + if (success) { + success = imapx_move_to_real_junk ( +@@ -959,12 +1035,29 @@ imapx_synchronize_sync (CamelFolder *fol + /* Sync twice - make sure deleted flags are written out, + * then sync again incase expunge changed anything */ + +- if (success && expunge) +- success = camel_imapx_server_expunge ( +- server, folder, cancellable, error); ++ if (success && expunge) { ++ success = camel_imapx_server_expunge (server, folder, cancellable, &local_error); + +- camel_imapx_store_op_done (istore, server, folder_name); +- g_object_unref (server); ++ while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ camel_imapx_store_op_done (istore, server, folder_name); ++ ++ g_clear_error (&local_error); ++ g_clear_object (&server); ++ ++ server = camel_imapx_store_get_server (istore, folder_name, FALSE, cancellable, &local_error); ++ if (server) { ++ success = camel_imapx_server_expunge (server, folder, cancellable, &local_error); ++ } ++ } ++ } ++ ++ if (local_error) ++ g_propagate_error (error, local_error); ++ ++ if (server) { ++ camel_imapx_store_op_done (istore, server, folder_name); ++ g_object_unref (server); ++ } + } + + return success; +@@ -980,6 +1073,7 @@ imapx_synchronize_message_sync (CamelFol + CamelIMAPXStore *istore; + CamelIMAPXServer *server; + const gchar *folder_name; ++ GError *local_error = NULL; + gboolean success = FALSE; + + folder_name = camel_folder_get_full_name (folder); +@@ -995,14 +1089,27 @@ imapx_synchronize_message_sync (CamelFol + } + + server = camel_imapx_store_get_server ( +- istore, folder_name, cancellable, error); ++ istore, folder_name, FALSE, cancellable, error); + if (server != NULL) { +- success = camel_imapx_server_sync_message ( +- server, folder, uid, cancellable, error); ++ success = camel_imapx_server_sync_message (server, folder, uid, cancellable, &local_error); + camel_imapx_store_op_done (istore, server, folder_name); + g_object_unref (server); ++ ++ while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ g_clear_error (&local_error); ++ ++ server = camel_imapx_store_get_server (istore, folder_name, FALSE, cancellable, &local_error); ++ if (server != NULL) { ++ success = camel_imapx_server_sync_message (server, folder, uid, cancellable, &local_error); ++ camel_imapx_store_op_done (istore, server, folder_name); ++ g_object_unref (server); ++ } ++ } + } + ++ if (local_error) ++ g_propagate_error (error, local_error); ++ + return success; + } + +@@ -1019,6 +1126,7 @@ imapx_transfer_messages_to_sync (CamelFo + CamelIMAPXStore *istore; + CamelIMAPXServer *server; + const gchar *folder_name; ++ GError *local_error = NULL; + gboolean success = FALSE; + + folder_name = camel_folder_get_full_name (source); +@@ -1034,15 +1142,31 @@ imapx_transfer_messages_to_sync (CamelFo + } + + server = camel_imapx_store_get_server ( +- istore, folder_name, cancellable, error); ++ istore, folder_name, FALSE, cancellable, error); + if (server != NULL) { + success = camel_imapx_server_copy_message ( + server, source, dest, uids, +- delete_originals, cancellable, error); ++ delete_originals, cancellable, &local_error); + camel_imapx_store_op_done (istore, server, folder_name); + g_object_unref (server); ++ ++ while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ g_clear_error (&local_error); ++ ++ server = camel_imapx_store_get_server (istore, folder_name, FALSE, cancellable, &local_error); ++ if (server != NULL) { ++ success = camel_imapx_server_copy_message ( ++ server, source, dest, uids, ++ delete_originals, cancellable, &local_error); ++ camel_imapx_store_op_done (istore, server, folder_name); ++ g_object_unref (server); ++ } ++ } + } + ++ if (local_error) ++ g_propagate_error (error, local_error); ++ + /* update destination folder only if not frozen, to not update + * for each single message transfer during filtering + */ +@@ -1102,6 +1226,7 @@ camel_imapx_folder_class_init (CamelIMAP + folder_class->append_message_sync = imapx_append_message_sync; + folder_class->expunge_sync = imapx_expunge_sync; + folder_class->fetch_messages_sync = imapx_fetch_messages_sync; ++ folder_class->get_message_cached = imapx_get_message_cached; + folder_class->get_message_sync = imapx_get_message_sync; + folder_class->get_quota_info_sync = imapx_get_quota_info_sync; + folder_class->purge_message_cache_sync = imapx_purge_message_cache_sync; +@@ -1238,12 +1363,17 @@ 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 (ifolder->cache, -1); ++ camel_data_cache_set_expire_access (ifolder->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)); + +- ifolder->search = camel_imapx_search_new (); ++ ifolder->search = camel_imapx_search_new (CAMEL_IMAPX_STORE (store)); + g_mutex_init (&ifolder->search_lock); + g_mutex_init (&ifolder->stream_lock); + ifolder->ignore_recent = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL); +diff -up evolution-data-server-3.8.5/camel/camel-imapx-search.c.imapx-conn-manager-ext evolution-data-server-3.8.5/camel/camel-imapx-search.c +--- evolution-data-server-3.8.5/camel/camel-imapx-search.c.imapx-conn-manager-ext 2014-05-13 14:17:43.103983666 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-search.c 2014-05-13 14:17:43.116983665 +0200 +@@ -16,6 +16,7 @@ + * + */ + ++#include "camel-imapx-store.h" + #include "camel-imapx-search.h" + + #include "camel-offline-store.h" +@@ -26,13 +27,16 @@ + ((obj), CAMEL_TYPE_IMAPX_SEARCH, CamelIMAPXSearchPrivate)) + + struct _CamelIMAPXSearchPrivate { +- GWeakRef server; ++ GWeakRef imapx_store; + gint *local_data_search; /* not NULL, if testing whether all used headers are all locally available */ ++ ++ GCancellable *cancellable; /* not referenced */ ++ GError **error; /* not referenced */ + }; + + enum { + PROP_0, +- PROP_SERVER ++ PROP_STORE + }; + + G_DEFINE_TYPE ( +@@ -47,8 +51,8 @@ imapx_search_set_property (GObject *obje + GParamSpec *pspec) + { + switch (property_id) { +- case PROP_SERVER: +- camel_imapx_search_set_server ( ++ case PROP_STORE: ++ camel_imapx_search_set_store ( + CAMEL_IMAPX_SEARCH (object), + g_value_get_object (value)); + return; +@@ -64,10 +68,10 @@ imapx_search_get_property (GObject *obje + GParamSpec *pspec) + { + switch (property_id) { +- case PROP_SERVER: ++ case PROP_STORE: + g_value_take_object ( + value, +- camel_imapx_search_ref_server ( ++ camel_imapx_search_ref_store ( + CAMEL_IMAPX_SEARCH (object))); + return; + } +@@ -82,7 +86,7 @@ imapx_search_dispose (GObject *object) + + priv = CAMEL_IMAPX_SEARCH_GET_PRIVATE (object); + +- g_weak_ref_set (&priv->server, NULL); ++ g_weak_ref_set (&priv->imapx_store, NULL); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (camel_imapx_search_parent_class)->dispose (object); +@@ -136,31 +140,51 @@ imapx_search_result_match_none (CamelSEx + static CamelSExpResult * + imapx_search_process_criteria (CamelSExp *sexp, + CamelFolderSearch *search, +- CamelIMAPXServer *server, ++ CamelIMAPXStore *imapx_store, + const GString *criteria, + const gchar *from_function) + { + CamelSExpResult *result; ++ CamelIMAPXSearch *imapx_search = CAMEL_IMAPX_SEARCH (search); + GPtrArray *uids = NULL; +- GError *error = NULL; ++ GError *local_error = NULL; ++ CamelIMAPXServer *imapx_server; ++ const gchar *folder_name; ++ ++ /* 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_get_server (imapx_store, folder_name, TRUE, imapx_search->priv->cancellable, &local_error); ++ if (imapx_server) { ++ uids = camel_imapx_server_uid_search (imapx_server, search->folder, criteria->str, imapx_search->priv->cancellable, &local_error); ++ camel_imapx_store_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_get_server (imapx_store, folder_name, TRUE, imapx_search->priv->cancellable, &local_error); ++ if (imapx_server) { ++ uids = camel_imapx_server_uid_search (imapx_server, search->folder, criteria->str, imapx_search->priv->cancellable, &local_error); ++ camel_imapx_store_op_done (imapx_store, imapx_server, folder_name); ++ } ++ } ++ } + +- uids = camel_imapx_server_uid_search ( +- server, search->folder, criteria->str, NULL, &error); ++ g_clear_object (&imapx_server); + + /* Sanity check. */ + g_return_val_if_fail ( +- ((uids != NULL) && (error == NULL)) || +- ((uids == NULL) && (error != NULL)), NULL); ++ ((uids != NULL) && (local_error == NULL)) || ++ ((uids == NULL) && (local_error != NULL)), NULL); ++ ++ if (local_error != NULL) { ++ g_propagate_error (imapx_search->priv->error, local_error); + +- /* XXX No allowance for errors in CamelSExp callbacks! +- * Dump the error to the console and make like we +- * got an empty result. */ +- if (error != NULL) { +- g_warning ( +- "%s: (UID SEARCH %s): %s", +- from_function, criteria->str, error->message); ++ /* Make like we've got an empty result */ + uids = g_ptr_array_new (); +- g_error_free (error); + } + + if (search->current != NULL) { +@@ -183,7 +207,7 @@ imapx_search_match_all (CamelSExp *sexp, + CamelFolderSearch *search) + { + CamelIMAPXSearch *imapx_search = CAMEL_IMAPX_SEARCH (search); +- CamelIMAPXServer *server; ++ CamelIMAPXStore *imapx_store; + CamelSExpResult *result; + GPtrArray *summary; + gint local_data_search = 0, *prev_local_data_search, ii; +@@ -191,9 +215,9 @@ imapx_search_match_all (CamelSExp *sexp, + if (argc != 1) + return imapx_search_result_match_none (sexp, search); + +- server = camel_imapx_search_ref_server (CAMEL_IMAPX_SEARCH (search)); +- if (!server || search->current || !search->summary) { +- g_clear_object (&server); ++ imapx_store = camel_imapx_search_ref_store (CAMEL_IMAPX_SEARCH (search)); ++ if (!imapx_store || search->current || !search->summary) { ++ g_clear_object (&imapx_store); + + /* Chain up to parent's method. */ + return CAMEL_FOLDER_SEARCH_CLASS (camel_imapx_search_parent_class)-> +@@ -224,7 +248,7 @@ imapx_search_match_all (CamelSExp *sexp, + imapx_search->priv->local_data_search = prev_local_data_search; + + if (local_data_search >= 0) { +- g_clear_object (&server); ++ g_clear_object (&imapx_store); + + /* Chain up to parent's method. */ + return CAMEL_FOLDER_SEARCH_CLASS (camel_imapx_search_parent_class)-> +@@ -235,7 +259,7 @@ imapx_search_match_all (CamelSExp *sexp, + but here is expected GPtrArray of matched UIDs */ + result = camel_sexp_term_eval (sexp, argv[0]); + +- g_object_unref (server); ++ g_clear_object (&imapx_store); + + g_return_val_if_fail (result != NULL, result); + g_return_val_if_fail (result->type == CAMEL_SEXP_RES_ARRAY_PTR, result); +@@ -250,7 +274,7 @@ imapx_search_body_contains (CamelSExp *s + CamelFolderSearch *search) + { + CamelIMAPXSearch *imapx_search = CAMEL_IMAPX_SEARCH (search); +- CamelIMAPXServer *server; ++ CamelIMAPXStore *imapx_store; + CamelSExpResult *result; + GString *criteria; + gint ii, jj; +@@ -269,10 +293,10 @@ imapx_search_body_contains (CamelSExp *s + if (argc == 0 || search->summary->len == 0) + return imapx_search_result_match_none (sexp, search); + +- server = camel_imapx_search_ref_server (CAMEL_IMAPX_SEARCH (search)); ++ imapx_store = camel_imapx_search_ref_store (CAMEL_IMAPX_SEARCH (search)); + +- /* This will be NULL if we're offline. Search from cache. */ +- if (server == NULL) { ++ /* This will be NULL if we're offline. Search from cache. */ ++ if (imapx_store == NULL) { + /* Chain up to parent's method. */ + return CAMEL_FOLDER_SEARCH_CLASS (camel_imapx_search_parent_class)-> + body_contains (sexp, argc, argv, search); +@@ -320,10 +344,10 @@ imapx_search_body_contains (CamelSExp *s + } + } + +- result = imapx_search_process_criteria (sexp, search, server, criteria, G_STRFUNC); ++ result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, G_STRFUNC); + + g_string_free (criteria, TRUE); +- g_object_unref (server); ++ g_object_unref (imapx_store); + + return result; + } +@@ -344,7 +368,7 @@ imapx_search_header_contains (CamelSExp + CamelFolderSearch *search) + { + CamelIMAPXSearch *imapx_search = CAMEL_IMAPX_SEARCH (search); +- CamelIMAPXServer *server; ++ CamelIMAPXStore *imapx_store; + CamelSExpResult *result; + const gchar *headername, *command = NULL; + GString *criteria; +@@ -373,10 +397,10 @@ imapx_search_header_contains (CamelSExp + return imapx_search_result_match_none (sexp, search); + } + +- server = camel_imapx_search_ref_server (CAMEL_IMAPX_SEARCH (search)); ++ imapx_store = camel_imapx_search_ref_store (CAMEL_IMAPX_SEARCH (search)); + +- /* This will be NULL if we're offline. Search from cache. */ +- if (server == NULL) { ++ /* This will be NULL if we're offline. Search from cache. */ ++ if (imapx_store == NULL) { + /* Chain up to parent's method. */ + return CAMEL_FOLDER_SEARCH_CLASS (camel_imapx_search_parent_class)-> + header_contains (sexp, argc, argv, search); +@@ -440,10 +464,10 @@ imapx_search_header_contains (CamelSExp + } + } + +- result = imapx_search_process_criteria (sexp, search, server, criteria, G_STRFUNC); ++ result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, G_STRFUNC); + + g_string_free (criteria, TRUE); +- g_object_unref (server); ++ g_object_unref (imapx_store); + + return result; + } +@@ -455,7 +479,7 @@ imapx_search_header_exists (CamelSExp *s + CamelFolderSearch *search) + { + CamelIMAPXSearch *imapx_search = CAMEL_IMAPX_SEARCH (search); +- CamelIMAPXServer *server; ++ CamelIMAPXStore *imapx_store; + CamelSExpResult *result; + GString *criteria; + gint ii; +@@ -490,10 +514,10 @@ imapx_search_header_exists (CamelSExp *s + return imapx_search_result_match_none (sexp, search); + } + +- server = camel_imapx_search_ref_server (CAMEL_IMAPX_SEARCH (search)); ++ imapx_store = camel_imapx_search_ref_store (CAMEL_IMAPX_SEARCH (search)); + +- /* This will be NULL if we're offline. Search from cache. */ +- if (server == NULL) { ++ /* This will be NULL if we're offline. Search from cache. */ ++ if (imapx_store == NULL) { + /* Chain up to parent's method. */ + return CAMEL_FOLDER_SEARCH_CLASS (camel_imapx_search_parent_class)-> + header_exists (sexp, argc, argv, search); +@@ -525,10 +549,10 @@ imapx_search_header_exists (CamelSExp *s + g_string_append_printf (criteria, "HEADER \"%s\" \"\"", headername); + } + +- result = imapx_search_process_criteria (sexp, search, server, criteria, G_STRFUNC); ++ result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, G_STRFUNC); + + g_string_free (criteria, TRUE); +- g_object_unref (server); ++ g_object_unref (imapx_store); + + return result; + } +@@ -554,12 +578,12 @@ camel_imapx_search_class_init (CamelIMAP + + g_object_class_install_property ( + object_class, +- PROP_SERVER, ++ PROP_STORE, + g_param_spec_object ( +- "server", +- "Server", +- "Server proxy for server-side searches", +- CAMEL_TYPE_IMAPX_SERVER, ++ "store", ++ "IMAPX Store", ++ "IMAPX Store for server-side searches", ++ CAMEL_TYPE_IMAPX_STORE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + } +@@ -573,67 +597,105 @@ camel_imapx_search_init (CamelIMAPXSearc + + /** + * camel_imapx_search_new: ++ * imapx_store: a #CamelIMAPXStore to which the search belongs + * + * Returns a new #CamelIMAPXSearch instance. + * +- * The #CamelIMAPXSearch must be given a #CamelIMAPXSearch:server before +- * it can issue server-side search requests. Otherwise it will fallback +- * to the default #CamelFolderSearch behavior. +- * + * Returns: a new #CamelIMAPXSearch + * + * Since: 3.8 + **/ + CamelFolderSearch * +-camel_imapx_search_new (void) ++camel_imapx_search_new (CamelIMAPXStore *imapx_store) + { +- return g_object_new (CAMEL_TYPE_IMAPX_SEARCH, NULL); ++ g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), NULL); ++ ++ return g_object_new ( ++ CAMEL_TYPE_IMAPX_SEARCH, ++ "store", imapx_store, ++ NULL); + } + + /** +- * camel_imapx_search_ref_server: ++ * camel_imapx_search_ref_store: + * @search: a #CamelIMAPXSearch + * +- * Returns a #CamelIMAPXServer to use for server-side searches, +- * or %NULL when the corresponding #CamelIMAPXStore is offline. ++ * Returns a #CamelIMAPXStore to use for server-side searches, ++ * or %NULL when the store is offline. + * +- * The returned #CamelIMAPXSearch is referenced for thread-safety and ++ * The returned #CamelIMAPXStore is referenced for thread-safety and + * must be unreferenced with g_object_unref() when finished with it. + * +- * Returns: a #CamelIMAPXServer, or %NULL ++ * Returns: a #CamelIMAPXStore, or %NULL + * + * Since: 3.8 + **/ +-CamelIMAPXServer * +-camel_imapx_search_ref_server (CamelIMAPXSearch *search) ++CamelIMAPXStore * ++camel_imapx_search_ref_store (CamelIMAPXSearch *search) + { ++ CamelIMAPXStore *imapx_store; ++ + g_return_val_if_fail (CAMEL_IS_IMAPX_SEARCH (search), NULL); + +- return g_weak_ref_get (&search->priv->server); ++ imapx_store = g_weak_ref_get (&search->priv->imapx_store); ++ ++ if (imapx_store && !camel_offline_store_get_online (CAMEL_OFFLINE_STORE (imapx_store))) ++ g_clear_object (&imapx_store); ++ ++ return imapx_store; + } + + /** +- * camel_imapx_search_set_server: ++ * camel_imapx_search_set_store: + * @search: a #CamelIMAPXSearch +- * @server: a #CamelIMAPXServer, or %NULL ++ * @imapx_server: a #CamelIMAPXStore, or %NULL + * +- * Sets a #CamelIMAPXServer to use for server-side searches. Generally ++ * Sets a #CamelIMAPXStore to use for server-side searches. Generally + * this is set for the duration of a single search when online, and then + * reset to %NULL. + * + * Since: 3.8 + **/ + void +-camel_imapx_search_set_server (CamelIMAPXSearch *search, +- CamelIMAPXServer *server) ++camel_imapx_search_set_store (CamelIMAPXSearch *search, ++ CamelIMAPXStore *imapx_store) + { + g_return_if_fail (CAMEL_IS_IMAPX_SEARCH (search)); + +- if (server != NULL) +- g_return_if_fail (CAMEL_IS_IMAPX_SERVER (server)); ++ if (imapx_store != NULL) ++ g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store)); + +- g_weak_ref_set (&search->priv->server, server); ++ g_weak_ref_set (&search->priv->imapx_store, imapx_store); + +- g_object_notify (G_OBJECT (search), "server"); ++ g_object_notify (G_OBJECT (search), "store"); + } + ++/** ++ * camel_imapx_search_set_cancellable_and_error: ++ * @search: a #CamelIMAPXSearch ++ * @cancellable: a #GCancellable, or %NULL ++ * @error: a #GError, or %NULL ++ * ++ * Sets @cancellable and @error to use for server-side searches. This way ++ * the search can return accurate errors and be eventually cancelled by ++ * a user. ++ * ++ * Note: The caller is responsible to keep alive both @cancellable and @error ++ * for the whole run of the search and reset them both to NULL after ++ * the search is finished. ++ * ++ * Since: 3.14 ++ **/ ++void ++camel_imapx_search_set_cancellable_and_error (CamelIMAPXSearch *search, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_return_if_fail (CAMEL_IS_IMAPX_SEARCH (search)); ++ ++ if (cancellable) ++ g_return_if_fail (G_IS_CANCELLABLE (cancellable)); ++ ++ search->priv->cancellable = cancellable; ++ search->priv->error = error; ++} +diff -up evolution-data-server-3.8.5/camel/camel-imapx-search.h.imapx-conn-manager-ext evolution-data-server-3.8.5/camel/camel-imapx-search.h +--- evolution-data-server-3.8.5/camel/camel-imapx-search.h.imapx-conn-manager-ext 2013-07-23 13:57:45.000000000 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-search.h 2014-05-13 14:17:43.116983665 +0200 +@@ -24,7 +24,7 @@ + #define CAMEL_IMAPX_SEARCH_H + + #include +-#include ++#include + + /* Standard GObject macros */ + #define CAMEL_TYPE_IMAPX_SEARCH \ +@@ -47,6 +47,9 @@ + + G_BEGIN_DECLS + ++/* Avoid a circular reference. */ ++struct _CamelIMAPXStore; ++ + typedef struct _CamelIMAPXSearch CamelIMAPXSearch; + typedef struct _CamelIMAPXSearchClass CamelIMAPXSearchClass; + typedef struct _CamelIMAPXSearchPrivate CamelIMAPXSearchPrivate; +@@ -70,11 +73,15 @@ struct _CamelIMAPXSearchClass { + + GType camel_imapx_search_get_type (void) G_GNUC_CONST; + CamelFolderSearch * +- camel_imapx_search_new (void); +-CamelIMAPXServer * +- camel_imapx_search_ref_server (CamelIMAPXSearch *search); +-void camel_imapx_search_set_server (CamelIMAPXSearch *search, +- CamelIMAPXServer *server); ++ camel_imapx_search_new (struct _CamelIMAPXStore *imapx_store); ++struct _CamelIMAPXStore * ++ camel_imapx_search_ref_store (CamelIMAPXSearch *search); ++void camel_imapx_search_set_store (CamelIMAPXSearch *search, ++ struct _CamelIMAPXStore *imapx_store); ++void camel_imapx_search_set_cancellable_and_error ++ (CamelIMAPXSearch *search, ++ GCancellable *cancellable, ++ GError **error); + + G_END_DECLS + +diff -up evolution-data-server-3.8.5/camel/camel-imapx-server.c.imapx-conn-manager-ext evolution-data-server-3.8.5/camel/camel-imapx-server.c +--- evolution-data-server-3.8.5/camel/camel-imapx-server.c.imapx-conn-manager-ext 2014-05-13 14:17:43.110983665 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-server.c 2014-05-13 14:17:43.118983665 +0200 +@@ -73,6 +73,8 @@ + + #define MAX_COMMAND_LEN 1000 + ++G_DEFINE_QUARK (camel-imapx-server-error-quark, camel_imapx_server_error) ++ + extern gint camel_application_is_exiting; + + /* Job-specific structs */ +@@ -343,6 +345,14 @@ struct _CamelIMAPXServerPrivate { + + GHashTable *known_alerts; + GMutex known_alerts_lock; ++ ++ GMutex jobs_prop_lock; ++ GHashTable *jobs_prop_folder_paths; ++ gint jobs_prop_command_count; /* without IDLE command */ ++ gint jobs_prop_expensive_command_count; ++ ++ GMutex shutdown_error_lock; ++ GError *shutdown_error; + }; + + enum { +@@ -377,7 +387,6 @@ static gboolean imapx_continuation (Cam + gboolean litplus, + GCancellable *cancellable, + GError **error); +-static gboolean imapx_disconnect (CamelIMAPXServer *is); + static gboolean imapx_is_command_queue_empty (CamelIMAPXServer *is); + static gint imapx_uid_cmp (gconstpointer ap, + gconstpointer bp, +@@ -560,6 +569,159 @@ replace_untagged_descriptor (GHashTable + } + + 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, ++ const gchar *folder_path) ++{ ++ gint n_stored; ++ ++ g_return_if_fail (folder_path != NULL); ++ ++ g_mutex_lock (&imapx_server->priv->jobs_prop_lock); ++ ++ n_stored = GPOINTER_TO_INT (g_hash_table_lookup (imapx_server->priv->jobs_prop_folder_paths, folder_path)); ++ g_hash_table_insert (imapx_server->priv->jobs_prop_folder_paths, g_strdup (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, ++ const gchar *folder_path) ++{ ++ gint n_stored; ++ ++ g_return_if_fail (folder_path != NULL); ++ ++ g_mutex_lock (&imapx_server->priv->jobs_prop_lock); ++ ++ n_stored = GPOINTER_TO_INT (g_hash_table_lookup (imapx_server->priv->jobs_prop_folder_paths, folder_path)); ++ g_warn_if_fail (n_stored >= 1); ++ ++ n_stored--; ++ if (n_stored > 0) { ++ g_hash_table_insert (imapx_server->priv->jobs_prop_folder_paths, g_strdup (folder_path), GINT_TO_POINTER (n_stored)); ++ } else { ++ g_hash_table_remove (imapx_server->priv->jobs_prop_folder_paths, folder_path); ++ } ++ ++ g_mutex_unlock (&imapx_server->priv->jobs_prop_lock); ++} ++ ++static void ++imapx_server_job_added (CamelIMAPXServer *imapx_server, ++ CamelIMAPXJob *job) ++{ ++ CamelFolder *folder; ++ ++ g_return_if_fail (job != NULL); ++ ++ folder = camel_imapx_job_ref_folder (job); ++ ++ if (folder != NULL) { ++ imapx_server_add_job_mailbox (imapx_server, camel_folder_get_full_name (folder)); ++ g_object_unref (folder); ++ } ++} ++ ++static void ++imapx_server_job_removed (CamelIMAPXServer *imapx_server, ++ CamelIMAPXJob *job) ++{ ++ CamelFolder *folder; ++ ++ g_return_if_fail (job != NULL); ++ ++ folder = camel_imapx_job_ref_folder (job); ++ ++ if (folder != NULL) { ++ imapx_server_remove_job_mailbox (imapx_server, camel_folder_get_full_name (folder)); ++ g_object_unref (folder); ++ } ++} ++ ++static void + add_initial_untagged_descriptor (GHashTable *untagged_handlers, + guint untagged_id) + { +@@ -582,8 +744,8 @@ static GHashTable * + create_initial_untagged_handler_table (void) + { + GHashTable *uh = g_hash_table_new_full ( +- g_str_hash, +- g_str_equal, ++ camel_strcase_hash, ++ camel_strcase_equal, + g_free, + NULL); + guint32 ii = 0; +@@ -833,12 +995,14 @@ imapx_command_start (CamelIMAPXServer *i + { + CamelIMAPXStream *stream = NULL; + CamelIMAPXCommandPart *cp; ++ CamelIMAPXJob *job; + gboolean cp_continuation; + gboolean cp_literal_plus; + GList *head; + gboolean success = FALSE; + gchar *string; + gint retval; ++ GError *local_error = NULL; + + camel_imapx_command_close (ic); + +@@ -857,6 +1021,14 @@ imapx_command_start (CamelIMAPXServer *i + is->literal = ic; + + camel_imapx_command_queue_push_tail (is->active, ic); ++ 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)) { ++ camel_imapx_job_set_error (job, local_error); ++ g_clear_error (&local_error); ++ goto err; ++ } + + stream = camel_imapx_server_ref_stream (is); + +@@ -898,6 +1070,7 @@ imapx_command_start (CamelIMAPXServer *i + + err: + camel_imapx_command_queue_remove (is->active, ic); ++ imapx_server_command_removed (is, ic); + + /* HACK: Since we're failing, make sure the command has a status + * structure and the result code indicates failure, so the +@@ -1016,6 +1189,7 @@ imapx_command_start_next (CamelIMAPXServ + + 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); +@@ -1150,6 +1324,7 @@ imapx_command_start_next (CamelIMAPXServ + + 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); +@@ -1223,6 +1398,7 @@ imapx_command_start_next (CamelIMAPXServ + + 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); +@@ -1279,7 +1455,7 @@ imapx_command_queue (CamelIMAPXServer *i + if (is->state == IMAPX_SHUTDOWN) { + c (is->tagprefix, "refuse to queue job on disconnected server\n"); + g_set_error ( +- error, CAMEL_IMAPX_ERROR, 1, ++ error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT, + "%s", _("Server disconnected")); + + QUEUE_UNLOCK (is); +@@ -1294,6 +1470,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); + +@@ -1376,38 +1553,34 @@ imapx_match_active_job (CamelIMAPXServer + return match; + } + ++/* Do *not* call this when the queue_lock is held, it can cause ++ deadlock when searching between multiple servers */ + static CamelIMAPXJob * +-imapx_is_job_in_queue (CamelIMAPXServer *is, +- CamelFolder *folder, +- guint32 type, +- const gchar *uid) ++imapx_server_ref_job (CamelIMAPXServer *imapx_server, ++ CamelFolder *folder, ++ guint32 job_type, ++ const gchar *uid) + { +- GList *head, *link; +- CamelIMAPXJob *job = NULL; +- gboolean found = FALSE; +- +- QUEUE_LOCK (is); ++ CamelIMAPXStore *imapx_store; ++ CamelIMAPXJob *job; + +- head = g_queue_peek_head_link (&is->jobs); ++ g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), NULL); + +- for (link = head; link != NULL; link = g_list_next (link)) { +- job = (CamelIMAPXJob *) link->data; ++ /* first try its own queue */ ++ job = camel_imapx_server_ref_job (imapx_server, folder, job_type, uid); ++ if (job) ++ return job; + +- if (!job || !(job->type & type)) +- continue; ++ /* then try queue for all the opened servers */ ++ imapx_store = camel_imapx_server_ref_store (imapx_server); ++ if (!imapx_store) ++ return NULL; + +- if (camel_imapx_job_matches (job, folder, uid)) { +- found = TRUE; +- break; +- } +- } ++ job = camel_imapx_store_ref_job (imapx_store, folder, job_type, uid); + +- QUEUE_UNLOCK (is); ++ g_object_unref (imapx_store); + +- if (found) +- return job; +- else +- return NULL; ++ return job; + } + + static void +@@ -1867,8 +2040,8 @@ imapx_untagged_fetch (CamelIMAPXServer * + is->changes = camel_folder_change_info_new (); + + camel_folder_change_info_change_uid (is->changes, uid); +- g_free (uid); + } ++ g_free (uid); + + if (imapx_idle_supported (is) && changed && imapx_in_idle (is)) { + camel_folder_summary_save_to_db ( +@@ -2313,9 +2486,6 @@ imapx_untagged_bye (CamelIMAPXServer *is + GCancellable *cancellable, + GError **error) + { +- CamelIMAPXStore *imapx_store; +- CamelService *service; +- CamelServiceConnectionStatus status; + guchar *token = NULL; + + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); +@@ -2325,7 +2495,7 @@ imapx_untagged_bye (CamelIMAPXServer *is + if (camel_imapx_stream_text (stream, &token, cancellable, NULL)) { + c (is->tagprefix, "BYE: %s\n", token); + g_set_error ( +- error, CAMEL_IMAPX_ERROR, 1, ++ error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT, + "IMAP server said BYE: %s", token); + } + +@@ -2333,19 +2503,6 @@ imapx_untagged_bye (CamelIMAPXServer *is + + is->state = IMAPX_SHUTDOWN; + +- imapx_store = camel_imapx_server_ref_store (is); +- service = CAMEL_SERVICE (imapx_store); +- status = camel_service_get_connection_status (service); +- +- /* Do not disconnect the service if we're still connecting. +- * camel_service_disconnect_sync() will cancel the connect +- * operation and the server message will get replaced with +- * a generic "Operation was cancelled" message. */ +- if (status == CAMEL_SERVICE_CONNECTED) +- camel_service_disconnect_sync (service, FALSE, NULL, NULL); +- +- g_object_unref (imapx_store); +- + return FALSE; + } + +@@ -2399,10 +2556,16 @@ imapx_untagged_ok_no_bad (CamelIMAPXServ + select_folder = g_weak_ref_get (&is->select_folder); + select_pending = g_weak_ref_get (&is->select_pending); + +- if (select_folder == NULL) ++ if (select_folder) ++ imapx_server_remove_job_mailbox (is, camel_folder_get_full_name (select_folder)); ++ ++ if (select_folder == NULL) { + g_weak_ref_set ( + &is->select_folder, + select_pending); ++ if (select_pending) ++ imapx_server_add_job_mailbox (is, camel_folder_get_full_name (select_pending)); ++ } + + g_clear_object (&select_folder); + g_clear_object (&select_pending); +@@ -2849,6 +3012,7 @@ imapx_completion (CamelIMAPXServer *is, + + camel_imapx_command_ref (ic); + camel_imapx_command_queue_remove (is->active, ic); ++ imapx_server_command_removed (is, ic); + camel_imapx_command_queue_push_tail (is->done, ic); + camel_imapx_command_unref (ic); + +@@ -3058,10 +3222,18 @@ imapx_register_job (CamelIMAPXServer *is + 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, "NO connection yet, maybe user cancelled jobs earlier ?"); ++ e (is->tagprefix, "Not connected yet, maybe user cancelled jobs earlier?"); + g_set_error ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_NOT_CONNECTED, +@@ -3080,8 +3252,10 @@ imapx_unregister_job (CamelIMAPXServer * + camel_imapx_job_done (job); + + QUEUE_LOCK (is); +- if (g_queue_remove (&is->jobs, job)) ++ if (g_queue_remove (&is->jobs, job)) { ++ imapx_server_job_removed (is, job); + camel_imapx_job_unref (job); ++ } + QUEUE_UNLOCK (is); + } + +@@ -3152,6 +3326,7 @@ imapx_command_idle_done (CamelIMAPXServe + IDLE_UNLOCK (idle); + + imapx_unregister_job (is, job); ++ + camel_imapx_command_unref (ic); + + return success; +@@ -3347,8 +3522,11 @@ imapx_idle_thread (gpointer data) + break; + } + ++ IDLE_LOCK (is->idle); + g_clear_error (&local_error); + is->idle->idle_thread = NULL; ++ IDLE_UNLOCK (is->idle); ++ + return NULL; + } + +@@ -3521,6 +3699,11 @@ imapx_command_select_done (CamelIMAPXSer + c (is->tagprefix, "Select failed\n"); + + g_mutex_lock (&is->select_lock); ++ folder = g_weak_ref_get (&is->select_folder); ++ if (folder) { ++ imapx_server_remove_job_mailbox (is, camel_folder_get_full_name (folder)); ++ g_object_unref (folder); ++ } + folder = g_weak_ref_get (&is->select_pending); + g_weak_ref_set (&is->select_folder, NULL); + g_weak_ref_set (&is->select_pending, NULL); +@@ -3548,7 +3731,9 @@ imapx_command_select_done (CamelIMAPXSer + + 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); + g_queue_push_tail (&failed, cw); + } + +@@ -3563,6 +3748,7 @@ imapx_command_select_done (CamelIMAPXSer + + if (!CAMEL_IS_IMAPX_JOB (job)) { + g_warn_if_reached (); ++ camel_imapx_command_unref (cw); + continue; + } + +@@ -3572,6 +3758,7 @@ imapx_command_select_done (CamelIMAPXSer + cw->status = imapx_copy_status (ic->status); + + cw->complete (is, cw, NULL, NULL); ++ camel_imapx_command_unref (cw); + } + + g_propagate_error (error, local_error); +@@ -3601,22 +3788,16 @@ imapx_command_select_done (CamelIMAPXSer + /* 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_is_job_in_queue ( ++ CamelIMAPXJob *job = imapx_server_ref_job ( + is, folder, IMAPX_JOB_REFRESH_INFO, NULL); + if (job) { +- RefreshInfoData *data = camel_imapx_job_get_data (job); +- +- if (data->scan_changes) { +- c (is->tagprefix, "Will not fetch_new_messages when already in scan_changes\n"); +- goto no_fetch_new; +- } ++ camel_imapx_job_unref (job); ++ c ( ++ is->tagprefix, ++ "Will not fetch_new_messages when already refreshing information\n"); ++ } else { ++ imapx_server_fetch_new_messages (is, folder, TRUE, TRUE, NULL, NULL); + } +- imapx_server_fetch_new_messages (is, folder, TRUE, TRUE, NULL, NULL); +- /* We don't do this right now because we want the new messages to +- * update the unseen count. */ +- //ifolder->uidnext_on_server = is->uidnext; +- no_fetch_new: +- ; + } + ifolder->uidvalidity_on_server = is->uidvalidity; + selected_folder = camel_folder_get_full_name (folder); +@@ -3638,6 +3819,9 @@ imapx_command_select_done (CamelIMAPXSer + + camel_imapx_command_unref (ic); + ++ if (selected_folder) ++ imapx_server_add_job_mailbox (is, selected_folder); ++ + g_signal_emit (is, signals[SELECT_CHANGED], 0, selected_folder); + + return success; +@@ -4132,7 +4316,7 @@ camel_imapx_server_authenticate (CamelIM + + g_return_val_if_fail ( + CAMEL_IS_IMAPX_SERVER (is), +- CAMEL_AUTHENTICATION_REJECTED); ++ CAMEL_AUTHENTICATION_ERROR); + + store = camel_imapx_server_ref_store (is); + +@@ -4202,8 +4386,27 @@ camel_imapx_server_authenticate (CamelIM + result = CAMEL_AUTHENTICATION_ERROR; + else if (ic->status->result == IMAPX_OK) + result = CAMEL_AUTHENTICATION_ACCEPTED; +- else +- result = CAMEL_AUTHENTICATION_REJECTED; ++ else if (ic->status->result == IMAPX_NO) { ++ if (camel_imapx_store_get_authenticating_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) { +@@ -4348,7 +4551,7 @@ imapx_reconnect (CamelIMAPXServer *is, + + exception: + +- imapx_disconnect (is); ++ camel_imapx_server_disconnect (is); + + if (is->cinfo) { + imapx_free_capability (is->cinfo); +@@ -4457,6 +4660,13 @@ imapx_command_fetch_message_done (CamelI + _("Failed to close the tmp stream")); + } + ++ if (success && g_cancellable_set_error_if_cancelled (cancellable, error)) { ++ success = FALSE; ++ g_prefix_error ( ++ error, "%s: ", ++ _("Error fetching message")); ++ } ++ + if (success) { + gchar *cur_filename; + gchar *tmp_filename; +@@ -5488,7 +5698,6 @@ imapx_job_fetch_new_messages_start (Came + _("Fetching summary information for new messages in '%s'"), + camel_folder_get_display_name (folder)); + +- //printf ("Fetch order: %d/%d\n", fetch_order, CAMEL_SORT_DESCENDING); + if (diff > uidset_size || fetch_order == CAMEL_SORT_DESCENDING) { + ic = camel_imapx_command_new ( + is, "FETCH", folder, +@@ -6837,7 +7046,7 @@ imapx_job_sync_changes_matches (CamelIMA + /* we cancel all the commands and their jobs, so associated jobs will be notified */ + static void + cancel_all_jobs (CamelIMAPXServer *is, +- GError *error) ++ const GError *error) + { + CamelIMAPXCommandQueue *queue; + GList *head, *link; +@@ -6847,11 +7056,21 @@ cancel_all_jobs (CamelIMAPXServer *is, + + queue = camel_imapx_command_queue_new (); + ++ imapx_server_set_shutdown_error (is, error); ++ + QUEUE_LOCK (is); + + camel_imapx_command_queue_transfer (is->queue, queue); + camel_imapx_command_queue_transfer (is->active, queue); + ++ head = camel_imapx_command_queue_peek_head_link (queue); ++ for (link = head; link != NULL; link = g_list_next (link)) { ++ CamelIMAPXCommand *ic = link->data; ++ ++ if (ic) ++ imapx_server_command_removed (is, ic); ++ } ++ + QUEUE_UNLOCK (is); + + head = camel_imapx_command_queue_peek_head_link (queue); +@@ -6871,7 +7090,14 @@ cancel_all_jobs (CamelIMAPXServer *is, + if (!CAMEL_IS_IMAPX_JOB (job)) + continue; + +- camel_imapx_job_cancel (job); ++ if (error) { ++ /* Insert an error into the CamelIMAPXCommand to be ++ * propagated when the completion callback function ++ * calls camel_imapx_command_set_error_if_failed(). */ ++ camel_imapx_job_set_error (job, error); ++ } else { ++ camel_imapx_job_cancel (job); ++ } + + /* Send a NULL GError since we already cancelled + * the job and we're not interested in individual +@@ -6916,6 +7142,7 @@ imapx_parser_thread (gpointer d) + GCancellable *cancellable; + gboolean have_stream; + GError *local_error = NULL; ++ GError *shutdown_error; + + QUEUE_LOCK (is); + /* Do not use CamelOperation here, because it can be cancelled at +@@ -6991,8 +7218,28 @@ imapx_parser_thread (gpointer d) + } + + /* Jump out of the loop if an error occurred. */ +- if (local_error != NULL) ++ 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)) { ++ 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); ++ + break; ++ } + + stream = camel_imapx_server_ref_stream (is); + if (stream != NULL) { +@@ -7020,7 +7267,12 @@ imapx_parser_thread (gpointer d) + QUEUE_UNLOCK (is); + + is->parser_quit = FALSE; +- g_signal_emit (is, signals[SHUTDOWN], 0); ++ ++ shutdown_error = imapx_server_dup_shutdown_error (is); ++ ++ g_signal_emit (is, signals[SHUTDOWN], 0, shutdown_error); ++ ++ g_clear_error (&shutdown_error); + + g_object_unref (is); + +@@ -7103,7 +7355,7 @@ imapx_server_dispose (GObject *object) + if (server->cinfo && imapx_idle_supported (server)) + imapx_exit_idle (server); + +- imapx_disconnect (server); ++ camel_imapx_server_disconnect (server); + + g_weak_ref_set (&server->priv->store, NULL); + +@@ -7141,6 +7393,12 @@ imapx_server_finalize (GObject *object) + g_hash_table_destroy (is->priv->known_alerts); + g_mutex_clear (&is->priv->known_alerts_lock); + ++ g_mutex_clear (&is->priv->jobs_prop_lock); ++ g_hash_table_destroy (is->priv->jobs_prop_folder_paths); ++ ++ g_mutex_clear (&is->priv->shutdown_error_lock); ++ g_clear_error (&is->priv->shutdown_error); ++ + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (camel_imapx_server_parent_class)->finalize (object); + } +@@ -7216,6 +7474,7 @@ camel_imapx_server_class_init (CamelIMAP + /** + * 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", +@@ -7223,8 +7482,8 @@ camel_imapx_server_class_init (CamelIMAP + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (CamelIMAPXServerClass, shutdown), + NULL, NULL, +- g_cclosure_marshal_VOID__VOID, +- G_TYPE_NONE, 0); ++ g_cclosure_marshal_VOID__BOXED, ++ G_TYPE_NONE, 1, G_TYPE_ERROR); + + class->tagprefix = 'A'; + } +@@ -7239,6 +7498,12 @@ camel_imapx_server_init (CamelIMAPXServe + g_mutex_init (&is->priv->stream_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); ++ ++ 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; + + is->queue = camel_imapx_command_queue_new (); + is->active = camel_imapx_command_queue_new (); +@@ -7259,6 +7524,8 @@ camel_imapx_server_init (CamelIMAPXServe + is->changes = camel_folder_change_info_new (); + is->parser_quit = FALSE; + ++ is->priv->shutdown_error = NULL; ++ + is->priv->known_alerts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + } + +@@ -7312,11 +7579,23 @@ camel_imapx_server_ref_stream (CamelIMAP + return stream; + } + +-static gboolean +-imapx_disconnect (CamelIMAPXServer *is) ++gboolean ++camel_imapx_server_disconnect (CamelIMAPXServer *is) + { + gboolean ret = TRUE; + ++ /*QUEUE_LOCK (is); ++ is->state = IMAPX_SHUTDOWN; ++ ++ is->parser_quit = TRUE; ++ ++ if (is->cancellable != NULL) { ++ g_cancellable_cancel (is->cancellable); ++ g_object_unref (is->cancellable); ++ is->cancellable = NULL; ++ } ++ QUEUE_UNLOCK (is);*/ ++ + g_mutex_lock (&is->priv->stream_lock); + + if (is->priv->stream != NULL) { +@@ -7387,18 +7666,15 @@ imapx_server_get_message (CamelIMAPXServ + gboolean registered; + gboolean success; + +- QUEUE_LOCK (is); +- +- if ((job = imapx_is_job_in_queue (is, folder, IMAPX_JOB_GET_MESSAGE, uid))) { ++ if ((job = imapx_server_ref_job (is, folder, IMAPX_JOB_GET_MESSAGE, uid))) { + /* Promote the existing GET_MESSAGE + * job's priority if ours is higher. */ + if (pri > job->pri) + job->pri = pri; + +- QUEUE_UNLOCK (is); +- + /* Wait for the job to finish. */ + camel_imapx_job_wait (job); ++ camel_imapx_job_unref (job); + + /* Disregard errors here. If we failed to retreive the + * message from cache (implying the job we were waiting +@@ -7407,10 +7683,10 @@ imapx_server_get_message (CamelIMAPXServ + ifolder->cache, "cur", uid, NULL); + if (stream != NULL) + return stream; +- +- QUEUE_LOCK (is); + } + ++ QUEUE_LOCK (is); ++ + mi = camel_folder_summary_get (folder->summary, uid); + if (!mi) { + g_set_error ( +@@ -7700,15 +7976,17 @@ camel_imapx_server_refresh_info (CamelIM + + full_name = camel_folder_get_full_name (folder); + +- QUEUE_LOCK (is); +- + /* Both RefreshInfo and Fetch messages can't operate simultaneously */ +- if (imapx_is_job_in_queue (is, folder, IMAPX_JOB_REFRESH_INFO, NULL) || +- imapx_is_job_in_queue (is, folder, IMAPX_JOB_FETCH_MESSAGES, NULL)) { +- QUEUE_UNLOCK (is); ++ job = imapx_server_ref_job (is, folder, IMAPX_JOB_REFRESH_INFO, NULL); ++ if (!job) ++ job = imapx_server_ref_job (is, folder, IMAPX_JOB_FETCH_MESSAGES, NULL); ++ if (job) { ++ camel_imapx_job_unref (job); + return TRUE; + } + ++ QUEUE_LOCK (is); ++ + data = g_slice_new0 (RefreshInfoData); + data->changes = camel_folder_change_info_new (); + +@@ -7963,13 +8241,11 @@ imapx_server_sync_changes (CamelIMAPXSer + + /* TODO above code should go into changes_start */ + +- QUEUE_LOCK (is); +- +- if ((job = imapx_is_job_in_queue (is, folder, IMAPX_JOB_SYNC_CHANGES, NULL))) { ++ if ((job = imapx_server_ref_job (is, folder, IMAPX_JOB_SYNC_CHANGES, NULL))) { + if (pri > job->pri) + job->pri = pri; + +- QUEUE_UNLOCK (is); ++ camel_imapx_job_unref (job); + + imapx_sync_free_user (on_user); + imapx_sync_free_user (off_user); +@@ -7977,6 +8253,8 @@ imapx_server_sync_changes (CamelIMAPXSer + return TRUE; + } + ++ QUEUE_LOCK (is); ++ + data = g_slice_new0 (SyncChangesData); + data->folder = g_object_ref (folder); + data->changed_uids = changed_uids; /* takes ownership */ +@@ -8036,13 +8314,15 @@ camel_imapx_server_expunge (CamelIMAPXSe + gboolean success; + + /* Do we really care to wait for this one to finish? */ +- QUEUE_LOCK (is); + +- if (imapx_is_job_in_queue (is, folder, IMAPX_JOB_EXPUNGE, NULL)) { +- QUEUE_UNLOCK (is); ++ job = imapx_server_ref_job (is, folder, IMAPX_JOB_EXPUNGE, NULL); ++ if (job) { ++ camel_imapx_job_unref (job); + return TRUE; + } + ++ QUEUE_LOCK (is); ++ + job = camel_imapx_job_new (cancellable); + job->type = IMAPX_JOB_EXPUNGE; + job->start = imapx_job_expunge_start; +@@ -8270,15 +8550,17 @@ camel_imapx_server_fetch_messages (Camel + firstuid = strtoull (uid, NULL, 10); + g_free (uid); + +- QUEUE_LOCK (is); +- + /* Both RefreshInfo and Fetch messages can't operate simultaneously */ +- if (imapx_is_job_in_queue (is, folder, IMAPX_JOB_REFRESH_INFO, NULL) || +- imapx_is_job_in_queue (is, folder, IMAPX_JOB_FETCH_MESSAGES, NULL)) { +- QUEUE_UNLOCK (is); ++ job = imapx_server_ref_job (is, folder, IMAPX_JOB_REFRESH_INFO, NULL); ++ if (!job) ++ job = imapx_server_ref_job (is, folder, IMAPX_JOB_FETCH_MESSAGES, NULL); ++ if (job) { ++ camel_imapx_job_unref (job); + return TRUE; + } + ++ QUEUE_LOCK (is); ++ + data = g_slice_new0 (RefreshInfoData); + data->changes = camel_folder_change_info_new (); + data->fetch_msg_limit = limit; +@@ -8450,55 +8732,54 @@ camel_imapx_server_uid_search (CamelIMAP + return results; + } + +-IMAPXJobQueueInfo * +-camel_imapx_server_get_job_queue_info (CamelIMAPXServer *is) ++gboolean ++camel_imapx_server_folder_name_in_jobs (CamelIMAPXServer *imapx_server, ++ const gchar *folder_path) + { +- IMAPXJobQueueInfo *jinfo = g_new0 (IMAPXJobQueueInfo, 1); +- CamelFolder *select_folder; +- CamelIMAPXJob *job = NULL; +- GList *head, *link; ++ gboolean res; + +- QUEUE_LOCK (is); ++ g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), FALSE); ++ g_return_val_if_fail (folder_path != NULL, FALSE); + +- jinfo->queue_len = g_queue_get_length (&is->jobs); +- jinfo->folders = g_hash_table_new_full ( +- (GHashFunc) g_str_hash, +- (GEqualFunc) g_str_equal, +- (GDestroyNotify) g_free, +- (GDestroyNotify) NULL); ++ g_mutex_lock (&imapx_server->priv->jobs_prop_lock); + +- head = g_queue_peek_head_link (&is->jobs); ++ res = GPOINTER_TO_INT (g_hash_table_lookup (imapx_server->priv->jobs_prop_folder_paths, folder_path)) > 0; + +- for (link = head; link != NULL; link = g_list_next (link)) { +- CamelFolder *folder; ++ g_mutex_unlock (&imapx_server->priv->jobs_prop_lock); + +- job = (CamelIMAPXJob *) link->data; +- folder = camel_imapx_job_ref_folder (job); ++ return res; ++} + +- if (folder != NULL) { +- gchar *folder_name; ++gboolean ++camel_imapx_server_has_expensive_command (CamelIMAPXServer *imapx_server) ++{ ++ gboolean res; + +- folder_name = camel_folder_dup_full_name (folder); +- g_hash_table_add (jinfo->folders, folder_name); ++ g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), FALSE); + +- g_object_unref (folder); +- } +- } ++ g_mutex_lock (&imapx_server->priv->jobs_prop_lock); + +- select_folder = g_weak_ref_get (&is->select_folder); ++ res = imapx_server->priv->jobs_prop_expensive_command_count > 0; + +- if (select_folder != NULL) { +- gchar *folder_name; ++ g_mutex_unlock (&imapx_server->priv->jobs_prop_lock); + +- folder_name = camel_folder_dup_full_name (select_folder); +- g_hash_table_add (jinfo->folders, folder_name); ++ return res; ++} + +- g_object_unref (select_folder); +- } ++gint ++camel_imapx_server_get_command_count (CamelIMAPXServer *imapx_server) ++{ ++ guint32 res; + +- QUEUE_UNLOCK (is); ++ g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), -1); ++ ++ g_mutex_lock (&imapx_server->priv->jobs_prop_lock); + +- return jinfo; ++ res = imapx_server->priv->jobs_prop_command_count; ++ ++ g_mutex_unlock (&imapx_server->priv->jobs_prop_lock); ++ ++ return res; + } + + /** +@@ -8575,3 +8856,108 @@ camel_imapx_server_command_run (CamelIMA + + return ok; + } ++ ++/** ++ * camel_imapx_server_is_job_in_queue: ++ * @imapx_server: a #CamelIMAPXServer instance ++ * @folder: a folder 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, ++ CamelFolder *folder, ++ guint32 job_type, ++ const gchar *uid) ++{ ++ 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, folder, uid)) { ++ found = TRUE; ++ camel_imapx_job_ref (job); ++ break; ++ } ++ } ++ ++ QUEUE_UNLOCK (imapx_server); ++ ++ return found ? job : NULL; ++} ++ ++/** ++ * 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) ++{ ++ GCancellable *cancellable; ++ GError *shutdown_error_copy = NULL; ++ ++ g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is)); ++ ++ QUEUE_LOCK (is); ++ ++ is->parser_quit = TRUE; ++ is->state = IMAPX_SHUTDOWN; ++ ++ if (is->cancellable) ++ cancellable = g_object_ref (is->cancellable); ++ else ++ cancellable = NULL; ++ ++ QUEUE_UNLOCK (is); ++ ++ if (!error) { ++ shutdown_error_copy = imapx_server_dup_shutdown_error (is); ++ error = shutdown_error_copy; ++ } ++ ++ if (error) { ++ cancel_all_jobs (is, error); ++ } else { ++ GError *local_error = NULL; ++ ++ g_set_error ( ++ &local_error, CAMEL_SERVICE_ERROR, ++ CAMEL_SERVICE_ERROR_UNAVAILABLE, ++ "Shutting down"); ++ ++ cancel_all_jobs (is, local_error); ++ ++ g_clear_error (&local_error); ++ } ++ ++ if (cancellable) ++ g_cancellable_cancel (cancellable); ++ g_clear_object (&cancellable); ++ g_clear_error (&shutdown_error_copy); ++} +diff -up evolution-data-server-3.8.5/camel/camel-imapx-server.h.imapx-conn-manager-ext evolution-data-server-3.8.5/camel/camel-imapx-server.h +--- evolution-data-server-3.8.5/camel/camel-imapx-server.h.imapx-conn-manager-ext 2013-07-23 13:57:54.000000000 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-server.h 2014-05-13 14:17:43.118983665 +0200 +@@ -53,18 +53,27 @@ + #define IMAPX_MODE_READ (1 << 0) + #define IMAPX_MODE_WRITE (1 << 1) + ++#define CAMEL_IMAPX_SERVER_ERROR (camel_imapx_server_error_quark ()) ++ + G_BEGIN_DECLS + ++typedef enum { ++ CAMEL_IMAPX_SERVER_ERROR_CONCURRENT_CONNECT_FAILED, ++ CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT ++} CamelIMAPXServerError; ++ ++GQuark camel_imapx_server_error_quark (void) G_GNUC_CONST; ++ + /* Avoid a circular reference. */ + struct _CamelIMAPXStore; + struct _CamelIMAPXSettings; ++struct _CamelIMAPXJob; + + typedef struct _CamelIMAPXServer CamelIMAPXServer; + typedef struct _CamelIMAPXServerClass CamelIMAPXServerClass; + typedef struct _CamelIMAPXServerPrivate CamelIMAPXServerPrivate; + + typedef struct _CamelIMAPXIdle CamelIMAPXIdle; +-struct _IMAPXJobQueueInfo; + + /* untagged response handling */ + typedef gboolean +@@ -167,7 +176,8 @@ struct _CamelIMAPXServerClass { + /* Signals */ + void (*select_changed) (CamelIMAPXServer *is, + const gchar *selected_folder); +- void (*shutdown) (CamelIMAPXServer *is); ++ void (*shutdown) (CamelIMAPXServer *is, ++ const GError *error); + + gchar tagprefix; + }; +@@ -184,6 +194,9 @@ CamelIMAPXStream * + gboolean camel_imapx_server_connect (CamelIMAPXServer *is, + GCancellable *cancellable, + GError **error); ++gboolean camel_imapx_server_disconnect (CamelIMAPXServer *is); ++void camel_imapx_server_shutdown (CamelIMAPXServer *is, ++ const GError *error); + gboolean imapx_connect_to_server (CamelIMAPXServer *is, + GCancellable *cancellable, + GError **error); +@@ -278,9 +291,13 @@ GPtrArray * camel_imapx_server_uid_searc + const gchar *criteria, + GCancellable *cancellable, + GError **error); +-struct _IMAPXJobQueueInfo * +- camel_imapx_server_get_job_queue_info +- (CamelIMAPXServer *is); ++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); + const CamelIMAPXUntaggedRespHandlerDesc * + camel_imapx_server_register_untagged_handler + (CamelIMAPXServer *is, +@@ -290,6 +307,11 @@ gboolean camel_imapx_server_command_run + CamelIMAPXCommand *ic, + GCancellable *cancellable, + GError **error); ++struct _CamelIMAPXJob * ++ camel_imapx_server_ref_job (CamelIMAPXServer *imapx_server, ++ CamelFolder *folder, ++ guint32 job_type, ++ const gchar *uid); + + G_END_DECLS + +diff -up evolution-data-server-3.8.5/camel/camel-imapx-settings.c.imapx-conn-manager-ext evolution-data-server-3.8.5/camel/camel-imapx-settings.c +--- evolution-data-server-3.8.5/camel/camel-imapx-settings.c.imapx-conn-manager-ext 2013-07-23 13:57:50.000000000 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-settings.c 2014-05-13 14:17:43.118983665 +0200 +@@ -516,7 +516,7 @@ camel_imapx_settings_class_init (CamelIM + "Number of concurrent IMAP connections to use", + MIN_CONCURRENT_CONNECTIONS, + MAX_CONCURRENT_CONNECTIONS, +- 5, ++ 3, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +diff -up evolution-data-server-3.8.5/camel/camel-imapx-store.c.imapx-conn-manager-ext evolution-data-server-3.8.5/camel/camel-imapx-store.c +--- evolution-data-server-3.8.5/camel/camel-imapx-store.c.imapx-conn-manager-ext 2014-05-13 14:17:43.114983665 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-store.c 2014-05-13 14:17:43.119983665 +0200 +@@ -40,6 +40,7 @@ + #include + + #include "camel-imapx-folder.h" ++#include "camel-imapx-job.h" + #include "camel-imapx-server.h" + #include "camel-imapx-settings.h" + #include "camel-imapx-store.h" +@@ -59,6 +60,7 @@ + struct _CamelIMAPXStorePrivate { + GHashTable *quota_info; + GMutex quota_info_lock; ++ gboolean is_concurrent_connection; + }; + + enum { +@@ -260,11 +262,12 @@ imapx_get_name (CamelService *service, + CamelIMAPXServer * + camel_imapx_store_get_server (CamelIMAPXStore *istore, + const gchar *folder_name, ++ gboolean for_expensive_job, + GCancellable *cancellable, + GError **error) + { + return camel_imapx_conn_manager_get_connection ( +- istore->con_man, folder_name, cancellable, error); ++ istore->con_man, folder_name, for_expensive_job, cancellable, error); + } + + void +@@ -286,7 +289,7 @@ imapx_connect_sync (CamelService *servic + CamelIMAPXStore *istore = (CamelIMAPXStore *) service; + CamelIMAPXServer *server; + +- server = camel_imapx_store_get_server (istore, NULL, cancellable, error); ++ server = camel_imapx_store_get_server (istore, NULL, FALSE, cancellable, error); + if (server) { + g_object_unref (server); + return TRUE; +@@ -309,7 +312,7 @@ imapx_disconnect_sync (CamelService *ser + res = service_class->disconnect_sync (service, clean, cancellable, error); + + if (istore->con_man != NULL) +- camel_imapx_conn_manager_close_connections (istore->con_man); ++ camel_imapx_conn_manager_close_connections (istore->con_man, NULL); + + return res; + } +@@ -333,7 +336,7 @@ imapx_authenticate_sync (CamelService *s + + g_return_val_if_fail ( + CAMEL_IS_IMAPX_SERVER (server), +- CAMEL_AUTHENTICATION_REJECTED); ++ CAMEL_AUTHENTICATION_ERROR); + + return camel_imapx_server_authenticate ( + server, mechanism, cancellable, error); +@@ -657,22 +660,35 @@ imapx_subscribe_folder (CamelStore *stor + { + CamelIMAPXStore *istore = (CamelIMAPXStore *) store; + CamelIMAPXServer *server; ++ GError *local_error = NULL; + gboolean success; + + if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) + return TRUE; + +- server = camel_imapx_store_get_server (istore, NULL, cancellable, error); ++ server = camel_imapx_store_get_server (istore, NULL, FALSE, cancellable, error); + if (!server) + return FALSE; + + if (folder_name && *folder_name == '/') + folder_name++; + +- success = camel_imapx_server_manage_subscription ( +- server, folder_name, TRUE, cancellable, error); ++ success = camel_imapx_server_manage_subscription (server, folder_name, TRUE, cancellable, &local_error); + g_object_unref (server); + ++ while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ g_clear_error (&local_error); ++ ++ server = camel_imapx_store_get_server (istore, NULL, FALSE, cancellable, &local_error); ++ if (server) { ++ success = camel_imapx_server_manage_subscription (server, folder_name, TRUE, cancellable, &local_error); ++ g_object_unref (server); ++ } ++ } ++ ++ if (local_error) ++ g_propagate_error (error, local_error); ++ + if (success) + imapx_mark_folder_subscribed (istore, folder_name, emit_signal); + +@@ -688,22 +704,35 @@ imapx_unsubscribe_folder (CamelStore *st + { + CamelIMAPXStore *istore = (CamelIMAPXStore *) store; + CamelIMAPXServer *server; ++ GError *local_error = NULL; + gboolean success; + + if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) + return TRUE; + +- server = camel_imapx_store_get_server (istore, NULL, cancellable, error); ++ server = camel_imapx_store_get_server (istore, NULL, FALSE, cancellable, error); + if (!server) + return FALSE; + + if (folder_name && *folder_name == '/') + folder_name++; + +- success = camel_imapx_server_manage_subscription ( +- server, folder_name, FALSE, cancellable, error); ++ success = camel_imapx_server_manage_subscription (server, folder_name, FALSE, cancellable, &local_error); + g_object_unref (server); + ++ while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ g_clear_error (&local_error); ++ ++ server = camel_imapx_store_get_server (istore, NULL, FALSE, cancellable, &local_error); ++ if (server) { ++ success = camel_imapx_server_manage_subscription (server, folder_name, FALSE, cancellable, &local_error); ++ g_object_unref (server); ++ } ++ } ++ ++ if (local_error) ++ g_propagate_error (error, local_error); ++ + if (success) + imapx_unmark_folder_subscribed (istore, folder_name, emit_signal); + +@@ -1012,9 +1041,23 @@ fetch_folders_for_pattern (CamelIMAPXSto + GPtrArray *folders; + GError *local_error = NULL; + ++ g_object_ref (server); ++ + folders = camel_imapx_server_list ( + server, pattern, flags, ext, cancellable, &local_error); +- if (folders == NULL || local_error) { ++ ++ while (!folders && 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_get_server (istore, NULL, FALSE, cancellable, &local_error); ++ if (server) ++ folders = camel_imapx_server_list (server, pattern, flags, ext, cancellable, &local_error); ++ } ++ ++ if (folders == NULL || local_error || !server) { ++ g_clear_object (&server); ++ + if (local_error) + g_propagate_error (error, local_error); + return FALSE; +@@ -1022,6 +1065,8 @@ fetch_folders_for_pattern (CamelIMAPXSto + + add_folders_to_summary (istore, server, folders, table, (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)); + ++ g_clear_object (&server); ++ + g_ptr_array_foreach (folders, free_list, folders); + g_ptr_array_free (folders, TRUE); + +@@ -1057,7 +1102,7 @@ fetch_folders_for_namespaces (CamelIMAPX + GHashTable *folders = NULL; + GList *namespaces = NULL, *l; + +- server = camel_imapx_store_get_server (istore, NULL, cancellable, error); ++ server = camel_imapx_store_get_server (istore, NULL, FALSE, cancellable, error); + if (!server) + return NULL; + +@@ -1581,6 +1626,7 @@ imapx_store_create_folder_sync (CamelSto + gchar *real_name, *full_name, *parent_real; + CamelFolderInfo *fi = NULL; + gchar dir_sep = 0; ++ GError *local_error = NULL; + gboolean success; + + if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) { +@@ -1591,7 +1637,7 @@ imapx_store_create_folder_sync (CamelSto + return NULL; + } + +- server = camel_imapx_store_get_server (istore, NULL, cancellable, error); ++ server = camel_imapx_store_get_server (istore, NULL, FALSE, cancellable, error); + if (!server) + return NULL; + +@@ -1642,10 +1688,22 @@ imapx_store_create_folder_sync (CamelSto + full_name = imapx_concat (istore, parent_real, real_name); + g_free (real_name); + +- success = camel_imapx_server_create_folder ( +- server, full_name, cancellable, error); ++ success = camel_imapx_server_create_folder (server, full_name, cancellable, &local_error); + g_object_unref (server); + ++ while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ g_clear_error (&local_error); ++ ++ server = camel_imapx_store_get_server (istore, NULL, FALSE, cancellable, &local_error); ++ if (server) { ++ success = camel_imapx_server_create_folder (server, full_name, cancellable, &local_error); ++ g_object_unref (server); ++ } ++ } ++ ++ if (local_error) ++ g_propagate_error (error, local_error); ++ + if (success) { + CamelIMAPXStoreInfo *si; + +@@ -1670,6 +1728,7 @@ imapx_store_delete_folder_sync (CamelSto + { + CamelIMAPXStore *istore = (CamelIMAPXStore *) store; + CamelIMAPXServer *server; ++ GError *local_error = NULL; + gboolean success; + + if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) { +@@ -1681,14 +1740,28 @@ imapx_store_delete_folder_sync (CamelSto + } + /* Use INBOX connection as the implementation would try to select inbox to ensure + * we are not selected on the folder being deleted */ +- server = camel_imapx_store_get_server (istore, "INBOX", cancellable, error); ++ server = camel_imapx_store_get_server (istore, "INBOX", FALSE, cancellable, error); + if (!server) + return FALSE; + +- success = camel_imapx_server_delete_folder ( +- server, folder_name, cancellable, error); ++ success = camel_imapx_server_delete_folder (server, folder_name, cancellable, &local_error); ++ camel_imapx_store_op_done (istore, server, "INBOX"); + g_object_unref (server); + ++ while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ g_clear_error (&local_error); ++ ++ server = camel_imapx_store_get_server (istore, "INBOX", FALSE, cancellable, &local_error); ++ if (server) { ++ success = camel_imapx_server_delete_folder (server, folder_name, cancellable, &local_error); ++ camel_imapx_store_op_done (istore, server, "INBOX"); ++ g_object_unref (server); ++ } ++ } ++ ++ if (local_error) ++ g_propagate_error (error, local_error); ++ + if (success) + imapx_delete_folder_from_cache (istore, folder_name); + +@@ -1734,11 +1807,28 @@ imapx_store_rename_folder_sync (CamelSto + + /* Use INBOX connection as the implementation would try to select inbox to ensure + * we are not selected on the folder being renamed */ +- server = camel_imapx_store_get_server (istore, "INBOX", cancellable, error); ++ server = camel_imapx_store_get_server (istore, "INBOX", FALSE, cancellable, error); + if (server) { +- success = camel_imapx_server_rename_folder ( +- server, old, new, cancellable, error); ++ GError *local_error = NULL; ++ ++ success = camel_imapx_server_rename_folder (server, old, new, cancellable, &local_error); ++ camel_imapx_store_op_done (istore, server, "INBOX"); + g_object_unref (server); ++ ++ while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ g_clear_error (&local_error); ++ ++ server = camel_imapx_store_get_server (istore, "INBOX", FALSE, cancellable, &local_error); ++ if (server) { ++ success = camel_imapx_server_rename_folder (server, old, new, cancellable, &local_error); ++ camel_imapx_store_op_done (istore, server, "INBOX"); ++ g_object_unref (server); ++ } ++ } ++ ++ if (local_error) ++ g_propagate_error (error, local_error); ++ + } + + if (!success) { +@@ -1787,9 +1877,18 @@ imapx_store_noop_sync (CamelStore *store + + for (link = list; link != NULL; link = g_list_next (link)) { + CamelIMAPXServer *server = CAMEL_IMAPX_SERVER (link->data); ++ GError *local_error = NULL; + + /* we just return last noops value, technically not correct though */ +- success = camel_imapx_server_noop (server, NULL, cancellable, error); ++ success = camel_imapx_server_noop (server, NULL, cancellable, &local_error); ++ ++ if (g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { ++ g_clear_error (&local_error); ++ break; ++ } else if (local_error) { ++ g_propagate_error (error, local_error); ++ } ++ + if (!success) + break; + } +@@ -2081,3 +2180,49 @@ camel_imapx_store_set_quota_info (CamelI + g_mutex_unlock (&store->priv->quota_info_lock); + } + ++void ++camel_imapx_store_set_authenticating_concurrent_connection (CamelIMAPXStore *store, ++ gboolean is_concurrent_connection) ++{ ++ g_return_if_fail (CAMEL_IS_IMAPX_STORE (store)); ++ ++ store->priv->is_concurrent_connection = is_concurrent_connection; ++} ++ ++gboolean ++camel_imapx_store_get_authenticating_concurrent_connection (CamelIMAPXStore *store) ++{ ++ g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), FALSE); ++ ++ return store->priv->is_concurrent_connection; ++} ++ ++/* 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, ++ CamelFolder *folder, ++ 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->con_man); ++ ++ for (siter = servers; siter; siter = g_list_next (siter)) { ++ CamelIMAPXServer *imapx_server = siter->data; ++ ++ job = camel_imapx_server_ref_job (imapx_server, folder, job_type, uid); ++ if (job) ++ break; ++ } ++ ++ g_list_free_full (servers, g_object_unref); ++ ++ return job; ++} +diff -up evolution-data-server-3.8.5/camel/camel-imapx-store.h.imapx-conn-manager-ext evolution-data-server-3.8.5/camel/camel-imapx-store.h +--- evolution-data-server-3.8.5/camel/camel-imapx-store.h.imapx-conn-manager-ext 2013-07-23 13:57:56.000000000 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-store.h 2014-05-13 14:17:43.119983665 +0200 +@@ -55,6 +55,9 @@ + + G_BEGIN_DECLS + ++/* Avoid a circular reference. */ ++struct _CamelIMAPXJob; ++ + typedef struct _CamelIMAPXStore CamelIMAPXStore; + typedef struct _CamelIMAPXStoreClass CamelIMAPXStoreClass; + typedef struct _CamelIMAPXStorePrivate CamelIMAPXStorePrivate; +@@ -93,6 +96,7 @@ GType camel_imapx_store_get_type (void) + CamelIMAPXServer * + camel_imapx_store_get_server (CamelIMAPXStore *store, + const gchar *folder_name, ++ gboolean for_expensive_job, + GCancellable *cancellable, + GError **error); + void camel_imapx_store_op_done (CamelIMAPXStore *istore, +@@ -106,6 +110,16 @@ void camel_imapx_store_set_quota_info + (CamelIMAPXStore *store, + const gchar *quota_root_name, + const CamelFolderQuotaInfo *info); ++void camel_imapx_store_set_authenticating_concurrent_connection ++ (CamelIMAPXStore *store, ++ gboolean is_concurrent_connection); ++gboolean camel_imapx_store_get_authenticating_concurrent_connection ++ (CamelIMAPXStore *store); ++struct _CamelIMAPXJob * ++ camel_imapx_store_ref_job (CamelIMAPXStore *imapx_store, ++ CamelFolder *folder, ++ guint32 job_type, ++ const gchar *uid); + + G_END_DECLS + +diff -up evolution-data-server-3.8.5/camel/camel-imapx-stream.c.imapx-conn-manager-ext evolution-data-server-3.8.5/camel/camel-imapx-stream.c +--- evolution-data-server-3.8.5/camel/camel-imapx-stream.c.imapx-conn-manager-ext 2013-07-23 13:57:55.000000000 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-stream.c 2014-05-13 14:17:43.119983665 +0200 +@@ -34,6 +34,7 @@ + #include + + #include "camel-imapx-utils.h" ++#include "camel-imapx-server.h" + #include "camel-imapx-stream.h" + + #define CAMEL_IMAPX_STREAM_GET_PRIVATE(obj) \ +@@ -92,10 +93,14 @@ imapx_stream_fill (CamelIMAPXStream *is, + * that to be an error. But we do -- we should only be here + * if we *know* there are data to receive. So set the error + * accordingly */ +- if (!left) ++ if (!left) { ++ io (is->tagprefix, "Failed to read any bytes into buffer of size %d (from buffer size %d, last read:'%.20s')\n", (gint) (is->priv->bufsize - (is->priv->end - is->priv->buf)), is->priv->bufsize, (const gchar *) is->priv->buf); ++ + g_set_error ( +- error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, ++ error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT, + _("Source stream returned no data")); ++ } ++ + return -1; + } + } +diff -up evolution-data-server-3.8.5/camel/camel-imapx-utils.c.imapx-conn-manager-ext evolution-data-server-3.8.5/camel/camel-imapx-utils.c +--- evolution-data-server-3.8.5/camel/camel-imapx-utils.c.imapx-conn-manager-ext 2014-05-13 14:17:43.110983665 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-utils.c 2014-05-13 14:17:43.119983665 +0200 +@@ -430,8 +430,8 @@ create_initial_capabilities_table (void) + * to free hash table + */ + capa_htable = g_hash_table_new_full ( +- g_str_hash, +- g_str_equal, ++ camel_strcase_hash, ++ camel_strcase_equal, + g_free, + NULL); + +@@ -456,7 +456,7 @@ imapx_parse_capability (CamelIMAPXStream + GError *local_error = NULL; + + cinfo = g_malloc0 (sizeof (*cinfo)); +- cinfo->auth_types = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL); ++ cinfo->auth_types = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, (GDestroyNotify) g_free, NULL); + + /* FIXME: handle auth types */ + while ((tok = camel_imapx_stream_token (stream, &token, &len, cancellable, &local_error)) != '\n' && +@@ -2607,10 +2607,3 @@ imapx_get_temp_uid (void) + + return res; + } +- +-void +-camel_imapx_destroy_job_queue_info (IMAPXJobQueueInfo *jinfo) +-{ +- g_hash_table_destroy (jinfo->folders); +- g_free (jinfo); +-} +diff -up evolution-data-server-3.8.5/camel/camel-imapx-utils.h.imapx-conn-manager-ext evolution-data-server-3.8.5/camel/camel-imapx-utils.h +--- evolution-data-server-3.8.5/camel/camel-imapx-utils.h.imapx-conn-manager-ext 2013-07-23 13:57:45.000000000 +0200 ++++ evolution-data-server-3.8.5/camel/camel-imapx-utils.h 2014-05-13 14:17:43.119983665 +0200 +@@ -365,17 +365,6 @@ gboolean camel_imapx_parse_quotaroot (st + GError **error); + + /* ********************************************************************** */ +-typedef struct _IMAPXJobQueueInfo { +- guint queue_len; +- +- /* list of folders for which jobs are in the queue */ +- GHashTable *folders; +-} IMAPXJobQueueInfo; +- +-void camel_imapx_destroy_job_queue_info +- (IMAPXJobQueueInfo *jinfo); +- +-/* ********************************************************************** */ + + extern guchar imapx_specials[256]; + +diff -up evolution-data-server-3.8.5/camel/camel-service.c.imapx-conn-manager-ext evolution-data-server-3.8.5/camel/camel-service.c +--- evolution-data-server-3.8.5/camel/camel-service.c.imapx-conn-manager-ext 2013-07-23 14:01:51.000000000 +0200 ++++ evolution-data-server-3.8.5/camel/camel-service.c 2014-05-13 14:17:43.120983665 +0200 +@@ -934,7 +934,7 @@ service_authenticate_finish (CamelServic + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (service), service_authenticate), +- CAMEL_AUTHENTICATION_REJECTED); ++ CAMEL_AUTHENTICATION_ERROR); + + simple = G_SIMPLE_ASYNC_RESULT (result); + async_context = g_simple_async_result_get_op_res_gpointer (simple); +@@ -2007,12 +2007,12 @@ camel_service_authenticate_sync (CamelSe + + g_return_val_if_fail ( + CAMEL_IS_SERVICE (service), +- CAMEL_AUTHENTICATION_REJECTED); ++ CAMEL_AUTHENTICATION_ERROR); + + class = CAMEL_SERVICE_GET_CLASS (service); + g_return_val_if_fail ( + class->authenticate_sync != NULL, +- CAMEL_AUTHENTICATION_REJECTED); ++ CAMEL_AUTHENTICATION_ERROR); + + result = class->authenticate_sync ( + service, mechanism, cancellable, error); +@@ -2096,15 +2096,15 @@ camel_service_authenticate_finish (Camel + + g_return_val_if_fail ( + CAMEL_IS_SERVICE (service), +- CAMEL_AUTHENTICATION_REJECTED); ++ CAMEL_AUTHENTICATION_ERROR); + g_return_val_if_fail ( + G_IS_ASYNC_RESULT (result), +- CAMEL_AUTHENTICATION_REJECTED); ++ CAMEL_AUTHENTICATION_ERROR); + + class = CAMEL_SERVICE_GET_CLASS (service); + g_return_val_if_fail ( + class->authenticate_finish, +- CAMEL_AUTHENTICATION_REJECTED); ++ CAMEL_AUTHENTICATION_ERROR); + + return class->authenticate_finish (service, result, error); + } diff --git a/SOURCES/evolution-data-server-3.8.5-imapx-error-cancelled-message-download.patch b/SOURCES/evolution-data-server-3.8.5-imapx-error-cancelled-message-download.patch new file mode 100644 index 0000000..f32350d --- /dev/null +++ b/SOURCES/evolution-data-server-3.8.5-imapx-error-cancelled-message-download.patch @@ -0,0 +1,1010 @@ +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 + diff --git a/SOURCES/evolution-data-server-3.8.5-memory-leaks.patch b/SOURCES/evolution-data-server-3.8.5-memory-leaks.patch new file mode 100644 index 0000000..8626dd4 --- /dev/null +++ b/SOURCES/evolution-data-server-3.8.5-memory-leaks.patch @@ -0,0 +1,59 @@ +diff -up evolution-data-server-3.8.5/calendar/libecal/e-cal-client.c.memory-leaks evolution-data-server-3.8.5/calendar/libecal/e-cal-client.c +--- evolution-data-server-3.8.5/calendar/libecal/e-cal-client.c.memory-leaks 2013-07-23 13:58:10.000000000 +0200 ++++ evolution-data-server-3.8.5/calendar/libecal/e-cal-client.c 2014-05-13 14:19:17.381979346 +0200 +@@ -4022,6 +4022,8 @@ e_cal_client_get_object_list_sync (ECalC + + *out_icalcomps = g_slist_reverse (tmp); + ++ g_strfreev (strv); ++ + return TRUE; + } + +diff -up evolution-data-server-3.8.5/calendar/libecal/e-cal-component.c.memory-leaks evolution-data-server-3.8.5/calendar/libecal/e-cal-component.c +--- evolution-data-server-3.8.5/calendar/libecal/e-cal-component.c.memory-leaks 2013-07-23 13:58:10.000000000 +0200 ++++ evolution-data-server-3.8.5/calendar/libecal/e-cal-component.c 2014-05-13 14:19:17.382979346 +0200 +@@ -4454,16 +4454,19 @@ set_alarm_description_cb (gpointer key, + SetAlarmDescriptionData *sadd; + gboolean changed = FALSE; + const gchar *old_summary = NULL; ++ gboolean free_description = FALSE; + + alarm = value; + sadd = user_data; + + /* set the new description on the alarm */ + desc_prop = icalcomponent_get_first_property (alarm, ICAL_DESCRIPTION_PROPERTY); +- if (desc_prop) ++ if (desc_prop) { + old_summary = icalproperty_get_description (desc_prop); +- else ++ } else { + desc_prop = icalproperty_new_description (sadd->new_summary); ++ free_description = TRUE; ++ } + + /* remove the X-EVOLUTION-NEEDS_DESCRIPTION property */ + icalprop = icalcomponent_get_first_property (alarm, ICAL_X_PROPERTY); +@@ -4488,6 +4491,9 @@ set_alarm_description_cb (gpointer key, + icalproperty_set_description (desc_prop, sadd->new_summary); + } + } ++ ++ if (free_description) ++ icalproperty_free (desc_prop); + } + + /** +diff -up evolution-data-server-3.8.5/libedataserver/e-source-registry.c.memory-leaks evolution-data-server-3.8.5/libedataserver/e-source-registry.c +--- evolution-data-server-3.8.5/libedataserver/e-source-registry.c.memory-leaks 2013-07-23 13:57:33.000000000 +0200 ++++ evolution-data-server-3.8.5/libedataserver/e-source-registry.c 2014-05-13 14:19:17.382979346 +0200 +@@ -3038,7 +3038,7 @@ e_source_registry_dup_unique_display_nam + link = g_list_find (list, source); + if (link != NULL) { + g_object_unref (link->data); +- list = g_list_remove_link (list, link); ++ list = g_list_delete_link (list, link); + } + + /* Now find another source with a matching display name. */ diff --git a/SPECS/evolution-data-server.spec b/SPECS/evolution-data-server.spec index 30206de..b4edef0 100644 --- a/SPECS/evolution-data-server.spec +++ b/SPECS/evolution-data-server.spec @@ -28,7 +28,7 @@ Name: evolution-data-server Version: 3.8.5 -Release: 23%{?dist} +Release: 24%{?dist} Group: System Environment/Libraries Summary: Backend data server for Evolution License: LGPLv2+ @@ -101,6 +101,21 @@ Patch21: evolution-data-server-3.8.5-maildir-get-folder-use-after-free.patch # RH bug #1034384 Patch22: evolution-data-server-3.8.5-imapx-disconnect-changes.patch +# RH bug #1097249, RH bug #1097250, RH bug #1097251 +Patch23: evolution-data-server-3.8.5-imapx-conn-manager-ext.patch + +# RH bug #1097252 +Patch24: evolution-data-server-3.8.5-imapx-check-cancelled-jobs.patch + +# RH bug #1097253 +Patch25: evolution-data-server-3.8.5-memory-leaks.patch + +# RH bug #1097250 - updated patch +Patch26: evolution-data-server-3.8.5-imapx-error-cancelled-message-download.patch + +# RH bug #1100240 +Patch27: evolution-data-server-3.8.5-imapx-check-all-namespaces.patch + ### Build Dependencies ### BuildRequires: libdb-devel @@ -192,6 +207,11 @@ This package contains developer documentation for %{name}. %patch20 -p1 -b .imapx-message-flags %patch21 -p1 -b .maildir-get-folder-use-after-free %patch22 -p1 -b .imapx-disconnect-changes +%patch23 -p1 -b .imapx-conn-manager-ext +%patch24 -p1 -b .imapx-check-cancelled-jobs +%patch25 -p1 -b .memory-leaks +%patch26 -p1 -b .imapx-error-cancelled-message-download +%patch27 -p1 -b .imapx-check-all-namespaces %build %if %{ldap_support} @@ -436,6 +456,22 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas &>/dev/null || : %{_datadir}/gtk-doc/html/libedataserver %changelog +* Wed May 28 2014 Milan Crha - 3.8.5-24 +- Resolves: #1092926 (rebuild due incorrect dist) + +* Thu May 22 2014 Milan Crha - 3.8.5-23.3 +- Add patch for RH bug #1100240 (IMAPx: Check in all namespaces) + +* Fri May 16 2014 Milan Crha - 3.8.5-23.2 +- Update patch for RH bug #1097250 (IMAPx: Avoid 'Empty cache file' error) + +* Tue May 13 2014 Milan Crha - 3.8.5-23.1 +- Add patch for RH bug #1097249 (IMAPx: Improve responsiveness) +- Add patch for RH bug #1097250 (IMAPx: Avoid 'Empty cache file' error) +- Add patch for RH bug #1097251 (IMAPx: Avoid 'Source stream returned no data' error) +- Add patch for RH bug #1097252 (IMAPx: Refetching summary information for all msgs) +- Add patch for RH bug #1097253 (Fix various memory leaks) + * Mon Apr 07 2014 Milan Crha - 3.8.5-23 - Add patch for RH bug #1034384 (IMAPx: Disconnect changes)