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); }