diff -up evolution-data-server-3.12.11/camel/camel-enums.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/camel-enums.h --- evolution-data-server-3.12.11/camel/camel-enums.h.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/camel-enums.h 2016-08-15 13:52:41.939976331 +0200 @@ -410,7 +410,8 @@ typedef enum { /*< flags >*/ CAMEL_STORE_REAL_JUNK_FOLDER = 1 << 4, CAMEL_STORE_CAN_EDIT_FOLDERS = 1 << 5, CAMEL_STORE_USE_CACHE_DIR = 1 << 6, - CAMEL_STORE_CAN_DELETE_FOLDERS_AT_ONCE = 1 << 7 + CAMEL_STORE_CAN_DELETE_FOLDERS_AT_ONCE = 1 << 7, + CAMEL_STORE_SUPPORTS_INITIAL_SETUP = 1 << 8 } CamelStoreFlags; /** diff -up evolution-data-server-3.12.11/camel/camel-offline-store.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/camel-offline-store.c --- evolution-data-server-3.12.11/camel/camel-offline-store.c.imapx-update-to-upstream 2014-10-31 15:25:33.000000000 +0100 +++ evolution-data-server-3.12.11/camel/camel-offline-store.c 2016-08-15 13:52:41.939976331 +0200 @@ -181,7 +181,7 @@ camel_offline_store_set_online_sync (Cam g_return_val_if_fail (CAMEL_IS_OFFLINE_STORE (store), FALSE); - if (store->priv->online == online) + if (camel_offline_store_get_online (store) == online) return TRUE; service = CAMEL_SERVICE (store); diff -up evolution-data-server-3.12.11/camel/camel-operation.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/camel-operation.c --- evolution-data-server-3.12.11/camel/camel-operation.c.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/camel-operation.c 2016-08-15 13:52:41.939976331 +0200 @@ -49,6 +49,9 @@ struct _CamelOperationPrivate { enum { STATUS, + PUSH_MESSAGE, + POP_MESSAGE, + PROGRESS, LAST_SIGNAL }; @@ -173,6 +176,32 @@ camel_operation_class_init (CamelOperati G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT); + + signals[PUSH_MESSAGE] = g_signal_new ( + "push-message", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_STRING); + + signals[POP_MESSAGE] = g_signal_new ( + "pop-message", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + signals[PROGRESS] = g_signal_new ( + "progress", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_INT); } static void @@ -249,6 +278,7 @@ camel_operation_push_message (GCancellab { CamelOperation *operation; StatusNode *node; + gchar *message; va_list ap; if (cancellable == NULL) @@ -259,14 +289,18 @@ camel_operation_push_message (GCancellab g_return_if_fail (CAMEL_IS_OPERATION (cancellable)); + va_start (ap, format); + message = g_strdup_vprintf (format, ap); + va_end (ap); + + g_signal_emit (cancellable, signals[PUSH_MESSAGE], 0, message); + LOCK (); operation = CAMEL_OPERATION (cancellable); - va_start (ap, format); - node = status_node_new (); - node->message = g_strdup_vprintf (format, ap); + node->message = message; /* takes ownership */ node->operation = g_object_ref (operation); if (g_queue_is_empty (&operation->priv->status_stack)) { @@ -288,8 +322,6 @@ camel_operation_push_message (GCancellab g_queue_push_head (&operation->priv->status_stack, node); - va_end (ap); - UNLOCK (); } @@ -317,6 +349,8 @@ camel_operation_pop_message (GCancellabl g_return_if_fail (CAMEL_IS_OPERATION (cancellable)); + g_signal_emit (cancellable, signals[POP_MESSAGE], 0); + LOCK (); operation = CAMEL_OPERATION (cancellable); @@ -376,6 +410,8 @@ camel_operation_progress (GCancellable * g_return_if_fail (CAMEL_IS_OPERATION (cancellable)); + g_signal_emit (cancellable, signals[PROGRESS], 0, percent); + LOCK (); operation = CAMEL_OPERATION (cancellable); diff -up evolution-data-server-3.12.11/camel/camel-store.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/camel-store.c --- evolution-data-server-3.12.11/camel/camel-store.c.imapx-update-to-upstream 2014-11-03 13:58:08.000000000 +0100 +++ evolution-data-server-3.12.11/camel/camel-store.c 2016-08-15 13:52:41.940976331 +0200 @@ -64,6 +64,7 @@ struct _AsyncContext { gchar *folder_name_2; gboolean expunge; guint32 flags; + GHashTable *save_setup; }; struct _SignalClosure { @@ -96,6 +97,11 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE ( static void async_context_free (AsyncContext *async_context) { + if (async_context->save_setup) { + g_hash_table_destroy (async_context->save_setup); + async_context->save_setup = NULL; + } + g_free (async_context->folder_name_1); g_free (async_context->folder_name_2); @@ -501,6 +507,15 @@ store_synchronize_sync (CamelStore *stor } static gboolean +store_initial_setup_sync (CamelStore *store, + GHashTable *out_save_setup, + GCancellable *cancellable, + GError **error) +{ + return TRUE; +} + +static gboolean store_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) @@ -570,6 +585,7 @@ camel_store_class_init (CamelStoreClass class->get_junk_folder_sync = store_get_junk_folder_sync; class->get_trash_folder_sync = store_get_trash_folder_sync; class->synchronize_sync = store_synchronize_sync; + class->initial_setup_sync = store_initial_setup_sync; signals[FOLDER_CREATED] = g_signal_new ( "folder-created", @@ -2897,3 +2913,171 @@ camel_store_synchronize_finish (CamelSto return g_task_propagate_boolean (G_TASK (result), error); } +/** + * camel_store_initial_setup_sync: + * @store: a #CamelStore + * @out_save_setup: (out) (transfer container) (element-type utf8 utf8): setup values to save + * @cancellable: optional #GCancellable object, or %NULL + * @error: return location for a #GError, or %NULL + * + * Runs initial setup for the @store. It's meant to preset some + * values the first time the account connects to the server after + * it had been created. The function should return %TRUE even if + * it didn't populate anything. The default implementation does + * just that. + * + * The save_setup result, if not %NULL, should be freed using + * g_hash_table_destroy(). It's not an error to have it %NULL, + * it only means the @store doesn't have anything to save. + * Both the key and the value in the hash are newly allocated + * UTF-8 strings, owned by the hash table. + * + * The @store advertises support of this function by including + * CAMEL_STORE_SUPPORTS_INITIAL_SETUP in CamelStore::flags. + * + * Returns: %TRUE on success, %FALSE on error + * + * Since: 3.12.11-25 (3.20) + **/ +gboolean +camel_store_initial_setup_sync (CamelStore *store, + GHashTable **out_save_setup, + GCancellable *cancellable, + GError **error) +{ + GHashTable *save_setup; + CamelStoreClass *class; + gboolean success; + + g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE); + g_return_val_if_fail (out_save_setup != NULL, FALSE); + + *out_save_setup = NULL; + + class = CAMEL_STORE_GET_CLASS (store); + g_return_val_if_fail (class->initial_setup_sync != NULL, FALSE); + + save_setup = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + success = class->initial_setup_sync (store, save_setup, cancellable, error); + + if (!success || !g_hash_table_size (save_setup)) { + g_hash_table_destroy (save_setup); + save_setup = NULL; + } + + CAMEL_CHECK_GERROR (store, initial_setup_sync, success, error); + + *out_save_setup = save_setup; + + return success; +} + +static void +store_initial_setup_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + gboolean success; + AsyncContext *async_context; + GError *local_error = NULL; + + async_context = (AsyncContext *) task_data; + + success = camel_store_initial_setup_sync ( + CAMEL_STORE (source_object), + &async_context->save_setup, + cancellable, &local_error); + + if (local_error != NULL) { + g_task_return_error (task, local_error); + } else { + g_task_return_boolean (task, success); + } +} + +/** + * camel_store_initial_setup: + * @store: a #CamelStore + * @io_priority: the I/O priority of the request + * @cancellable: optional #GCancellable object, or %NULL + * @callback: a #GAsyncReadyCallback to call when the request is satisfied + * @user_data: data to pass to the callback function + * + * Runs initial setup for the @store asynchronously. + * + * When the operation is finished, @callback will be called. You can then + * call camel_store_initial_setup_finish() to get the result of the operation. + * + * The @store advertises support of this function by including + * CAMEL_STORE_SUPPORTS_INITIAL_SETUP in CamelStore::flags. + * + * Since: 3.12.11-25 (3.20) + **/ +void +camel_store_initial_setup (CamelStore *store, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + AsyncContext *async_context; + + g_return_if_fail (CAMEL_IS_STORE (store)); + + async_context = g_slice_new0 (AsyncContext); + + task = g_task_new (store, cancellable, callback, user_data); + g_task_set_source_tag (task, camel_store_initial_setup); + g_task_set_priority (task, io_priority); + + g_task_set_task_data ( + task, async_context, + (GDestroyNotify) async_context_free); + + g_task_run_in_thread (task, store_initial_setup_thread); + + g_object_unref (task); +} + +/** + * camel_store_initial_setup_finish: + * @store: a #CamelStore + * @result: a #GAsyncResult + * @out_save_setup: (out) (transfer container) (element-type utf8 utf8): setup values to save + * @error: return location for a #GError, or %NULL + * + * Finishes the operation started with camel_store_initial_setup(). + * + * The save_setup result, if not %NULL, should be freed using + * g_hash_table_destroy(). It's not an error to have it %NULL, + * it only means the @store doesn't have anything to save. + * + * Returns: %TRUE on success, %FALSE on error + * + * Since: 3.12.11-25 (3.20) + **/ +gboolean +camel_store_initial_setup_finish (CamelStore *store, + GAsyncResult *result, + GHashTable **out_save_setup, + GError **error) +{ + AsyncContext *async_context; + + g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE); + g_return_val_if_fail (out_save_setup != NULL, FALSE); + g_return_val_if_fail (g_task_is_valid (result, store), FALSE); + + g_return_val_if_fail ( + g_async_result_is_tagged ( + result, camel_store_initial_setup), FALSE); + + async_context = g_task_get_task_data (G_TASK (result)); + *out_save_setup = async_context->save_setup; + async_context->save_setup = NULL; + + return g_task_propagate_boolean (G_TASK (result), error); +} diff -up evolution-data-server-3.12.11/camel/camel-store.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/camel-store.h --- evolution-data-server-3.12.11/camel/camel-store.h.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/camel-store.h 2016-08-15 13:52:41.940976331 +0200 @@ -63,6 +63,32 @@ #define CAMEL_STORE_ERROR \ (camel_store_error_quark ()) +/** + * --@CAMEL_STORE_SETUP_ARCHIVE_FOLDER: Name of an Archive folder key-- + * @CAMEL_STORE_SETUP_DRAFTS_FOLDER: Name of a Drafts folder key + * @CAMEL_STORE_SETUP_SENT_FOLDER: Name of a Sent folder key + * @CAMEL_STORE_SETUP_TEMPLATES_FOLDER: Name of a Templates folder key + * + * Key names to a hash table with values to preset for the account used + * as in the camel_store_initial_setup_sync() function. + * + * The key name consists of up to four parts: Source:Extension:Property[:Type] + * Source can be 'Collection', 'Account', 'Submission', 'Transport', 'Backend'. + * Extension is any extension name; it's up to the key creator to make sure + * the extension belongs to that particular Source. + * Property is a property name in the Extension. + * Type is an optional letter describing the type of the value; if not set, then + * string is used. Available values are: 'b' for boolean, 'i' for integer, + * 's' for string, 'f' for folder full path. + * All the part values are case sensitive. + * + * Since: 3.12.11-25 (3.20) + **/ +/* #define CAMEL_STORE_SETUP_ARCHIVE_FOLDER "Account:Mail Account:archive-folder:f" */ +#define CAMEL_STORE_SETUP_DRAFTS_FOLDER "Submission:Mail Composition:drafts-folder:f" +#define CAMEL_STORE_SETUP_SENT_FOLDER "Submission:Mail Submission:sent-folder:f" +#define CAMEL_STORE_SETUP_TEMPLATES_FOLDER "Submission:Mail Composition:templates-folder:f" + G_BEGIN_DECLS /** @@ -178,9 +204,13 @@ struct _CamelStoreClass { gboolean expunge, GCancellable *cancellable, GError **error); + gboolean (*initial_setup_sync) (CamelStore *store, + GHashTable *save_setup, + GCancellable *cancellable, + GError **error); /* Reserved slots for methods. */ - gpointer reserved_for_methods[21]; + gpointer reserved_for_methods[20]; /* Signals */ void (*folder_created) (CamelStore *store, @@ -357,6 +387,20 @@ void camel_store_synchronize (CamelSto gboolean camel_store_synchronize_finish (CamelStore *store, GAsyncResult *result, GError **error); +gboolean camel_store_initial_setup_sync (CamelStore *store, + GHashTable **out_save_setup, + GCancellable *cancellable, + GError **error); +void camel_store_initial_setup (CamelStore *store, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean camel_store_initial_setup_finish + (CamelStore *store, + GAsyncResult *result, + GHashTable **out_save_setup, + GError **error); G_END_DECLS diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.c.imapx-update-to-upstream 2014-06-16 14:57:07.000000000 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.c 2016-08-15 13:52:41.940976331 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-command.c * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ @@ -36,21 +36,11 @@ struct _CamelIMAPXRealCommand { volatile gint ref_count; - CamelIMAPXJob *job; - /* For building the part. */ GString *buffer; - /* Mailbox to select before running command. */ - GWeakRef mailbox; - /* For network/parse errors. */ GError *error; - - /* Used for running some commands synchronously. */ - GCond done_sync_cond; - GMutex done_sync_mutex; - gboolean done_sync_flag; }; /* Safe to cast to a GQueue. */ @@ -60,8 +50,7 @@ struct _CamelIMAPXCommandQueue { CamelIMAPXCommand * camel_imapx_command_new (CamelIMAPXServer *is, - const gchar *name, - CamelIMAPXMailbox *mailbox, + guint32 job_kind, const gchar *format, ...) { @@ -74,14 +63,13 @@ camel_imapx_command_new (CamelIMAPXServe /* Initialize private bits. */ real_ic->ref_count = 1; real_ic->buffer = g_string_sized_new (512); - g_weak_ref_init (&real_ic->mailbox, mailbox); - g_cond_init (&real_ic->done_sync_cond); - g_mutex_init (&real_ic->done_sync_mutex); /* Initialize public bits. */ real_ic->public.is = is; real_ic->public.tag = tag++; - real_ic->public.name = name; + real_ic->public.job_kind = job_kind; + real_ic->public.status = NULL; + real_ic->public.completed = FALSE; g_queue_init (&real_ic->public.parts); if (format != NULL && *format != '\0') { @@ -141,22 +129,10 @@ camel_imapx_command_unref (CamelIMAPXCom /* Free the private stuff. */ - if (real_ic->job != NULL) - camel_imapx_job_unref (real_ic->job); - g_string_free (real_ic->buffer, TRUE); - g_weak_ref_clear (&real_ic->mailbox); - g_clear_error (&real_ic->error); - g_cond_clear (&real_ic->done_sync_cond); - g_mutex_clear (&real_ic->done_sync_mutex); - - /* Do NOT try to free the GError. If set it should have been - * propagated to the CamelIMAPXJob, so it's either NULL or the - * CamelIMAPXJob owns it now. */ - /* Fill the memory with a bit pattern before releasing * it back to the slab allocator, so we can more easily * identify dangling CamelIMAPXCommand pointers. */ @@ -180,64 +156,6 @@ camel_imapx_command_check (CamelIMAPXCom return (real_ic != NULL && real_ic->ref_count > 0); } -gint -camel_imapx_command_compare (CamelIMAPXCommand *ic1, - CamelIMAPXCommand *ic2) -{ - g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic1), 0); - g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic2), 0); - - if (ic1->pri == ic2->pri) - return 0; - - return (ic1->pri < ic2->pri) ? -1 : 1; -} - -CamelIMAPXJob * -camel_imapx_command_get_job (CamelIMAPXCommand *ic) -{ - CamelIMAPXRealCommand *real_ic; - - g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic), NULL); - - real_ic = (CamelIMAPXRealCommand *) ic; - - return real_ic->job; -} - -void -camel_imapx_command_set_job (CamelIMAPXCommand *ic, - CamelIMAPXJob *job) -{ - CamelIMAPXRealCommand *real_ic; - - g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic)); - - real_ic = (CamelIMAPXRealCommand *) ic; - - if (job != NULL) { - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); - camel_imapx_job_ref (job); - } - - if (real_ic->job != NULL) - camel_imapx_job_unref (real_ic->job); - - real_ic->job = job; -} - -CamelIMAPXMailbox * -camel_imapx_command_ref_mailbox (CamelIMAPXCommand *ic) -{ - CamelIMAPXRealCommand *real_ic; - - g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic), NULL); - - real_ic = (CamelIMAPXRealCommand *) ic; - - return g_weak_ref_get (&real_ic->mailbox); -} - void camel_imapx_command_add (CamelIMAPXCommand *ic, const gchar *format, @@ -280,7 +198,7 @@ camel_imapx_command_addv (CamelIMAPXComm g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic)); - c (ic->is->tagprefix, "adding command, format = '%s'\n", format); + c (camel_imapx_server_get_tagprefix (ic->is), "adding command, format = '%s'\n", format); buffer = ((CamelIMAPXRealCommand *) ic)->buffer; @@ -330,12 +248,12 @@ camel_imapx_command_addv (CamelIMAPXComm break; case 'D': /* datawrapper */ D = va_arg (ap, CamelDataWrapper *); - c (ic->is->tagprefix, "got data wrapper '%p'\n", D); + c (camel_imapx_server_get_tagprefix (ic->is), "got data wrapper '%p'\n", D); camel_imapx_command_add_part (ic, CAMEL_IMAPX_COMMAND_DATAWRAPPER, D); break; case 'P': /* filename path */ P = va_arg (ap, gchar *); - c (ic->is->tagprefix, "got file path '%s'\n", P); + c (camel_imapx_server_get_tagprefix (ic->is), "got file path '%s'\n", P); camel_imapx_command_add_part (ic, CAMEL_IMAPX_COMMAND_FILE, P); break; case 't': /* token */ @@ -344,7 +262,7 @@ camel_imapx_command_addv (CamelIMAPXComm break; case 's': /* simple string */ s = va_arg (ap, gchar *); - c (ic->is->tagprefix, "got string '%s'\n", g_str_has_prefix (format, "LOGIN") ? "***" : s); + c (camel_imapx_server_get_tagprefix (ic->is), "got string '%s'\n", g_str_has_prefix (format, "LOGIN") ? "***" : s); output_string: if (s && *s) { guchar mask = imapx_is_mask (s); @@ -400,19 +318,19 @@ camel_imapx_command_addv (CamelIMAPXComm case 'u': if (llong == 1) { l = va_arg (ap, glong); - c (ic->is->tagprefix, "got glong '%d'\n", (gint) l); + c (camel_imapx_server_get_tagprefix (ic->is), "got glong '%d'\n", (gint) l); memcpy (literal_format, start, p - start); literal_format[p - start] = 0; g_string_append_printf (buffer, literal_format, l); } else if (llong == 2) { guint64 i64 = va_arg (ap, guint64); - c (ic->is->tagprefix, "got guint64 '%d'\n", (gint) i64); + c (camel_imapx_server_get_tagprefix (ic->is), "got guint64 '%d'\n", (gint) i64); memcpy (literal_format, start, p - start); literal_format[p - start] = 0; g_string_append_printf (buffer, literal_format, i64); } else { d = va_arg (ap, gint); - c (ic->is->tagprefix, "got gint '%d'\n", d); + c (camel_imapx_server_get_tagprefix (ic->is), "got gint '%d'\n", d); memcpy (literal_format, start, p - start); literal_format[p - start] = 0; g_string_append_printf (buffer, literal_format, d); @@ -426,7 +344,7 @@ camel_imapx_command_addv (CamelIMAPXComm case '\\': /* only for \\ really, we dont support \n\r etc at all */ c = *p; if (c) { - g_assert (c == '\\'); + g_warn_if_fail (c == '\\'); g_string_append_len (buffer, ps, p - ps); p++; ps = p; @@ -472,6 +390,8 @@ camel_imapx_command_add_part (CamelIMAPX /* we presume we'll need to get additional data only if we're not authenticated yet */ g_object_ref (ob); mechanism = camel_sasl_get_mechanism (CAMEL_SASL (ob)); + if (g_strcmp0 (mechanism, "Google") == 0) + mechanism = "XOAUTH2"; g_string_append (buffer, mechanism); if (!camel_sasl_get_authenticated ((CamelSasl *) ob)) type |= CAMEL_IMAPX_COMMAND_CONTINUATION; @@ -502,7 +422,7 @@ camel_imapx_command_add_part (CamelIMAPX if (type & CAMEL_IMAPX_COMMAND_LITERAL_PLUS) { g_string_append_c (buffer, '{'); g_string_append_printf (buffer, "%u", ob_size); - if (CAMEL_IMAPX_HAVE_CAPABILITY (ic->is->cinfo, LITERALPLUS)) { + if (camel_imapx_server_have_capability (ic->is, IMAPX_CAPABILITY_LITERALPLUS)) { g_string_append_c (buffer, '+'); } else { type &= ~CAMEL_IMAPX_COMMAND_LITERAL_PLUS; @@ -533,282 +453,12 @@ camel_imapx_command_close (CamelIMAPXCom buffer = ((CamelIMAPXRealCommand *) ic)->buffer; if (buffer->len > 5 && g_ascii_strncasecmp (buffer->str, "LOGIN", 5) == 0) { - c (ic->is->tagprefix, "completing command buffer is [%d] 'LOGIN...'\n", (gint) buffer->len); + c (camel_imapx_server_get_tagprefix (ic->is), "completing command buffer is [%d] 'LOGIN...'\n", (gint) buffer->len); } else { - c (ic->is->tagprefix, "completing command buffer is [%d] '%.*s'\n", (gint) buffer->len, (gint) buffer->len, buffer->str); + c (camel_imapx_server_get_tagprefix (ic->is), "completing command buffer is [%d] '%.*s'\n", (gint) buffer->len, (gint) buffer->len, buffer->str); } if (buffer->len > 0) camel_imapx_command_add_part (ic, CAMEL_IMAPX_COMMAND_SIMPLE, NULL); g_string_set_size (buffer, 0); } - -void -camel_imapx_command_wait (CamelIMAPXCommand *ic) -{ - CamelIMAPXRealCommand *real_ic; - - g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic)); - - real_ic = (CamelIMAPXRealCommand *) ic; - - g_mutex_lock (&real_ic->done_sync_mutex); - while (!real_ic->done_sync_flag) - g_cond_wait ( - &real_ic->done_sync_cond, - &real_ic->done_sync_mutex); - g_mutex_unlock (&real_ic->done_sync_mutex); -} - -void -camel_imapx_command_done (CamelIMAPXCommand *ic) -{ - CamelIMAPXRealCommand *real_ic; - - g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic)); - - real_ic = (CamelIMAPXRealCommand *) ic; - - g_mutex_lock (&real_ic->done_sync_mutex); - real_ic->done_sync_flag = TRUE; - g_cond_broadcast (&real_ic->done_sync_cond); - g_mutex_unlock (&real_ic->done_sync_mutex); -} - -/** - * camel_imapx_command_failed: - * @ic: a #CamelIMAPXCommand - * @error: the error which caused the failure - * - * Copies @error to be returned in camel_imapx_command_set_error_if_failed(). - * Call this function if a networking or parsing error occurred to force all - * active IMAP commands to abort processing. - * - * Since: 3.10 - **/ -void -camel_imapx_command_failed (CamelIMAPXCommand *ic, - const GError *error) -{ - CamelIMAPXRealCommand *real_ic; - - g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic)); - g_return_if_fail (error != NULL); - - real_ic = (CamelIMAPXRealCommand *) ic; - - /* Do not overwrite errors, the first passed in wins */ - if (real_ic->error != NULL) - return; - - real_ic->error = g_error_copy (error); -} - -gboolean -camel_imapx_command_set_error_if_failed (CamelIMAPXCommand *ic, - GError **error) -{ - CamelIMAPXRealCommand *real_ic; - - g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic), FALSE); - - real_ic = (CamelIMAPXRealCommand *) ic; - - /* Check for a networking or parsing error. */ - if (real_ic->error != NULL) { - g_propagate_error (error, real_ic->error); - real_ic->error = NULL; - return TRUE; - } - - /* Check if the IMAP server rejected the command. */ - if (ic->status != NULL && ic->status->result != IMAPX_OK) { - - /* FIXME Map IMAP response codes to more - * meaningful GError domains/codes. - * - * switch (ic->status->condition) { - * case IMAPX_AUTHENTICATIONFAILED: - * g_set_error (...); - * break; - * ... - * } - */ - - if (ic->status->text != NULL) - g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, - "%s", ic->status->text); - else - g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, - "%s", _("Unknown error")); - return TRUE; - } - - if (real_ic->job) - return camel_imapx_job_set_error_if_failed (real_ic->job, error); - - return FALSE; -} - -CamelIMAPXCommandQueue * -camel_imapx_command_queue_new (void) -{ - /* An initialized GQueue is simply zero-filled, - * so we can skip calling g_queue_init() here. */ - return g_slice_new0 (CamelIMAPXCommandQueue); -} - -void -camel_imapx_command_queue_free (CamelIMAPXCommandQueue *queue) -{ - CamelIMAPXCommand *ic; - - g_return_if_fail (queue != NULL); - - while ((ic = g_queue_pop_head ((GQueue *) queue)) != NULL) - camel_imapx_command_unref (ic); - - g_slice_free (CamelIMAPXCommandQueue, queue); -} - -void -camel_imapx_command_queue_transfer (CamelIMAPXCommandQueue *from, - CamelIMAPXCommandQueue *to) -{ - GList *link; - - g_return_if_fail (from != NULL); - g_return_if_fail (to != NULL); - - while ((link = g_queue_pop_head_link ((GQueue *) from)) != NULL) - g_queue_push_tail_link ((GQueue *) to, link); -} - -void -camel_imapx_command_queue_push_tail (CamelIMAPXCommandQueue *queue, - CamelIMAPXCommand *ic) -{ - g_return_if_fail (queue != NULL); - g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic)); - - camel_imapx_command_ref (ic); - - g_queue_push_tail ((GQueue *) queue, ic); -} - -void -camel_imapx_command_queue_insert_sorted (CamelIMAPXCommandQueue *queue, - CamelIMAPXCommand *ic) -{ - g_return_if_fail (queue != NULL); - g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic)); - - camel_imapx_command_ref (ic); - - g_queue_insert_sorted ( - (GQueue *) queue, ic, (GCompareDataFunc) - camel_imapx_command_compare, NULL); -} - -gboolean -camel_imapx_command_queue_is_empty (CamelIMAPXCommandQueue *queue) -{ - g_return_val_if_fail (queue != NULL, TRUE); - - return g_queue_is_empty ((GQueue *) queue); -} - -guint -camel_imapx_command_queue_get_length (CamelIMAPXCommandQueue *queue) -{ - g_return_val_if_fail (queue != NULL, 0); - - return g_queue_get_length ((GQueue *) queue); -} - -CamelIMAPXCommand * -camel_imapx_command_queue_peek_head (CamelIMAPXCommandQueue *queue) -{ - g_return_val_if_fail (queue != NULL, NULL); - - return g_queue_peek_head ((GQueue *) queue); -} - -GList * -camel_imapx_command_queue_peek_head_link (CamelIMAPXCommandQueue *queue) -{ - g_return_val_if_fail (queue != NULL, NULL); - - return g_queue_peek_head_link ((GQueue *) queue); -} - -gboolean -camel_imapx_command_queue_remove (CamelIMAPXCommandQueue *queue, - CamelIMAPXCommand *ic) -{ - g_return_val_if_fail (queue != NULL, FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic), FALSE); - - if (g_queue_remove ((GQueue *) queue, ic)) { - camel_imapx_command_unref (ic); - return TRUE; - } - - return FALSE; -} - -void -camel_imapx_command_queue_delete_link (CamelIMAPXCommandQueue *queue, - GList *link) -{ - g_return_if_fail (queue != NULL); - g_return_if_fail (link != NULL); - - /* Verify the link is actually in the queue. */ - if (g_queue_link_index ((GQueue *) queue, link) == -1) { - g_warning ("%s: Link not found in queue", G_STRFUNC); - return; - } - - camel_imapx_command_unref ((CamelIMAPXCommand *) link->data); - g_queue_delete_link ((GQueue *) queue, link); -} - -/** - * camel_imapx_command_queue_ref_by_tag: - * @queue: a #CamelIMAPXCommandQueue - * @tag: a #CamelIMAPXCommand tag - * - * Returns the #CamelIMAPXCommand in @queue with a matching @tag, or %NULL - * if no match is found. - * - * The returned #CamelIMAPXCommand is referenced for thread-safety and should - * be unreferenced with camel_imapx_command_unref() when finished with it. - * - * Since: 3.10 - **/ -CamelIMAPXCommand * -camel_imapx_command_queue_ref_by_tag (CamelIMAPXCommandQueue *queue, - guint32 tag) -{ - CamelIMAPXCommand *match = NULL; - GList *head, *link; - - g_return_val_if_fail (queue != NULL, NULL); - - head = camel_imapx_command_queue_peek_head_link (queue); - - for (link = head; link != NULL; link = g_list_next (link)) { - CamelIMAPXCommand *command = link->data; - - if (command->tag == tag) { - match = camel_imapx_command_ref (command); - break; - } - } - - return match; -} - diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.h.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-command.h 2016-08-15 13:52:41.943976330 +0200 @@ -1,24 +1,23 @@ /* * camel-imapx-command.h * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ #ifndef CAMEL_IMAPX_COMMAND_H #define CAMEL_IMAPX_COMMAND_H -#include "camel-imapx-mailbox.h" #include "camel-imapx-utils.h" #define CAMEL_IS_IMAPX_COMMAND(command) \ @@ -27,7 +26,6 @@ G_BEGIN_DECLS /* Avoid a circular reference. */ -struct _CamelIMAPXJob; struct _CamelIMAPXServer; typedef struct _CamelIMAPXCommand CamelIMAPXCommand; @@ -66,39 +64,27 @@ struct _CamelIMAPXCommand { struct _CamelIMAPXServer *is; gint pri; - /* Command name/type (e.g. FETCH) */ - const gchar *name; + guint32 job_kind; /* CamelIMAPXJobKind */ - /* Status for command, indicates it is complete if != NULL. */ + /* Status for command. */ struct _status_info *status; guint32 tag; + gboolean completed; GQueue parts; GList *current_part; - - /* Responsible for free'ing the command. */ - CamelIMAPXCommandFunc complete; }; CamelIMAPXCommand * camel_imapx_command_new (struct _CamelIMAPXServer *is, - const gchar *name, - CamelIMAPXMailbox *mailbox, + guint32 job_kind, const gchar *format, ...); CamelIMAPXCommand * camel_imapx_command_ref (CamelIMAPXCommand *ic); void camel_imapx_command_unref (CamelIMAPXCommand *ic); gboolean camel_imapx_command_check (CamelIMAPXCommand *ic); -gint camel_imapx_command_compare (CamelIMAPXCommand *ic1, - CamelIMAPXCommand *ic2); -struct _CamelIMAPXJob * - camel_imapx_command_get_job (CamelIMAPXCommand *ic); -void camel_imapx_command_set_job (CamelIMAPXCommand *ic, - struct _CamelIMAPXJob *job); -CamelIMAPXMailbox * - camel_imapx_command_ref_mailbox (CamelIMAPXCommand *ic); void camel_imapx_command_add (CamelIMAPXCommand *ic, const gchar *format, ...); @@ -109,51 +95,6 @@ void camel_imapx_command_add_part (Came CamelIMAPXCommandPartType type, gpointer data); void camel_imapx_command_close (CamelIMAPXCommand *ic); -void camel_imapx_command_wait (CamelIMAPXCommand *ic); -void camel_imapx_command_done (CamelIMAPXCommand *ic); -void camel_imapx_command_failed (CamelIMAPXCommand *ic, - const GError *error); -gboolean camel_imapx_command_set_error_if_failed - (CamelIMAPXCommand *ic, - GError **error); - -/* These are simple GQueue wrappers for CamelIMAPXCommands. - * They help make sure reference counting is done properly. - * Add more wrappers as needed, don't circumvent them. */ - -typedef struct _CamelIMAPXCommandQueue CamelIMAPXCommandQueue; - -CamelIMAPXCommandQueue * - camel_imapx_command_queue_new (void); -void camel_imapx_command_queue_free (CamelIMAPXCommandQueue *queue); -void camel_imapx_command_queue_transfer - (CamelIMAPXCommandQueue *from, - CamelIMAPXCommandQueue *to); -void camel_imapx_command_queue_push_tail - (CamelIMAPXCommandQueue *queue, - CamelIMAPXCommand *ic); -void camel_imapx_command_queue_insert_sorted - (CamelIMAPXCommandQueue *queue, - CamelIMAPXCommand *ic); -gboolean camel_imapx_command_queue_is_empty - (CamelIMAPXCommandQueue *queue); -guint camel_imapx_command_queue_get_length - (CamelIMAPXCommandQueue *queue); -CamelIMAPXCommand * - camel_imapx_command_queue_peek_head - (CamelIMAPXCommandQueue *queue); -GList * camel_imapx_command_queue_peek_head_link - (CamelIMAPXCommandQueue *queue); -gboolean camel_imapx_command_queue_remove - (CamelIMAPXCommandQueue *queue, - CamelIMAPXCommand *ic); -void camel_imapx_command_queue_delete_link - (CamelIMAPXCommandQueue *queue, - GList *link); -CamelIMAPXCommand * - camel_imapx_command_queue_ref_by_tag - (CamelIMAPXCommandQueue *queue, - guint32 tag); G_END_DECLS diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.c.imapx-update-to-upstream 2014-11-07 08:34:58.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.c 2016-08-15 13:52:41.945976330 +0200 @@ -18,7 +18,16 @@ * Authors: Chenthill Palanisamy */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + #include "camel-imapx-conn-manager.h" +#include "camel-imapx-folder.h" +#include "camel-imapx-job.h" #include "camel-imapx-settings.h" #include "camel-imapx-store.h" #include "camel-imapx-utils.h" @@ -34,6 +43,9 @@ #define CON_WRITE_UNLOCK(x) \ (g_rw_lock_writer_unlock (&(x)->priv->rw_lock)) +#define JOB_QUEUE_LOCK(x) g_rec_mutex_lock (&(x)->priv->job_queue_lock) +#define JOB_QUEUE_UNLOCK(x) g_rec_mutex_unlock (&(x)->priv->job_queue_lock) + #define CAMEL_IMAPX_CONN_MANAGER_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), CAMEL_TYPE_IMAPX_CONN_MANAGER, CamelIMAPXConnManagerPrivate)) @@ -41,23 +53,32 @@ typedef struct _ConnectionInfo ConnectionInfo; struct _CamelIMAPXConnManagerPrivate { - /* XXX Might be easier for this to be a hash table, - * with CamelIMAPXServer pointers as the keys. */ - GList *connections; + GList *connections; /* ConnectionInfo * */ GWeakRef store; GRWLock rw_lock; guint limit_max_connections; GMutex pending_connections_lock; GSList *pending_connections; /* GCancellable * */ + + gchar last_tagprefix; + + GRecMutex job_queue_lock; + GSList *job_queue; /* CamelIMAPXJob * */ + + GMutex busy_connections_lock; + GCond busy_connections_cond; + + GMutex busy_mailboxes_lock; /* used for both busy_mailboxes and idle_mailboxes */ + GHashTable *busy_mailboxes; /* CamelIMAPXMailbox ~> gint */ + GHashTable *idle_mailboxes; /* CamelIMAPXMailbox ~> gint */ }; struct _ConnectionInfo { GMutex lock; CamelIMAPXServer *is; - GHashTable *folder_names; - gchar *selected_folder; - GError *shutdown_error; + gboolean busy; + gulong refresh_mailbox_handler_id; volatile gint ref_count; }; @@ -66,42 +87,101 @@ enum { PROP_STORE }; +enum { + CONNECTION_CREATED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + G_DEFINE_TYPE ( CamelIMAPXConnManager, camel_imapx_conn_manager, G_TYPE_OBJECT) +static gboolean +imapx_conn_manager_copy_message_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + CamelIMAPXMailbox *destination, + GPtrArray *uids, + gboolean delete_originals, + gboolean remove_deleted_flags, + gboolean skip_sync_changes, + GCancellable *cancellable, + GError **error); + +typedef struct _MailboxRefreshData { + CamelIMAPXConnManager *conn_man; + CamelIMAPXMailbox *mailbox; +} MailboxRefreshData; + static void -imapx_conn_shutdown (CamelIMAPXServer *is, - const GError *error, - CamelIMAPXConnManager *con_man); +mailbox_refresh_data_free (MailboxRefreshData *data) +{ + if (data) { + g_clear_object (&data->conn_man); + g_clear_object (&data->mailbox); + g_free (data); + } +} + +static gpointer +imapx_conn_manager_idle_mailbox_refresh_thread (gpointer user_data) +{ + MailboxRefreshData *data = user_data; + GError *local_error = NULL; + + g_return_val_if_fail (data != NULL, NULL); + + /* passing NULL cancellable means to use only the job's abort cancellable */ + if (!camel_imapx_conn_manager_refresh_info_sync (data->conn_man, data->mailbox, NULL, &local_error)) { + c ('*', "%s: Failed to refresh mailbox '%s': %s\n", G_STRFUNC, + camel_imapx_mailbox_get_name (data->mailbox), + local_error ? local_error->message : "Unknown error"); + } + + mailbox_refresh_data_free (data); + g_clear_error (&local_error); + + return NULL; +} static void -imapx_conn_update_select (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - CamelIMAPXConnManager *con_man); -static void -imapx_conn_mailbox_closed (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - CamelIMAPXConnManager *con_man); +imapx_conn_manager_refresh_mailbox_cb (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + CamelIMAPXConnManager *conn_man) +{ + MailboxRefreshData *data; + GThread *thread; + GError *local_error = NULL; + + g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is)); + g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man)); + + data = g_new0 (MailboxRefreshData, 1); + data->conn_man = g_object_ref (conn_man); + data->mailbox = g_object_ref (mailbox); + + thread = g_thread_try_new (NULL, imapx_conn_manager_idle_mailbox_refresh_thread, data, &local_error); + if (!thread) { + g_warning ("%s: Failed to create IDLE mailbox refresh thread: %s", G_STRFUNC, local_error ? local_error->message : "Unknown error"); + mailbox_refresh_data_free (data); + } else { + g_thread_unref (thread); + } + + g_clear_error (&local_error); +} static ConnectionInfo * connection_info_new (CamelIMAPXServer *is) { ConnectionInfo *cinfo; - GHashTable *folder_names; - - folder_names = g_hash_table_new_full ( - (GHashFunc) g_str_hash, - (GEqualFunc) g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) NULL); cinfo = g_slice_new0 (ConnectionInfo); g_mutex_init (&cinfo->lock); cinfo->is = g_object_ref (is); - cinfo->folder_names = folder_names; - cinfo->shutdown_error = NULL; cinfo->ref_count = 1; return cinfo; @@ -125,247 +205,348 @@ connection_info_unref (ConnectionInfo *c g_return_if_fail (cinfo->ref_count > 0); if (g_atomic_int_dec_and_test (&cinfo->ref_count)) { - camel_imapx_server_shutdown (cinfo->is, cinfo->shutdown_error); - g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, imapx_conn_shutdown, NULL); - g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, imapx_conn_update_select, NULL); - g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, imapx_conn_mailbox_closed, NULL); + if (cinfo->refresh_mailbox_handler_id) + g_signal_handler_disconnect (cinfo->is, cinfo->refresh_mailbox_handler_id); g_mutex_clear (&cinfo->lock); g_object_unref (cinfo->is); - g_hash_table_destroy (cinfo->folder_names); - g_free (cinfo->selected_folder); - g_clear_error (&cinfo->shutdown_error); g_slice_free (ConnectionInfo, cinfo); } } -static void -connection_info_cancel_and_unref (ConnectionInfo *cinfo) -{ - g_return_if_fail (cinfo != NULL); - g_return_if_fail (cinfo->ref_count > 0); - - g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, imapx_conn_shutdown, NULL); - g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, imapx_conn_update_select, NULL); - g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, imapx_conn_mailbox_closed, NULL); - camel_imapx_server_shutdown (cinfo->is, cinfo->shutdown_error); - connection_info_unref (cinfo); -} - static gboolean -connection_info_is_available (ConnectionInfo *cinfo) +connection_info_try_reserve (ConnectionInfo *cinfo) { - gboolean available; + gboolean reserved = FALSE; g_return_val_if_fail (cinfo != NULL, FALSE); g_mutex_lock (&cinfo->lock); - /* Available means it's not tracking any folder names or no jobs are running. */ - available = (g_hash_table_size (cinfo->folder_names) == 0) || - camel_imapx_server_get_command_count (cinfo->is) == 0; + if (!cinfo->busy) { + cinfo->busy = TRUE; + reserved = TRUE; + } g_mutex_unlock (&cinfo->lock); - return available; + return reserved; } static gboolean -connection_info_has_folder_name (ConnectionInfo *cinfo, - const gchar *folder_name) +connection_info_get_busy (ConnectionInfo *cinfo) { - gpointer value; + gboolean busy; g_return_val_if_fail (cinfo != NULL, FALSE); - if (folder_name == NULL) - return FALSE; - g_mutex_lock (&cinfo->lock); - value = g_hash_table_lookup (cinfo->folder_names, folder_name); + busy = cinfo->busy; g_mutex_unlock (&cinfo->lock); - return (value != NULL); + return busy; } static void -connection_info_insert_folder_name (ConnectionInfo *cinfo, - const gchar *folder_name) +connection_info_set_busy (ConnectionInfo *cinfo, + gboolean busy) { g_return_if_fail (cinfo != NULL); - g_return_if_fail (folder_name != NULL); g_mutex_lock (&cinfo->lock); - g_hash_table_insert ( - cinfo->folder_names, - g_strdup (folder_name), - GINT_TO_POINTER (1)); + cinfo->busy = busy; g_mutex_unlock (&cinfo->lock); } static void -connection_info_remove_folder_name (ConnectionInfo *cinfo, - const gchar *folder_name) +imapx_conn_manager_signal_busy_connections (CamelIMAPXConnManager *conn_man) { - g_return_if_fail (cinfo != NULL); - g_return_if_fail (folder_name != NULL); + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man)); - g_mutex_lock (&cinfo->lock); + g_mutex_lock (&conn_man->priv->busy_connections_lock); + g_cond_broadcast (&conn_man->priv->busy_connections_cond); + g_mutex_unlock (&conn_man->priv->busy_connections_lock); +} + +static void +imapx_conn_manager_unmark_busy (CamelIMAPXConnManager *conn_man, + ConnectionInfo *cinfo) +{ + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man)); + g_return_if_fail (cinfo != NULL); + g_return_if_fail (connection_info_get_busy (cinfo)); - g_hash_table_remove (cinfo->folder_names, folder_name); + connection_info_set_busy (cinfo, FALSE); - g_mutex_unlock (&cinfo->lock); + imapx_conn_manager_signal_busy_connections (conn_man); } -static gchar * -connection_info_dup_selected_folder (ConnectionInfo *cinfo) +static gboolean +imapx_conn_manager_remove_info (CamelIMAPXConnManager *conn_man, + ConnectionInfo *cinfo) { - gchar *selected_folder; + GList *list, *link; + gboolean removed = FALSE; - g_return_val_if_fail (cinfo != NULL, NULL); + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + g_return_val_if_fail (cinfo != NULL, FALSE); - g_mutex_lock (&cinfo->lock); + CON_WRITE_LOCK (conn_man); - selected_folder = g_strdup (cinfo->selected_folder); + list = conn_man->priv->connections; + link = g_list_find (list, cinfo); - g_mutex_unlock (&cinfo->lock); + if (link != NULL) { + list = g_list_delete_link (list, link); + connection_info_unref (cinfo); + removed = TRUE; + } + + conn_man->priv->connections = list; + + CON_WRITE_UNLOCK (conn_man); + + if (removed) + imapx_conn_manager_signal_busy_connections (conn_man); - return selected_folder; + return removed; } static void -connection_info_set_selected_folder (ConnectionInfo *cinfo, - const gchar *selected_folder) +imapx_conn_manager_cancel_pending_connections (CamelIMAPXConnManager *conn_man) { - g_return_if_fail (cinfo != NULL); + GSList *link; - g_mutex_lock (&cinfo->lock); + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man)); - g_free (cinfo->selected_folder); - cinfo->selected_folder = g_strdup (selected_folder); + g_mutex_lock (&conn_man->priv->pending_connections_lock); + for (link = conn_man->priv->pending_connections; link; link = g_slist_next (link)) { + GCancellable *cancellable = link->data; - g_mutex_unlock (&cinfo->lock); + if (cancellable) + g_cancellable_cancel (cancellable); + } + g_mutex_unlock (&conn_man->priv->pending_connections_lock); } static void -connection_info_set_shutdown_error (ConnectionInfo *cinfo, - const GError *shutdown_error) +imapx_conn_manager_abort_jobs (CamelIMAPXConnManager *conn_man) { - g_return_if_fail (cinfo != NULL); + GSList *link; - g_mutex_lock (&cinfo->lock); + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man)); - if (cinfo->shutdown_error != shutdown_error) { - g_clear_error (&cinfo->shutdown_error); - if (shutdown_error) - cinfo->shutdown_error = g_error_copy (shutdown_error); + JOB_QUEUE_LOCK (conn_man); + + for (link = conn_man->priv->job_queue; link; link = g_slist_next (link)) { + CamelIMAPXJob *job = link->data; + + if (job) + camel_imapx_job_abort (job); } - g_mutex_unlock (&cinfo->lock); + JOB_QUEUE_UNLOCK (conn_man); } -static GList * -imapx_conn_manager_list_info (CamelIMAPXConnManager *con_man) +static CamelFolder * +imapx_conn_manager_ref_folder_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) { - GList *list; - - g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL); + CamelIMAPXStore *store; + CamelFolder *folder; + gchar *folder_path; - CON_READ_LOCK (con_man); + store = camel_imapx_conn_manager_ref_store (conn_man); + folder_path = camel_imapx_mailbox_dup_folder_path (mailbox); - list = g_list_copy (con_man->priv->connections); - g_list_foreach (list, (GFunc) connection_info_ref, NULL); + folder = camel_store_get_folder_sync (CAMEL_STORE (store), folder_path, 0, cancellable, NULL); + if (folder) + camel_imapx_folder_set_mailbox (CAMEL_IMAPX_FOLDER (folder), mailbox); - CON_READ_UNLOCK (con_man); + g_free (folder_path); + g_clear_object (&store); - return list; + return folder; } -static ConnectionInfo * -imapx_conn_manager_lookup_info (CamelIMAPXConnManager *con_man, - CamelIMAPXServer *is) +static void +imapx_conn_manager_inc_mailbox_hash (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GHashTable *mailboxes_hash) { - ConnectionInfo *cinfo = NULL; - GList *list, *link; + gint count; - g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL); - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man)); + g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); + g_return_if_fail (mailboxes_hash != NULL); - CON_READ_LOCK (con_man); + g_mutex_lock (&conn_man->priv->busy_mailboxes_lock); - list = con_man->priv->connections; + count = GPOINTER_TO_INT (g_hash_table_lookup (mailboxes_hash, mailbox)); + count++; - for (link = list; link != NULL; link = g_list_next (link)) { - ConnectionInfo *candidate = link->data; + g_hash_table_insert (mailboxes_hash, g_object_ref (mailbox), GINT_TO_POINTER (count)); - if (candidate->is == is) { - cinfo = connection_info_ref (candidate); - break; - } + g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock); +} + +static void +imapx_conn_manager_dec_mailbox_hash (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GHashTable *mailboxes_hash) +{ + gint count; + + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man)); + g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); + g_return_if_fail (mailboxes_hash != NULL); + + g_mutex_lock (&conn_man->priv->busy_mailboxes_lock); + + count = GPOINTER_TO_INT (g_hash_table_lookup (mailboxes_hash, mailbox)); + if (!count) { + g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock); + return; } - CON_READ_UNLOCK (con_man); + count--; - return cinfo; + if (count) + g_hash_table_insert (mailboxes_hash, g_object_ref (mailbox), GINT_TO_POINTER (count)); + else + g_hash_table_remove (mailboxes_hash, mailbox); + + g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock); } static gboolean -imapx_conn_manager_remove_info (CamelIMAPXConnManager *con_man, - ConnectionInfo *cinfo) +imapx_conn_manager_is_mailbox_hash (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GHashTable *mailboxes_hash) { - GList *list, *link; - gboolean removed = FALSE; + gint count; - g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), FALSE); - g_return_val_if_fail (cinfo != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + g_return_val_if_fail (mailboxes_hash != NULL, FALSE); - CON_WRITE_LOCK (con_man); + g_mutex_lock (&conn_man->priv->busy_mailboxes_lock); - list = con_man->priv->connections; - link = g_list_find (list, cinfo); + count = GPOINTER_TO_INT (g_hash_table_lookup (mailboxes_hash, mailbox)); - if (link != NULL) { - list = g_list_delete_link (list, link); - connection_info_unref (cinfo); - removed = TRUE; - } + g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock); + + return count > 0; +} - con_man->priv->connections = list; +static void +imapx_conn_manager_clear_mailboxes_hashes (CamelIMAPXConnManager *conn_man) +{ + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man)); - CON_WRITE_UNLOCK (con_man); + g_mutex_lock (&conn_man->priv->busy_mailboxes_lock); - return removed; + g_hash_table_remove_all (conn_man->priv->busy_mailboxes); + g_hash_table_remove_all (conn_man->priv->idle_mailboxes); + + g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock); } static void -imax_conn_manager_cancel_pending_connections (CamelIMAPXConnManager *con_man) +imapx_conn_manager_inc_mailbox_busy (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox) { - GSList *link; + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man)); + g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); - g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man)); + imapx_conn_manager_inc_mailbox_hash (conn_man, mailbox, conn_man->priv->busy_mailboxes); +} - g_mutex_lock (&con_man->priv->pending_connections_lock); - for (link = con_man->priv->pending_connections; link; link = g_slist_next (link)) { - GCancellable *cancellable = link->data; +static void +imapx_conn_manager_dec_mailbox_busy (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox) +{ + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man)); + g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); - if (cancellable) - g_cancellable_cancel (cancellable); - } - g_mutex_unlock (&con_man->priv->pending_connections_lock); + imapx_conn_manager_dec_mailbox_hash (conn_man, mailbox, conn_man->priv->busy_mailboxes); +} + +static gboolean +imapx_conn_manager_is_mailbox_busy (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox) +{ + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + + return imapx_conn_manager_is_mailbox_hash (conn_man, mailbox, conn_man->priv->busy_mailboxes); +} + +static void +imapx_conn_manager_inc_mailbox_idle (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox) +{ + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man)); + g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); + + imapx_conn_manager_inc_mailbox_hash (conn_man, mailbox, conn_man->priv->idle_mailboxes); +} + +static void +imapx_conn_manager_dec_mailbox_idle (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox) +{ + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man)); + g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); + + imapx_conn_manager_dec_mailbox_hash (conn_man, mailbox, conn_man->priv->idle_mailboxes); +} + +static gboolean +imapx_conn_manager_is_mailbox_idle (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox) +{ + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + + return imapx_conn_manager_is_mailbox_hash (conn_man, mailbox, conn_man->priv->idle_mailboxes); +} + +static gboolean +imapx_conn_manager_has_inbox_idle (CamelIMAPXConnManager *conn_man) +{ + CamelIMAPXStore *imapx_store; + CamelIMAPXMailbox *inbox_mailbox; + gboolean is_idle; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + + imapx_store = camel_imapx_conn_manager_ref_store (conn_man); + inbox_mailbox = imapx_store ? camel_imapx_store_ref_mailbox (imapx_store, "INBOX") : NULL; + + g_clear_object (&imapx_store); + + is_idle = inbox_mailbox && imapx_conn_manager_is_mailbox_idle (conn_man, inbox_mailbox); + + g_clear_object (&inbox_mailbox); + + return is_idle; } static void -imapx_conn_manager_set_store (CamelIMAPXConnManager *con_man, +imapx_conn_manager_set_store (CamelIMAPXConnManager *conn_man, CamelStore *store) { g_return_if_fail (CAMEL_IS_STORE (store)); - g_weak_ref_set (&con_man->priv->store, store); + g_weak_ref_set (&conn_man->priv->store, store); } static void @@ -406,18 +587,24 @@ imapx_conn_manager_get_property (GObject static void imapx_conn_manager_dispose (GObject *object) { - CamelIMAPXConnManagerPrivate *priv; + CamelIMAPXConnManager *conn_man; - priv = CAMEL_IMAPX_CONN_MANAGER_GET_PRIVATE (object); + conn_man = CAMEL_IMAPX_CONN_MANAGER (object); + + imapx_conn_manager_cancel_pending_connections (conn_man); + imapx_conn_manager_abort_jobs (conn_man); g_list_free_full ( - priv->connections, + conn_man->priv->connections, (GDestroyNotify) connection_info_unref); - priv->connections = NULL; + conn_man->priv->connections = NULL; - imax_conn_manager_cancel_pending_connections (CAMEL_IMAPX_CONN_MANAGER (object)); + g_weak_ref_set (&conn_man->priv->store, NULL); - g_weak_ref_set (&priv->store, NULL); + g_mutex_lock (&conn_man->priv->busy_mailboxes_lock); + g_hash_table_remove_all (conn_man->priv->busy_mailboxes); + g_hash_table_remove_all (conn_man->priv->idle_mailboxes); + g_mutex_unlock (&conn_man->priv->busy_mailboxes_lock); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (camel_imapx_conn_manager_parent_class)->dispose (object); @@ -431,10 +618,17 @@ imapx_conn_manager_finalize (GObject *ob priv = CAMEL_IMAPX_CONN_MANAGER_GET_PRIVATE (object); g_warn_if_fail (priv->pending_connections == NULL); + g_warn_if_fail (priv->job_queue == NULL); g_rw_lock_clear (&priv->rw_lock); + g_rec_mutex_clear (&priv->job_queue_lock); g_mutex_clear (&priv->pending_connections_lock); + g_mutex_clear (&priv->busy_connections_lock); + g_cond_clear (&priv->busy_connections_cond); g_weak_ref_clear (&priv->store); + g_mutex_clear (&priv->busy_mailboxes_lock); + g_hash_table_destroy (priv->busy_mailboxes); + g_hash_table_destroy (priv->idle_mailboxes); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (camel_imapx_conn_manager_parent_class)->finalize (object); @@ -459,546 +653,2147 @@ camel_imapx_conn_manager_class_init (Cam g_param_spec_object ( "store", "Store", - "The CamelStore to which we belong", - CAMEL_TYPE_STORE, + "The CamelIMAPXStore to which we belong", + CAMEL_TYPE_IMAPX_STORE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); -} - -static void -camel_imapx_conn_manager_init (CamelIMAPXConnManager *con_man) -{ - con_man->priv = CAMEL_IMAPX_CONN_MANAGER_GET_PRIVATE (con_man); - g_rw_lock_init (&con_man->priv->rw_lock); - g_mutex_init (&con_man->priv->pending_connections_lock); - g_weak_ref_init (&con_man->priv->store, NULL); + signals[CONNECTION_CREATED] = g_signal_new ( + "connection-created", + G_OBJECT_CLASS_TYPE (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (CamelIMAPXConnManagerClass, connection_created), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + CAMEL_TYPE_IMAPX_SERVER); } static void -imapx_conn_shutdown (CamelIMAPXServer *is, - const GError *error, - CamelIMAPXConnManager *con_man) +camel_imapx_conn_manager_init (CamelIMAPXConnManager *conn_man) { - ConnectionInfo *cinfo; - - /* Returns a new ConnectionInfo reference. */ - cinfo = imapx_conn_manager_lookup_info (con_man, is); + conn_man->priv = CAMEL_IMAPX_CONN_MANAGER_GET_PRIVATE (conn_man); - if (cinfo != NULL) { - imapx_conn_manager_remove_info (con_man, cinfo); - connection_info_unref (cinfo); - } - - /* If one connection ends with this error, then it means all - other opened connections also may end with the same error, - thus better to kill them all from the list of connections. - */ - if (g_error_matches (error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - camel_imapx_conn_manager_close_connections (con_man, error); - } + g_rw_lock_init (&conn_man->priv->rw_lock); + g_rec_mutex_init (&conn_man->priv->job_queue_lock); + g_mutex_init (&conn_man->priv->pending_connections_lock); + g_mutex_init (&conn_man->priv->busy_connections_lock); + g_cond_init (&conn_man->priv->busy_connections_cond); + g_weak_ref_init (&conn_man->priv->store, NULL); + g_mutex_init (&conn_man->priv->busy_mailboxes_lock); + + conn_man->priv->last_tagprefix = 'A' - 1; + conn_man->priv->busy_mailboxes = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); + conn_man->priv->idle_mailboxes = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); } -static void -imapx_conn_update_select (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - CamelIMAPXConnManager *con_man) +static gchar +imapx_conn_manager_get_next_free_tagprefix_unlocked (CamelIMAPXConnManager *conn_man) { - ConnectionInfo *cinfo; - gchar *old_selected_folder, *selected_folder = NULL; + gchar adept; + gint ii; + GList *iter; - /* Returns a new ConnectionInfo reference. */ - cinfo = imapx_conn_manager_lookup_info (con_man, is); + adept = conn_man->priv->last_tagprefix + 1; - if (cinfo == NULL) - return; + /* the 'Z' is dedicated to auth types query */ + if (adept >= 'Z') + adept = 'A'; + else if (adept < 'A') + adept = 'A'; + + for (ii = 0; ii < 26; ii++) { + for (iter = conn_man->priv->connections; iter; iter = g_list_next (iter)) { + ConnectionInfo *cinfo = iter->data; - old_selected_folder = connection_info_dup_selected_folder (cinfo); + if (!cinfo || !cinfo->is) + continue; - if (old_selected_folder != NULL) { - if (!camel_imapx_server_folder_name_in_jobs (is, old_selected_folder)) { - connection_info_remove_folder_name (cinfo, old_selected_folder); - c (is->tagprefix, "Removed folder %s from connection folder list - select changed \n", old_selected_folder); + if (camel_imapx_server_get_tagprefix (cinfo->is) == adept) + break; } - g_free (old_selected_folder); + /* Read all current active connections and none has the same tag prefix */ + if (!iter) + break; + + adept++; + if (adept >= 'Z') + adept = 'A'; } - if (mailbox) - selected_folder = camel_imapx_mailbox_dup_folder_path (mailbox); - connection_info_set_selected_folder (cinfo, selected_folder); - g_free (selected_folder); + g_return_val_if_fail (adept >= 'A' && adept < 'Z', 'Z'); - connection_info_unref (cinfo); -} + conn_man->priv->last_tagprefix = adept; -static void -imapx_conn_mailbox_closed (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - CamelIMAPXConnManager *con_man) -{ - imapx_conn_update_select (is, NULL, con_man); + return adept; } -/* This should find a connection if the slots are full, returns NULL if there are slots available for a new connection for a folder */ -static CamelIMAPXServer * -imapx_find_connection_unlocked (CamelIMAPXConnManager *con_man, - const gchar *folder_name, - gboolean for_expensive_job) +static ConnectionInfo * +imapx_create_new_connection_unlocked (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) { - CamelStore *store; - CamelSettings *settings; CamelIMAPXServer *is = NULL; + CamelIMAPXStore *imapx_store; ConnectionInfo *cinfo = NULL; - GList *list, *link; - guint concurrent_connections, opened_connections, expensive_connections = 0; - guint min_jobs = G_MAXUINT; + gboolean success; /* Caller must be holding CON_WRITE_LOCK. */ - store = camel_imapx_conn_manager_ref_store (con_man); - g_return_val_if_fail (store != NULL, NULL); + /* Check if we got cancelled while we were waiting. */ + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return NULL; - settings = camel_service_ref_settings (CAMEL_SERVICE (store)); + imapx_store = camel_imapx_conn_manager_ref_store (conn_man); + g_return_val_if_fail (imapx_store != NULL, NULL); - concurrent_connections = - camel_imapx_settings_get_concurrent_connections ( - CAMEL_IMAPX_SETTINGS (settings)); - - if (con_man->priv->limit_max_connections > 0 && - con_man->priv->limit_max_connections < concurrent_connections) - concurrent_connections = con_man->priv->limit_max_connections; + is = camel_imapx_server_new (imapx_store); + camel_imapx_server_set_tagprefix (is, imapx_conn_manager_get_next_free_tagprefix_unlocked (conn_man)); - g_object_unref (settings); + g_signal_emit (conn_man, signals[CONNECTION_CREATED], 0, is); - /* XXX Have a dedicated connection for INBOX ? */ + /* XXX As part of the connect operation the CamelIMAPXServer will + * have to call camel_session_authenticate_sync(), but it has + * no way to pass itself through in that call so the service + * knows which CamelIMAPXServer is trying to authenticate. + * + * IMAPX is the only provider that does multiple connections + * like this, so I didn't want to pollute the CamelSession and + * CamelService authentication APIs with an extra argument. + * Instead we do this little hack so the service knows which + * CamelIMAPXServer to act on in its authenticate_sync() method. + * + * Because we're holding the CAMEL_SERVICE_REC_CONNECT_LOCK + * we should not have multiple IMAPX connections trying to + * authenticate at once, so this should be thread-safe. + */ + camel_imapx_store_set_connecting_server (imapx_store, is, conn_man->priv->connections != NULL); + success = camel_imapx_server_connect_sync (is, cancellable, error); + camel_imapx_store_set_connecting_server (imapx_store, NULL, FALSE); - opened_connections = g_list_length (con_man->priv->connections); - list = con_man->priv->connections; + if (!success) + goto exit; - /* If a folder was not given, find the least-busy connection. */ - if (folder_name == NULL) { - goto least_busy; - } + cinfo = connection_info_new (is); - /* First try to find a connection already handling this folder. */ - for (link = list; link != NULL; link = g_list_next (link)) { - ConnectionInfo *candidate = link->data; + cinfo->refresh_mailbox_handler_id = g_signal_connect ( + is, "refresh-mailbox", G_CALLBACK (imapx_conn_manager_refresh_mailbox_cb), conn_man); - if (camel_imapx_server_has_expensive_command (candidate->is)) - expensive_connections++; + /* Takes ownership of the ConnectionInfo. */ + conn_man->priv->connections = g_list_append (conn_man->priv->connections, cinfo); - if (connection_info_has_folder_name (candidate, folder_name) && camel_imapx_server_is_connected (candidate->is) && - (opened_connections >= concurrent_connections || for_expensive_job || !camel_imapx_server_has_expensive_command (candidate->is))) { - if (cinfo) { - /* group expensive jobs into one connection */ - if (for_expensive_job && camel_imapx_server_has_expensive_command (cinfo->is)) - continue; + c (camel_imapx_server_get_tagprefix (is), "Created new connection %p (server:%p) for %s; total connections %d\n", + cinfo, cinfo->is, + mailbox ? camel_imapx_mailbox_get_name (mailbox) : "[null]", + g_list_length (conn_man->priv->connections)); - if (!for_expensive_job && camel_imapx_server_get_command_count (cinfo->is) < camel_imapx_server_get_command_count (candidate->is)) - continue; +exit: + g_object_unref (imapx_store); + g_clear_object (&is); - connection_info_unref (cinfo); - } + return cinfo; +} - cinfo = connection_info_ref (candidate); - if (for_expensive_job && camel_imapx_server_has_expensive_command (cinfo->is)) - goto exit; - } - } +static gint +imapx_conn_manager_get_max_connections (CamelIMAPXConnManager *conn_man) +{ + CamelIMAPXStore *imapx_store; + CamelSettings *settings; + gint max_connections; - least_busy: - if (for_expensive_job) { - /* allow only half connections being with expensive operations */ - if (expensive_connections > 0 && - expensive_connections < concurrent_connections / 2 && - opened_connections < concurrent_connections) - goto exit; + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), -1); - /* cinfo here doesn't have any expensive command, thus ignore it */ - if (cinfo) { - connection_info_unref (cinfo); - cinfo = NULL; - } + imapx_store = camel_imapx_conn_manager_ref_store (conn_man); + if (!imapx_store) + return -1; - /* Pick the connection with the least number of jobs in progress among those with expensive jobs. */ - for (link = list; link != NULL; link = g_list_next (link)) { - ConnectionInfo *candidate = link->data; - guint jobs; + settings = camel_service_ref_settings (CAMEL_SERVICE (imapx_store)); - if (!camel_imapx_server_is_connected (candidate->is) || - !camel_imapx_server_has_expensive_command (candidate->is)) - continue; + max_connections = camel_imapx_settings_get_concurrent_connections (CAMEL_IMAPX_SETTINGS (settings)); - jobs = camel_imapx_server_get_command_count (candidate->is); + if (conn_man->priv->limit_max_connections > 0 && + conn_man->priv->limit_max_connections < max_connections) + max_connections = conn_man->priv->limit_max_connections; - if (cinfo == NULL) { - cinfo = connection_info_ref (candidate); - min_jobs = jobs; - - } else if (jobs < min_jobs) { - connection_info_unref (cinfo); - cinfo = connection_info_ref (candidate); - min_jobs = jobs; - } - } + g_object_unref (settings); + g_object_unref (imapx_store); - if (cinfo) - goto exit; - } + return max_connections > 0 ? max_connections : 1; +} - /* Next try to find a connection not handling any folders. */ - for (link = list; link != NULL; link = g_list_next (link)) { - ConnectionInfo *candidate = link->data; +static void +imapx_conn_manager_connection_wait_cancelled_cb (GCancellable *cancellable, + CamelIMAPXConnManager *conn_man) +{ + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man)); - if (camel_imapx_server_is_connected (candidate->is) && - connection_info_is_available (candidate)) { - if (cinfo) - connection_info_unref (cinfo); - cinfo = connection_info_ref (candidate); - goto exit; - } - } + imapx_conn_manager_signal_busy_connections (conn_man); +} - /* open a new connection, if there is a room for it */ - if (opened_connections < concurrent_connections && (!for_expensive_job || opened_connections < concurrent_connections / 2)) { - if (cinfo && camel_imapx_server_get_command_count (cinfo->is) != 0) { - connection_info_unref (cinfo); - cinfo = NULL; - } - goto exit; - } else { - if (cinfo) - min_jobs = camel_imapx_server_get_command_count (cinfo->is); - } +static ConnectionInfo * +camel_imapx_conn_manager_ref_connection (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + gboolean *out_is_new_connection, + GCancellable *cancellable, + GError **error) +{ + ConnectionInfo *cinfo = NULL; + CamelIMAPXStore *imapx_store; + CamelSession *session; + GError *local_error = NULL; - /* Pick the connection with the least number of jobs in progress. */ - for (link = list; link != NULL; link = g_list_next (link)) { - ConnectionInfo *candidate = link->data; - gint n_commands; + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), NULL); - if (!camel_imapx_server_is_connected (candidate->is)) - continue; + if (out_is_new_connection) + *out_is_new_connection = FALSE; - n_commands = camel_imapx_server_get_command_count (candidate->is); + imapx_store = camel_imapx_conn_manager_ref_store (conn_man); + if (!imapx_store) + return NULL; - if (cinfo == NULL) { - cinfo = connection_info_ref (candidate); - min_jobs = n_commands; + session = camel_service_ref_session (CAMEL_SERVICE (imapx_store)); - } else if (n_commands < min_jobs) { - connection_info_unref (cinfo); - cinfo = connection_info_ref (candidate); - min_jobs = n_commands; + if (camel_offline_store_get_online (CAMEL_OFFLINE_STORE (imapx_store)) && + session && camel_session_get_online (session)) { + + g_mutex_lock (&conn_man->priv->pending_connections_lock); + if (cancellable) { + g_object_ref (cancellable); + } else { + cancellable = g_cancellable_new (); + } + conn_man->priv->pending_connections = g_slist_prepend (conn_man->priv->pending_connections, cancellable); + g_mutex_unlock (&conn_man->priv->pending_connections_lock); + + /* Hold the writer lock while we requisition a CamelIMAPXServer + * to prevent other threads from adding or removing connections. */ + CON_READ_LOCK (conn_man); + + /* Check if we've got cancelled while waiting for the lock. */ + while (!cinfo && !g_cancellable_set_error_if_cancelled (cancellable, &local_error)) { + gint opened_connections, max_connections; + GList *link; + + for (link = conn_man->priv->connections; link; link = g_list_next (link)) { + ConnectionInfo *candidate = link->data; + + if (candidate && connection_info_try_reserve (candidate)) { + cinfo = connection_info_ref (candidate); + break; + } + } + + if (cinfo) + break; + + opened_connections = g_list_length (conn_man->priv->connections); + max_connections = imapx_conn_manager_get_max_connections (conn_man); + + if (max_connections <= 0) + break; + + if (!cinfo && opened_connections < max_connections) { + GError *local_error_2 = NULL; + + CON_READ_UNLOCK (conn_man); + CON_WRITE_LOCK (conn_man); + cinfo = imapx_create_new_connection_unlocked (conn_man, mailbox, cancellable, &local_error_2); + if (cinfo) + connection_info_set_busy (cinfo, TRUE); + CON_WRITE_UNLOCK (conn_man); + CON_READ_LOCK (conn_man); + + if (!cinfo) { + gboolean limit_connections = + g_error_matches (local_error_2, CAMEL_IMAPX_SERVER_ERROR, + CAMEL_IMAPX_SERVER_ERROR_CONCURRENT_CONNECT_FAILED) && + conn_man->priv->connections; + + c ('*', "Failed to open a new connection, while having %d opened, with error: %s; will limit connections: %s\n", + g_list_length (conn_man->priv->connections), + local_error_2 ? local_error_2->message : "Unknown error", + limit_connections ? "yes" : "no"); + + if (limit_connections) { + /* limit to one-less than current connection count - be nice to the server */ + conn_man->priv->limit_max_connections = g_list_length (conn_man->priv->connections) - 1; + if (!conn_man->priv->limit_max_connections) + conn_man->priv->limit_max_connections = 1; + + g_clear_error (&local_error_2); + } else { + if (local_error_2) + g_propagate_error (&local_error, local_error_2); + break; + } + } else { + connection_info_ref (cinfo); + + if (out_is_new_connection) + *out_is_new_connection = TRUE; + } + } + + if (!cinfo) { + gulong handler_id; + + CON_READ_UNLOCK (conn_man); + + handler_id = g_cancellable_connect (cancellable, G_CALLBACK (imapx_conn_manager_connection_wait_cancelled_cb), conn_man, NULL); + + g_mutex_lock (&conn_man->priv->busy_connections_lock); + g_cond_wait (&conn_man->priv->busy_connections_cond, &conn_man->priv->busy_connections_lock); + g_mutex_unlock (&conn_man->priv->busy_connections_lock); + + if (handler_id) + g_cancellable_disconnect (cancellable, handler_id); + + CON_READ_LOCK (conn_man); + } + } + + CON_READ_UNLOCK (conn_man); + + g_mutex_lock (&conn_man->priv->pending_connections_lock); + conn_man->priv->pending_connections = g_slist_remove (conn_man->priv->pending_connections, cancellable); + g_object_unref (cancellable); + g_mutex_unlock (&conn_man->priv->pending_connections_lock); + } + + g_clear_object (&imapx_store); + g_clear_object (&session); + + if (!cinfo && (!local_error || local_error->domain == G_RESOLVER_ERROR)) { + if (local_error) { + g_set_error ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_UNAVAILABLE, + _("You must be working online to complete this operation (%s)"), + local_error->message); + + g_clear_error (&local_error); + } else { + g_set_error_literal ( + &local_error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_UNAVAILABLE, + _("You must be working online to complete this operation")); + } + } + + if (local_error) + g_propagate_error (error, local_error); + + return cinfo; +} + +/****************************/ + +CamelIMAPXConnManager * +camel_imapx_conn_manager_new (CamelStore *store) +{ + g_return_val_if_fail (CAMEL_IS_STORE (store), NULL); + + return g_object_new ( + CAMEL_TYPE_IMAPX_CONN_MANAGER, "store", store, NULL); +} + +CamelIMAPXStore * +camel_imapx_conn_manager_ref_store (CamelIMAPXConnManager *conn_man) +{ + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), NULL); + + return g_weak_ref_get (&conn_man->priv->store); +} + +gboolean +camel_imapx_conn_manager_connect_sync (CamelIMAPXConnManager *conn_man, + GCancellable *cancellable, + GError **error) +{ + ConnectionInfo *cinfo; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + + CON_READ_LOCK (conn_man); + if (conn_man->priv->connections) { + CON_READ_UNLOCK (conn_man); + return TRUE; + } + CON_READ_UNLOCK (conn_man); + + imapx_conn_manager_clear_mailboxes_hashes (conn_man); + + cinfo = camel_imapx_conn_manager_ref_connection (conn_man, NULL, NULL, cancellable, error); + if (cinfo) { + imapx_conn_manager_unmark_busy (conn_man, cinfo); + connection_info_unref (cinfo); + } + + return cinfo != NULL; +} + +gboolean +camel_imapx_conn_manager_disconnect_sync (CamelIMAPXConnManager *conn_man, + GCancellable *cancellable, + GError **error) +{ + GList *link, *connections; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + + /* Do this before acquiring the write lock, because any pending + connection holds the write lock, thus makes this request starve. */ + imapx_conn_manager_cancel_pending_connections (conn_man); + imapx_conn_manager_abort_jobs (conn_man); + + CON_WRITE_LOCK (conn_man); + + c ('*', "Disconnecting all %d connections\n", g_list_length (conn_man->priv->connections)); + + connections = conn_man->priv->connections; + conn_man->priv->connections = NULL; + + CON_WRITE_UNLOCK (conn_man); + + for (link = connections; link; link = g_list_next (link)) { + ConnectionInfo *cinfo = link->data; + GError *local_error = NULL; + + if (!cinfo) + continue; + + if (!camel_imapx_server_disconnect_sync (cinfo->is, cancellable, &local_error)) { + c (camel_imapx_server_get_tagprefix (cinfo->is), " Failed to disconnect from the server: %s\n", + local_error ? local_error->message : "Unknown error"); + } + + connection_info_unref (cinfo); + g_clear_error (&local_error); + } + + g_list_free (connections); + + imapx_conn_manager_clear_mailboxes_hashes (conn_man); + + return TRUE; +} + +static gboolean +imapx_conn_manager_should_wait_for (CamelIMAPXConnManager *conn_man, + CamelIMAPXJob *new_job, + CamelIMAPXJob *queued_job) +{ + guint32 job_kind; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + g_return_val_if_fail (queued_job != NULL, FALSE); + + if (camel_imapx_job_get_kind (new_job) == CAMEL_IMAPX_JOB_GET_MESSAGE) + return FALSE; + + job_kind = camel_imapx_job_get_kind (queued_job); + + /* List jobs with high priority. */ + return job_kind == CAMEL_IMAPX_JOB_GET_MESSAGE || + job_kind == CAMEL_IMAPX_JOB_COPY_MESSAGE || + job_kind == CAMEL_IMAPX_JOB_MOVE_MESSAGE || + job_kind == CAMEL_IMAPX_JOB_EXPUNGE; +} + +gboolean +camel_imapx_conn_manager_run_job_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXJob *job, + CamelIMAPXJobMatchesFunc finish_before_job, + GCancellable *cancellable, + GError **error) +{ + GSList *link; + ConnectionInfo *cinfo; + gboolean success = FALSE, is_new_connection = FALSE; + GError *local_error = NULL; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + g_return_val_if_fail (job != NULL, FALSE); + + JOB_QUEUE_LOCK (conn_man); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + JOB_QUEUE_UNLOCK (conn_man); + return FALSE; + } + + link = conn_man->priv->job_queue; + while (link) { + CamelIMAPXJob *queued_job = link->data; + gboolean matches; + + g_warn_if_fail (queued_job != NULL); + g_warn_if_fail (queued_job != job); + + if (!queued_job) { + link = g_slist_next (link); + continue; + } + + matches = camel_imapx_job_matches (job, queued_job); + if (matches || (finish_before_job && finish_before_job (job, queued_job)) || + imapx_conn_manager_should_wait_for (conn_man, job, queued_job)) { + camel_imapx_job_ref (queued_job); + + JOB_QUEUE_UNLOCK (conn_man); + + camel_imapx_job_wait_sync (queued_job, cancellable); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + camel_imapx_job_unref (queued_job); + return FALSE; + } + + if (matches) { + gpointer result = NULL; + GDestroyNotify destroy_result = NULL; + + /* Do not inherit cancelled errors, just try again */ + if (!camel_imapx_job_was_cancelled (queued_job) && + camel_imapx_job_copy_result (queued_job, &success, &result, &local_error, &destroy_result)) { + camel_imapx_job_set_result (job, success, result, local_error, destroy_result); + camel_imapx_job_unref (queued_job); + + if (local_error) + g_propagate_error (error, local_error); + + return success; + } + } + + JOB_QUEUE_LOCK (conn_man); + + camel_imapx_job_unref (queued_job); + + /* The queue could change, start from the beginning. */ + link = conn_man->priv->job_queue; + } else { + link = g_slist_next (link); + } + } + + conn_man->priv->job_queue = g_slist_prepend (conn_man->priv->job_queue, job); + + JOB_QUEUE_UNLOCK (conn_man); + + do { + g_clear_error (&local_error); + + cinfo = camel_imapx_conn_manager_ref_connection (conn_man, camel_imapx_job_get_mailbox (job), &is_new_connection, cancellable, error); + if (cinfo) { + CamelIMAPXMailbox *job_mailbox; + + job_mailbox = camel_imapx_job_get_mailbox (job); + + if (job_mailbox) + imapx_conn_manager_inc_mailbox_busy (conn_man, job_mailbox); + + if (camel_imapx_server_is_in_idle (cinfo->is)) { + CamelIMAPXMailbox *idle_mailbox; + + idle_mailbox = camel_imapx_server_ref_idle_mailbox (cinfo->is); + if (idle_mailbox) + imapx_conn_manager_dec_mailbox_idle (conn_man, idle_mailbox); + g_clear_object (&idle_mailbox); + } + + success = camel_imapx_server_stop_idle_sync (cinfo->is, cancellable, &local_error); + + if (success && camel_imapx_server_can_use_idle (cinfo->is)) { + GList *link, *connection_infos, *disconnected_infos = NULL; + + CON_READ_LOCK (conn_man); + connection_infos = g_list_copy (conn_man->priv->connections); + g_list_foreach (connection_infos, (GFunc) connection_info_ref, NULL); + CON_READ_UNLOCK (conn_man); + + /* Stop IDLE on all connections serving the same mailbox, + to avoid notifications for changes done by itself */ + for (link = connection_infos; link && !g_cancellable_is_cancelled (cancellable); link = g_list_next (link)) { + ConnectionInfo *other_cinfo = link->data; + CamelIMAPXMailbox *other_mailbox; + + if (!other_cinfo || other_cinfo == cinfo || connection_info_get_busy (other_cinfo) || + !camel_imapx_server_is_in_idle (other_cinfo->is)) + continue; + + other_mailbox = camel_imapx_server_ref_idle_mailbox (other_cinfo->is); + if (job_mailbox == other_mailbox) { + if (!camel_imapx_server_stop_idle_sync (other_cinfo->is, cancellable, &local_error)) { + c (camel_imapx_server_get_tagprefix (other_cinfo->is), + "Failed to stop IDLE call (will be removed) on connection %p (server:%p) due to error: %s\n", + other_cinfo, other_cinfo->is, local_error ? local_error->message : "Unknown error"); + + camel_imapx_server_disconnect_sync (other_cinfo->is, cancellable, NULL); + + disconnected_infos = g_list_prepend (disconnected_infos, connection_info_ref (other_cinfo)); + } else { + imapx_conn_manager_dec_mailbox_idle (conn_man, other_mailbox); + } + + g_clear_error (&local_error); + } + + g_clear_object (&other_mailbox); + } + + for (link = disconnected_infos; link; link = g_list_next (link)) { + ConnectionInfo *other_cinfo = link->data; + + imapx_conn_manager_remove_info (conn_man, other_cinfo); + } + + g_list_free_full (disconnected_infos, (GDestroyNotify) connection_info_unref); + g_list_free_full (connection_infos, (GDestroyNotify) connection_info_unref); + } + + if (success) + success = camel_imapx_job_run_sync (job, cinfo->is, cancellable, &local_error); + + if (job_mailbox) + imapx_conn_manager_dec_mailbox_busy (conn_man, job_mailbox); + + if (success) { + CamelIMAPXMailbox *idle_mailbox = NULL; + + if (!imapx_conn_manager_has_inbox_idle (conn_man)) { + CamelIMAPXStore *imapx_store; + + imapx_store = camel_imapx_conn_manager_ref_store (conn_man); + idle_mailbox = imapx_store ? camel_imapx_store_ref_mailbox (imapx_store, "INBOX") : NULL; + + g_clear_object (&imapx_store); + } + + if (!idle_mailbox) + idle_mailbox = camel_imapx_server_ref_selected (cinfo->is); + + /* Can start IDLE on the connection only if the IDLE folder is not busy + and not in IDLE already, to avoid multiple IDLE notifications on the same mailbox */ + if (idle_mailbox && camel_imapx_server_can_use_idle (cinfo->is) && + !imapx_conn_manager_is_mailbox_busy (conn_man, idle_mailbox) && + !imapx_conn_manager_is_mailbox_idle (conn_man, idle_mailbox)) { + camel_imapx_server_schedule_idle_sync (cinfo->is, idle_mailbox, cancellable, NULL); + + if (camel_imapx_server_is_in_idle (cinfo->is)) { + g_clear_object (&idle_mailbox); + + idle_mailbox = camel_imapx_server_ref_idle_mailbox (cinfo->is); + if (idle_mailbox) + imapx_conn_manager_inc_mailbox_idle (conn_man, idle_mailbox); + } + } + + g_clear_object (&idle_mailbox); + + imapx_conn_manager_unmark_busy (conn_man, cinfo); + } else if (!local_error || ((local_error->domain == G_IO_ERROR || local_error->domain == G_TLS_ERROR || local_error->domain == CAMEL_IMAPX_ERROR || + g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) && + !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))) { + c (camel_imapx_server_get_tagprefix (cinfo->is), "Removed connection %p (server:%p) due to error: %s\n", + cinfo, cinfo->is, local_error ? local_error->message : "Unknown error"); + + camel_imapx_server_disconnect_sync (cinfo->is, cancellable, NULL); + imapx_conn_manager_remove_info (conn_man, cinfo); + + if (!local_error || + g_error_matches (local_error, G_TLS_ERROR, G_TLS_ERROR_MISC) || + g_error_matches (local_error, G_TLS_ERROR, G_TLS_ERROR_EOF) || + g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CLOSED)) { + GError *tmp = local_error; + + local_error = NULL; + + /* This message won't get into UI. */ + g_set_error (&local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT, + "Reconnect after failure: %s", tmp ? tmp->message : "Unknown error"); + + g_clear_error (&tmp); + } + } else { + c (camel_imapx_server_get_tagprefix (cinfo->is), "Unmark connection %p (server:%p) busy after failure, error: %s\n", + cinfo, cinfo->is, local_error ? local_error->message : "Unknown error"); + + imapx_conn_manager_unmark_busy (conn_man, cinfo); + } + + connection_info_unref (cinfo); + } + + /* If there's a reconnect required for a new connection, then there happened + something really wrong, thus rather give up. */ + } while (!success && !is_new_connection && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)); + + if (local_error) + g_propagate_error (error, local_error); + + JOB_QUEUE_LOCK (conn_man); + conn_man->priv->job_queue = g_slist_remove (conn_man->priv->job_queue, job); + JOB_QUEUE_UNLOCK (conn_man); + + camel_imapx_job_done (job); + + return success; +} + +static gboolean +imapx_conn_manager_nothing_matches (CamelIMAPXJob *job, + CamelIMAPXJob *other_job) +{ + /* For jobs where none can match. */ + return FALSE; +} + +static gboolean +imapx_conn_manager_matches_sync_changes_or_refresh_info (CamelIMAPXJob *job, + CamelIMAPXJob *other_job) +{ + CamelIMAPXJobKind other_job_kind; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (other_job != NULL, FALSE); + g_return_val_if_fail (job != other_job, FALSE); + + if (camel_imapx_job_get_mailbox (job) != camel_imapx_job_get_mailbox (other_job)) + return FALSE; + + other_job_kind = camel_imapx_job_get_kind (other_job); + + return other_job_kind == CAMEL_IMAPX_JOB_SYNC_CHANGES || + other_job_kind == CAMEL_IMAPX_JOB_REFRESH_INFO; +} + +struct ListJobData { + gchar *pattern; + CamelStoreGetFolderInfoFlags flags; +}; + +static void +list_job_data_free (gpointer ptr) +{ + struct ListJobData *job_data = ptr; + + if (job_data) { + g_free (job_data->pattern); + g_free (job_data); + } +} + +static gboolean +imapx_conn_manager_list_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) +{ + struct ListJobData *job_data; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); + + job_data = camel_imapx_job_get_user_data (job); + g_return_val_if_fail (job_data != NULL, FALSE); + + return camel_imapx_server_list_sync (server, job_data->pattern, job_data->flags, cancellable, error); +} + +static gboolean +imapx_conn_manager_list_matches (CamelIMAPXJob *job, + CamelIMAPXJob *other_job) +{ + struct ListJobData *job_data, *other_job_data; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (other_job != NULL, FALSE); + + if (camel_imapx_job_get_kind (job) != CAMEL_IMAPX_JOB_LIST || + camel_imapx_job_get_kind (job) != camel_imapx_job_get_kind (other_job)) + return FALSE; + + job_data = camel_imapx_job_get_user_data (job); + other_job_data = camel_imapx_job_get_user_data (other_job); + + if (!job_data || !other_job_data) + return FALSE; + + return job_data->flags == other_job_data->flags && + g_strcmp0 (job_data->pattern, other_job_data->pattern) == 0; +} + +gboolean +camel_imapx_conn_manager_list_sync (CamelIMAPXConnManager *conn_man, + const gchar *pattern, + CamelStoreGetFolderInfoFlags flags, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXJob *job; + struct ListJobData *job_data; + gboolean success = FALSE; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_LIST, NULL, + imapx_conn_manager_list_run_sync, + imapx_conn_manager_list_matches, + NULL); + + job_data = g_new0 (struct ListJobData, 1); + job_data->pattern = g_strdup (pattern); + job_data->flags = flags; + + camel_imapx_job_set_user_data (job, job_data, list_job_data_free); + + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error); + if (success) + camel_imapx_job_copy_result (job, &success, NULL, error, NULL); + + camel_imapx_job_unref (job); + + return success; +} + +static gboolean +imapx_conn_manager_refresh_info_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXMailbox *mailbox; + gboolean success; + GError *local_error = NULL; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); + + mailbox = camel_imapx_job_get_mailbox (job); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + + success = camel_imapx_server_refresh_info_sync (server, mailbox, cancellable, &local_error); + + camel_imapx_job_set_result (job, success, NULL, local_error, NULL); + + if (local_error) + g_propagate_error (error, local_error); + + return success; +} + +gboolean +camel_imapx_conn_manager_refresh_info_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXJob *job; + gboolean success; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + + if (!camel_imapx_conn_manager_sync_changes_sync (conn_man, mailbox, cancellable, error)) + return FALSE; + + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_REFRESH_INFO, mailbox, + imapx_conn_manager_refresh_info_run_sync, NULL, NULL); + + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, + imapx_conn_manager_matches_sync_changes_or_refresh_info, + cancellable, error); + + camel_imapx_job_unref (job); + + return success; +} + +static gboolean +imapx_conn_manager_move_to_real_junk_sync (CamelIMAPXConnManager *conn_man, + CamelFolder *folder, + GCancellable *cancellable, + gboolean *out_need_to_expunge, + GError **error) +{ + CamelIMAPXFolder *imapx_folder; + CamelIMAPXMailbox *mailbox; + CamelIMAPXSettings *settings; + GPtrArray *uids_to_copy; + gchar *real_junk_path = NULL; + gboolean success = TRUE; + + *out_need_to_expunge = FALSE; + + /* Caller already obtained the mailbox from the folder, + * so the folder should still have it readily available. */ + imapx_folder = CAMEL_IMAPX_FOLDER (folder); + mailbox = camel_imapx_folder_ref_mailbox (imapx_folder); + g_return_val_if_fail (mailbox != NULL, FALSE); + + uids_to_copy = g_ptr_array_new_with_free_func ( + (GDestroyNotify) camel_pstring_free); + + settings = CAMEL_IMAPX_SETTINGS (camel_service_ref_settings (CAMEL_SERVICE (camel_folder_get_parent_store (folder)))); + if (camel_imapx_settings_get_use_real_junk_path (settings)) { + real_junk_path = camel_imapx_settings_dup_real_junk_path (settings); + camel_imapx_folder_claim_move_to_real_junk_uids (imapx_folder, uids_to_copy); + } + g_object_unref (settings); + + if (uids_to_copy->len > 0) { + CamelIMAPXStore *imapx_store; + CamelIMAPXMailbox *destination = NULL; + + imapx_store = camel_imapx_conn_manager_ref_store (conn_man); + + if (real_junk_path != NULL) { + folder = camel_store_get_folder_sync ( + CAMEL_STORE (imapx_store), + real_junk_path, 0, + cancellable, error); + } else { + g_set_error ( + error, CAMEL_FOLDER_ERROR, + CAMEL_FOLDER_ERROR_INVALID_PATH, + _("No destination folder specified")); + folder = NULL; + } + + if (folder != NULL) { + destination = camel_imapx_folder_list_mailbox ( + CAMEL_IMAPX_FOLDER (folder), + cancellable, error); + g_object_unref (folder); + } + + /* Avoid duplicating messages in the Junk folder. */ + if (destination == mailbox) { + success = TRUE; + } else if (destination != NULL) { + success = imapx_conn_manager_copy_message_sync ( + conn_man, mailbox, destination, + uids_to_copy, TRUE, FALSE, TRUE, + cancellable, error); + *out_need_to_expunge = success; + } else { + success = FALSE; + } + + if (!success) { + g_prefix_error ( + error, "%s: ", + _("Unable to move junk messages")); } + + g_clear_object (&destination); + g_clear_object (&imapx_store); + } + + g_ptr_array_unref (uids_to_copy); + g_free (real_junk_path); + + g_clear_object (&mailbox); + + return success; +} + +static gboolean +imapx_conn_manager_move_to_real_trash_sync (CamelIMAPXConnManager *conn_man, + CamelFolder *folder, + GCancellable *cancellable, + gboolean *out_need_to_expunge, + GError **error) +{ + CamelIMAPXFolder *imapx_folder; + CamelIMAPXMailbox *mailbox, *destination = NULL; + CamelIMAPXSettings *settings; + CamelIMAPXStore *imapx_store; + GPtrArray *uids_to_copy; + gchar *real_trash_path = NULL; + guint32 folder_deleted_count = 0; + gboolean success = TRUE; + + *out_need_to_expunge = FALSE; + + /* Caller already obtained the mailbox from the folder, + * so the folder should still have it readily available. */ + imapx_folder = CAMEL_IMAPX_FOLDER (folder); + mailbox = camel_imapx_folder_ref_mailbox (imapx_folder); + g_return_val_if_fail (mailbox != NULL, FALSE); + + uids_to_copy = g_ptr_array_new_with_free_func ( + (GDestroyNotify) camel_pstring_free); + + settings = CAMEL_IMAPX_SETTINGS (camel_service_ref_settings (CAMEL_SERVICE (camel_folder_get_parent_store (folder)))); + if (camel_imapx_settings_get_use_real_trash_path (settings)) { + real_trash_path = camel_imapx_settings_dup_real_trash_path (settings); + camel_imapx_folder_claim_move_to_real_trash_uids (CAMEL_IMAPX_FOLDER (folder), uids_to_copy); + } + g_object_unref (settings); + + imapx_store = camel_imapx_conn_manager_ref_store (conn_man); + + if (real_trash_path != NULL) { + folder = camel_store_get_folder_sync ( + CAMEL_STORE (imapx_store), + real_trash_path, 0, + cancellable, error); + } else { + if (uids_to_copy->len > 0) { + g_set_error ( + error, CAMEL_FOLDER_ERROR, + CAMEL_FOLDER_ERROR_INVALID_PATH, + _("No destination folder specified")); + } + + folder = NULL; + } + + if (folder != NULL) { + destination = camel_imapx_folder_list_mailbox ( + CAMEL_IMAPX_FOLDER (folder), + cancellable, error); + folder_deleted_count = camel_folder_summary_get_deleted_count (folder->summary); + g_object_unref (folder); + } + + /* Avoid duplicating messages in the Trash folder. */ + if (destination == mailbox) { + success = TRUE; + /* Deleted messages in the real Trash folder will be permanently deleted immediately. */ + *out_need_to_expunge = folder_deleted_count > 0 || uids_to_copy->len > 0; + } else if (destination != NULL) { + if (uids_to_copy->len > 0) { + success = imapx_conn_manager_copy_message_sync ( + conn_man, mailbox, destination, + uids_to_copy, TRUE, TRUE, TRUE, + cancellable, error); + *out_need_to_expunge = success; + } + } else if (uids_to_copy->len > 0) { + success = FALSE; + } + + if (!success) { + g_prefix_error ( + error, "%s: ", + _("Unable to move deleted messages")); + } + + g_ptr_array_unref (uids_to_copy); + g_free (real_trash_path); + + g_clear_object (&imapx_store); + g_clear_object (&destination); + g_clear_object (&mailbox); + + return success; +} + +static gboolean +imapx_conn_manager_expunge_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + gboolean skip_sync_changes, + GCancellable *cancellable, + GError **error); + +static gboolean +imapx_conn_manager_sync_changes_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXMailbox *mailbox; + GError *local_error = NULL; + gboolean can_influence_flags, success; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); + + mailbox = camel_imapx_job_get_mailbox (job); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + + can_influence_flags = GPOINTER_TO_INT (camel_imapx_job_get_user_data (job)) == 1; + + success = camel_imapx_server_sync_changes_sync (server, mailbox, can_influence_flags, cancellable, &local_error); + + camel_imapx_job_set_result (job, success, NULL, local_error, NULL); + + if (local_error) + g_propagate_error (error, local_error); + + return success; +} + +static gboolean +imapx_conn_manager_sync_changes_matches (CamelIMAPXJob *job, + CamelIMAPXJob *other_job) +{ + gboolean job_can_influence_flags, other_job_can_influence_flags; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (other_job != NULL, FALSE); + + if (camel_imapx_job_get_kind (job) != CAMEL_IMAPX_JOB_SYNC_CHANGES || + camel_imapx_job_get_kind (job) != camel_imapx_job_get_kind (other_job)) + return FALSE; + + job_can_influence_flags = GPOINTER_TO_INT (camel_imapx_job_get_user_data (job)) == 1; + other_job_can_influence_flags = GPOINTER_TO_INT (camel_imapx_job_get_user_data (other_job)) == 1; + + return job_can_influence_flags == other_job_can_influence_flags; +} + +gboolean +camel_imapx_conn_manager_sync_changes_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXJob *job; + CamelFolder *folder = NULL; + gboolean need_to_expunge = FALSE, expunge = FALSE; + gboolean success; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_SYNC_CHANGES, mailbox, + imapx_conn_manager_sync_changes_run_sync, + imapx_conn_manager_sync_changes_matches, NULL); + + /* Skip store of the \Deleted flag */ + camel_imapx_job_set_user_data (job, GINT_TO_POINTER (1), NULL); + + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, + imapx_conn_manager_matches_sync_changes_or_refresh_info, + cancellable, error); + + camel_imapx_job_unref (job); + + if (success) { + folder = imapx_conn_manager_ref_folder_sync (conn_man, mailbox, cancellable, error); + if (!folder) + success = FALSE; + } + + if (success) { + success = imapx_conn_manager_move_to_real_junk_sync ( + conn_man, folder, cancellable, + &need_to_expunge, error); + expunge |= need_to_expunge; + } + + if (success) { + success = imapx_conn_manager_move_to_real_trash_sync ( + conn_man, folder, cancellable, + &need_to_expunge, error); + expunge |= need_to_expunge; + } + + if (success && expunge) { + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_SYNC_CHANGES, mailbox, + imapx_conn_manager_sync_changes_run_sync, + imapx_conn_manager_sync_changes_matches, NULL); + + /* Store also the \Deleted flag */ + camel_imapx_job_set_user_data (job, GINT_TO_POINTER (0), NULL); + + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, + imapx_conn_manager_matches_sync_changes_or_refresh_info, + cancellable, error); + + camel_imapx_job_unref (job); + + success = imapx_conn_manager_expunge_sync (conn_man, mailbox, TRUE, cancellable, error); + } + + g_clear_object (&folder); + + return success; +} + +static gboolean +imapx_conn_manager_expunge_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXMailbox *mailbox; + GError *local_error = NULL; + gboolean success; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); + + mailbox = camel_imapx_job_get_mailbox (job); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + + success = camel_imapx_server_expunge_sync (server, mailbox, cancellable, &local_error); + + camel_imapx_job_set_result (job, success, NULL, local_error, NULL); + + if (local_error) + g_propagate_error (error, local_error); + + return success; +} + +static gboolean +imapx_conn_manager_expunge_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + gboolean skip_sync_changes, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXJob *job; + gboolean success; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + + if (!skip_sync_changes && !camel_imapx_conn_manager_sync_changes_sync (conn_man, mailbox, cancellable, error)) + return FALSE; + + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_EXPUNGE, mailbox, + imapx_conn_manager_expunge_run_sync, NULL, NULL); + + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error); + + camel_imapx_job_unref (job); + + return success; +} + +gboolean +camel_imapx_conn_manager_expunge_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) +{ + return imapx_conn_manager_expunge_sync (conn_man, mailbox, FALSE, cancellable, error); +} + +struct GetMessageJobData { + CamelFolderSummary *summary; + CamelDataCache *message_cache; + gchar *message_uid; +}; + +static void +get_message_job_data_free (gpointer ptr) +{ + struct GetMessageJobData *job_data = ptr; + + if (job_data) { + g_clear_object (&job_data->summary); + g_clear_object (&job_data->message_cache); + g_free (job_data->message_uid); + g_free (job_data); + } +} + +static gboolean +imapx_conn_manager_get_message_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) +{ + struct GetMessageJobData *job_data; + CamelIMAPXMailbox *mailbox; + CamelStream *result; + GError *local_error = NULL; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); + + mailbox = camel_imapx_job_get_mailbox (job); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + + job_data = camel_imapx_job_get_user_data (job); + g_return_val_if_fail (job_data != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (job_data->summary), FALSE); + g_return_val_if_fail (CAMEL_IS_DATA_CACHE (job_data->message_cache), FALSE); + g_return_val_if_fail (job_data->message_uid != NULL, FALSE); + + result = camel_imapx_server_get_message_sync ( + server, mailbox, job_data->summary, job_data->message_cache, job_data->message_uid, + cancellable, &local_error); + + camel_imapx_job_set_result (job, result != NULL, result, local_error, result ? g_object_unref : NULL); + + if (local_error) + g_propagate_error (error, local_error); + + return result != NULL; +} + +static gboolean +imapx_conn_manager_get_message_matches (CamelIMAPXJob *job, + CamelIMAPXJob *other_job) +{ + struct GetMessageJobData *job_data, *other_job_data; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (other_job != NULL, FALSE); + + if (camel_imapx_job_get_kind (job) != CAMEL_IMAPX_JOB_GET_MESSAGE || + camel_imapx_job_get_kind (job) != camel_imapx_job_get_kind (other_job)) + return FALSE; + + job_data = camel_imapx_job_get_user_data (job); + other_job_data = camel_imapx_job_get_user_data (other_job); + + if (!job_data || !other_job_data) + return FALSE; + + return g_strcmp0 (job_data->message_uid, other_job_data->message_uid) == 0; +} + +static void +imapx_conn_manager_get_message_copy_result (CamelIMAPXJob *job, + gconstpointer set_result, + gpointer *out_result) +{ + if (!set_result || !*out_result) + return; + + *out_result = g_object_ref ((gpointer) set_result); +} + +CamelStream * +camel_imapx_conn_manager_get_message_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + CamelFolderSummary *summary, + CamelDataCache *message_cache, + const gchar *message_uid, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXJob *job; + struct GetMessageJobData *job_data; + CamelStream *result; + gpointer result_data = NULL; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), NULL); + + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_GET_MESSAGE, mailbox, + imapx_conn_manager_get_message_run_sync, + imapx_conn_manager_get_message_matches, + imapx_conn_manager_get_message_copy_result); + + job_data = g_new0 (struct GetMessageJobData, 1); + job_data->summary = g_object_ref (summary); + job_data->message_cache = g_object_ref (message_cache); + job_data->message_uid = g_strdup (message_uid); + + camel_imapx_job_set_user_data (job, job_data, get_message_job_data_free); + + if (camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error) && + camel_imapx_job_take_result_data (job, &result_data)) { + result = result_data; + } else { + result = NULL; + } + + camel_imapx_job_unref (job); + + return result; +} + +struct CopyMessageJobData { + CamelIMAPXMailbox *destination; + GPtrArray *uids; + gboolean delete_originals; + gboolean remove_deleted_flags; +}; + +static void +copy_message_job_data_free (gpointer ptr) +{ + struct CopyMessageJobData *job_data = ptr; + + if (job_data) { + g_clear_object (&job_data->destination); + g_ptr_array_free (job_data->uids, TRUE); + g_free (job_data); + } +} + +static gboolean +imapx_conn_manager_copy_message_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) +{ + struct CopyMessageJobData *job_data; + CamelIMAPXMailbox *mailbox; + GError *local_error = NULL; + gboolean success; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); + + mailbox = camel_imapx_job_get_mailbox (job); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + + job_data = camel_imapx_job_get_user_data (job); + g_return_val_if_fail (job_data != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (job_data->destination), FALSE); + g_return_val_if_fail (job_data->uids != NULL, FALSE); + + success = camel_imapx_server_copy_message_sync ( + server, mailbox, job_data->destination, job_data->uids, job_data->delete_originals, + job_data->remove_deleted_flags, cancellable, &local_error); + + camel_imapx_job_set_result (job, success, NULL, local_error, NULL); + + if (local_error) + g_propagate_error (error, local_error); + + return success; +} + +static gboolean +imapx_conn_manager_copy_message_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + CamelIMAPXMailbox *destination, + GPtrArray *uids, + gboolean delete_originals, + gboolean remove_deleted_flags, + gboolean skip_sync_changes, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXJob *job; + struct CopyMessageJobData *job_data; + gboolean success; + gint ii; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + + if (!skip_sync_changes && !camel_imapx_conn_manager_sync_changes_sync (conn_man, mailbox, cancellable, error)) + return FALSE; + + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_COPY_MESSAGE, mailbox, + imapx_conn_manager_copy_message_run_sync, + imapx_conn_manager_nothing_matches, + NULL); + + job_data = g_new0 (struct CopyMessageJobData, 1); + job_data->destination = g_object_ref (destination); + job_data->uids = g_ptr_array_new_full (uids->len, (GDestroyNotify) camel_pstring_free); + job_data->delete_originals = delete_originals; + job_data->remove_deleted_flags = remove_deleted_flags; + + for (ii = 0; ii < uids->len; ii++) { + g_ptr_array_add (job_data->uids, (gpointer) camel_pstring_strdup (uids->pdata[ii])); } -exit: - if (cinfo != NULL && folder_name != NULL) - connection_info_insert_folder_name (cinfo, folder_name); + camel_imapx_job_set_user_data (job, job_data, copy_message_job_data_free); + + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error); + + camel_imapx_job_unref (job); - if (camel_debug_flag (conman)) { - printf ("%s: for-expensive:%d will return:%p cmd-count:%d has-expensive:%d found:%d; connections opened:%d max:%d\n", G_STRFUNC, for_expensive_job, cinfo, cinfo ? camel_imapx_server_get_command_count (cinfo->is) : -2, cinfo ? camel_imapx_server_has_expensive_command (cinfo->is) : -2, expensive_connections, g_list_length (list), concurrent_connections); - for (link = list; link != NULL; link = g_list_next (link)) { - ConnectionInfo *candidate = link->data; + if (success) { + CamelFolder *dest; - printf (" cmds:%d has-expensive:%d avail:%d cinfo:%p server:%p\n", camel_imapx_server_get_command_count (candidate->is), camel_imapx_server_has_expensive_command (candidate->is), connection_info_is_available (candidate), candidate, candidate->is); + dest = imapx_conn_manager_ref_folder_sync (conn_man, destination, cancellable, NULL); + + /* Update destination folder only if it's not frozen, + * to avoid updating for each "move" action on a single + * message while filtering. */ + if (dest && !camel_folder_is_frozen (dest)) { + /* Ignore errors here */ + camel_imapx_conn_manager_refresh_info_sync (conn_man, destination, cancellable, NULL); } - } - if (cinfo != NULL) { - is = g_object_ref (cinfo->is); - connection_info_unref (cinfo); + g_clear_object (&dest); } - g_object_unref (store); + return success; +} - return is; +gboolean +camel_imapx_conn_manager_copy_message_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + CamelIMAPXMailbox *destination, + GPtrArray *uids, + gboolean delete_originals, + gboolean remove_deleted_flags, + GCancellable *cancellable, + GError **error) +{ + return imapx_conn_manager_copy_message_sync (conn_man, mailbox, destination, uids, + delete_originals, remove_deleted_flags, FALSE, cancellable, error); } -static gchar -imapx_conn_manager_get_next_free_tagprefix_unlocked (CamelIMAPXConnManager *con_man) +struct AppendMessageJobData { + CamelFolderSummary *summary; + CamelDataCache *message_cache; + CamelMimeMessage *message; + const CamelMessageInfo *mi; +}; + +static void +append_message_job_data_free (gpointer ptr) { - gchar adept; - GList *iter; + struct AppendMessageJobData *job_data = ptr; - /* the 'Z' is dedicated to auth types query */ - adept = 'A'; - while (adept < 'Z') { - for (iter = con_man->priv->connections; iter; iter = g_list_next (iter)) { - ConnectionInfo *cinfo = iter->data; + if (job_data) { + g_clear_object (&job_data->summary); + g_clear_object (&job_data->message_cache); + g_clear_object (&job_data->message); + g_free (job_data); + } +} - if (!cinfo || !cinfo->is) - continue; +static gboolean +imapx_conn_manager_append_message_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) +{ + struct AppendMessageJobData *job_data; + CamelIMAPXMailbox *mailbox; + gchar *appended_uid = NULL; + GError *local_error = NULL; + gboolean success; - if (cinfo->is->tagprefix == adept) - break; - } + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); - /* Read all current active connections and none has the same tag prefix */ - if (!iter) - break; + mailbox = camel_imapx_job_get_mailbox (job); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - adept++; + job_data = camel_imapx_job_get_user_data (job); + g_return_val_if_fail (job_data != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (job_data->summary), FALSE); + g_return_val_if_fail (CAMEL_IS_DATA_CACHE (job_data->message_cache), FALSE); + g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (job_data->message), FALSE); + + success = camel_imapx_server_append_message_sync (server, mailbox, job_data->summary, job_data->message_cache, + job_data->message, job_data->mi, &appended_uid, cancellable, &local_error); + + camel_imapx_job_set_result (job, success, appended_uid, local_error, appended_uid ? g_free : NULL); + + if (local_error) + g_propagate_error (error, local_error); + + return success; +} + +gboolean +camel_imapx_conn_manager_append_message_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + CamelFolderSummary *summary, + CamelDataCache *message_cache, + CamelMimeMessage *message, + const CamelMessageInfo *mi, + gchar **append_uid, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXJob *job; + struct AppendMessageJobData *job_data; + gboolean success; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_APPEND_MESSAGE, mailbox, + imapx_conn_manager_append_message_run_sync, + imapx_conn_manager_nothing_matches, + NULL); + + job_data = g_new0 (struct AppendMessageJobData, 1); + job_data->summary = g_object_ref (summary); + job_data->message_cache = g_object_ref (message_cache); + job_data->message = g_object_ref (message); + job_data->mi = mi; + + camel_imapx_job_set_user_data (job, job_data, append_message_job_data_free); + + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error); + if (success) { + gpointer result_data = NULL; + + success = camel_imapx_job_take_result_data (job, &result_data); + if (success && append_uid) + *append_uid = result_data; + else + g_free (result_data); } - g_return_val_if_fail (adept >= 'A' && adept < 'Z', 'Z'); + camel_imapx_job_unref (job); - return adept; + return success; } -static CamelIMAPXServer * -imapx_create_new_connection_unlocked (CamelIMAPXConnManager *con_man, - const gchar *folder_name, - GCancellable *cancellable, - GError **error) +static gboolean +imapx_conn_manager_sync_message_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) +{ + struct GetMessageJobData *job_data; + CamelIMAPXMailbox *mailbox; + GError *local_error = NULL; + gboolean success; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); + + mailbox = camel_imapx_job_get_mailbox (job); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + + job_data = camel_imapx_job_get_user_data (job); + g_return_val_if_fail (job_data != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (job_data->summary), FALSE); + g_return_val_if_fail (CAMEL_IS_DATA_CACHE (job_data->message_cache), FALSE); + g_return_val_if_fail (job_data->message_uid != NULL, FALSE); + + success = camel_imapx_server_sync_message_sync ( + server, mailbox, job_data->summary, job_data->message_cache, job_data->message_uid, + cancellable, &local_error); + + camel_imapx_job_set_result (job, success, NULL, local_error, NULL); + + if (local_error) + g_propagate_error (error, local_error); + + return success; +} + +gboolean +camel_imapx_conn_manager_sync_message_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + CamelFolderSummary *summary, + CamelDataCache *message_cache, + const gchar *message_uid, + GCancellable *cancellable, + GError **error) { - CamelStore *store; - CamelIMAPXServer *is = NULL; - CamelIMAPXStore *imapx_store; - ConnectionInfo *cinfo = NULL; + CamelIMAPXJob *job; + struct GetMessageJobData *job_data; gboolean success; - /* Caller must be holding CON_WRITE_LOCK. */ + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); - /* Check if we got cancelled while we were waiting. */ - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return NULL; + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_SYNC_MESSAGE, mailbox, + imapx_conn_manager_sync_message_run_sync, + imapx_conn_manager_get_message_matches, + NULL); - store = camel_imapx_conn_manager_ref_store (con_man); - g_return_val_if_fail (store != NULL, NULL); + job_data = g_new0 (struct GetMessageJobData, 1); + job_data->summary = g_object_ref (summary); + job_data->message_cache = g_object_ref (message_cache); + job_data->message_uid = g_strdup (message_uid); - imapx_store = CAMEL_IMAPX_STORE (store); + camel_imapx_job_set_user_data (job, job_data, get_message_job_data_free); - is = camel_imapx_server_new (imapx_store); - is->tagprefix = imapx_conn_manager_get_next_free_tagprefix_unlocked (con_man); + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error); - /* XXX As part of the connect operation the CamelIMAPXServer will - * have to call camel_session_authenticate_sync(), but it has - * no way to pass itself through in that call so the service - * knows which CamelIMAPXServer is trying to authenticate. - * - * IMAPX is the only provider that does multiple connections - * like this, so I didn't want to pollute the CamelSession and - * CamelService authentication APIs with an extra argument. - * Instead we do this little hack so the service knows which - * CamelIMAPXServer to act on in its authenticate_sync() method. - * - * Because we're holding the CAMEL_SERVICE_REC_CONNECT_LOCK - * we should not have multiple IMAPX connections trying to - * authenticate at once, so this should be thread-safe. - */ - camel_imapx_store_set_connecting_server (imapx_store, is, con_man->priv->connections != NULL); - success = camel_imapx_server_connect (is, cancellable, error); - camel_imapx_store_set_connecting_server (imapx_store, NULL, FALSE); + camel_imapx_job_unref (job); - if (!success) { - g_clear_object (&is); - goto exit; - } + return success; +} - g_signal_connect ( - is, "shutdown", - G_CALLBACK (imapx_conn_shutdown), con_man); - - g_signal_connect ( - is, "mailbox-select", - G_CALLBACK (imapx_conn_update_select), con_man); - g_signal_connect ( - is, "mailbox-closed", - G_CALLBACK (imapx_conn_mailbox_closed), con_man); +static gboolean +imapx_conn_manager_create_mailbox_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) +{ + const gchar *mailbox_name; + GError *local_error = NULL; + gboolean success; - cinfo = connection_info_new (is); + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); - if (folder_name != NULL) - connection_info_insert_folder_name (cinfo, folder_name); + mailbox_name = camel_imapx_job_get_user_data (job); + g_return_val_if_fail (mailbox_name != NULL, FALSE); - /* Takes ownership of the ConnectionInfo. */ - con_man->priv->connections = g_list_prepend ( - con_man->priv->connections, cinfo); + success = camel_imapx_server_create_mailbox_sync (server, mailbox_name, cancellable, &local_error); - c (is->tagprefix, "Created new connection %p (server:%p) for %s; total connections %d\n", cinfo, cinfo->is, folder_name, g_list_length (con_man->priv->connections)); + camel_imapx_job_set_result (job, success, NULL, local_error, NULL); -exit: - g_object_unref (store); + if (local_error) + g_propagate_error (error, local_error); - return is; + return success; } -/****************************/ +gboolean +camel_imapx_conn_manager_create_mailbox_sync (CamelIMAPXConnManager *conn_man, + const gchar *mailbox_name, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXJob *job; + gboolean success; -CamelIMAPXConnManager * -camel_imapx_conn_manager_new (CamelStore *store) + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_CREATE_MAILBOX, NULL, + imapx_conn_manager_create_mailbox_run_sync, + imapx_conn_manager_nothing_matches, + NULL); + + camel_imapx_job_set_user_data (job, g_strdup (mailbox_name), g_free); + + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error); + + camel_imapx_job_unref (job); + + return success; +} + +static gboolean +imapx_conn_manager_delete_mailbox_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) { - g_return_val_if_fail (CAMEL_IS_STORE (store), NULL); + CamelIMAPXMailbox *mailbox; + GError *local_error = NULL; + gboolean success; - return g_object_new ( - CAMEL_TYPE_IMAPX_CONN_MANAGER, "store", store, NULL); + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); + + mailbox = camel_imapx_job_get_mailbox (job); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + + success = camel_imapx_server_delete_mailbox_sync (server, mailbox, cancellable, &local_error); + + camel_imapx_job_set_result (job, success, NULL, local_error, NULL); + + if (local_error) + g_propagate_error (error, local_error); + + return success; +} + +gboolean +camel_imapx_conn_manager_delete_mailbox_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXJob *job; + gboolean success; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_DELETE_MAILBOX, mailbox, + imapx_conn_manager_delete_mailbox_run_sync, + imapx_conn_manager_nothing_matches, + NULL); + + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error); + + camel_imapx_job_unref (job); + + return success; +} + +static gboolean +imapx_conn_manager_rename_mailbox_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXMailbox *mailbox; + const gchar *new_mailbox_name; + GError *local_error = NULL; + gboolean success; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); + + mailbox = camel_imapx_job_get_mailbox (job); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + + new_mailbox_name = camel_imapx_job_get_user_data (job); + g_return_val_if_fail (new_mailbox_name != NULL, FALSE); + + success = camel_imapx_server_rename_mailbox_sync (server, mailbox, new_mailbox_name, cancellable, &local_error); + + camel_imapx_job_set_result (job, success, NULL, local_error, NULL); + + if (local_error) + g_propagate_error (error, local_error); + + return success; } -CamelStore * -camel_imapx_conn_manager_ref_store (CamelIMAPXConnManager *con_man) +gboolean +camel_imapx_conn_manager_rename_mailbox_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + const gchar *new_mailbox_name, + GCancellable *cancellable, + GError **error) { - g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL); + CamelIMAPXJob *job; + gboolean success; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_RENAME_MAILBOX, mailbox, + imapx_conn_manager_rename_mailbox_run_sync, + imapx_conn_manager_nothing_matches, + NULL); + + camel_imapx_job_set_user_data (job, g_strdup (new_mailbox_name), g_free); - return g_weak_ref_get (&con_man->priv->store); + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error); + + camel_imapx_job_unref (job); + + return success; } -CamelIMAPXServer * -camel_imapx_conn_manager_get_connection (CamelIMAPXConnManager *con_man, - const gchar *folder_name, - gboolean for_expensive_job, - GCancellable *cancellable, - GError **error) +static gboolean +imapx_conn_manager_subscribe_mailbox_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) { - CamelIMAPXServer *is = NULL; + CamelIMAPXMailbox *mailbox; + GError *local_error = NULL; + gboolean success; - g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL); + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); - g_mutex_lock (&con_man->priv->pending_connections_lock); - if (cancellable) { - g_object_ref (cancellable); - } else { - cancellable = g_cancellable_new (); - } - con_man->priv->pending_connections = g_slist_prepend (con_man->priv->pending_connections, cancellable); - g_mutex_unlock (&con_man->priv->pending_connections_lock); + mailbox = camel_imapx_job_get_mailbox (job); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - /* Hold the writer lock while we requisition a CamelIMAPXServer - * to prevent other threads from adding or removing connections. */ - CON_WRITE_LOCK (con_man); - - /* Check if we've got cancelled while waiting for the lock. */ - if (!g_cancellable_set_error_if_cancelled (cancellable, error)) { - is = imapx_find_connection_unlocked (con_man, folder_name, for_expensive_job); - if (is == NULL) { - GError *local_error = NULL; - - is = imapx_create_new_connection_unlocked (con_man, folder_name, cancellable, &local_error); - - if (!is) { - gboolean limit_connections = - g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, - CAMEL_IMAPX_SERVER_ERROR_CONCURRENT_CONNECT_FAILED) && - con_man->priv->connections; - - c ('*', "Failed to open a new connection, while having %d opened, with error: %s; will limit connections: %s\n", - g_list_length (con_man->priv->connections), - local_error ? local_error->message : "Unknown error", - limit_connections ? "yes" : "no"); - - if (limit_connections) { - /* limit to one-less than current connection count - be nice to the server */ - con_man->priv->limit_max_connections = g_list_length (con_man->priv->connections) - 1; - if (!con_man->priv->limit_max_connections) - con_man->priv->limit_max_connections = 1; - - g_clear_error (&local_error); - is = imapx_find_connection_unlocked (con_man, folder_name, for_expensive_job); - } else if (local_error) { - g_propagate_error (error, local_error); - } - } - } - } + success = camel_imapx_server_subscribe_mailbox_sync (server, mailbox, cancellable, &local_error); - CON_WRITE_UNLOCK (con_man); + camel_imapx_job_set_result (job, success, NULL, local_error, NULL); - g_mutex_lock (&con_man->priv->pending_connections_lock); - con_man->priv->pending_connections = g_slist_remove (con_man->priv->pending_connections, cancellable); - g_object_unref (cancellable); - g_mutex_unlock (&con_man->priv->pending_connections_lock); + if (local_error) + g_propagate_error (error, local_error); - return is; + return success; } -GList * -camel_imapx_conn_manager_get_connections (CamelIMAPXConnManager *con_man) +gboolean +camel_imapx_conn_manager_subscribe_mailbox_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) { - GList *list, *link; + CamelIMAPXJob *job; + gboolean success; - g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL); + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); - list = imapx_conn_manager_list_info (con_man); + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_SUBSCRIBE_MAILBOX, mailbox, + imapx_conn_manager_subscribe_mailbox_run_sync, NULL, NULL); - /* Swap ConnectionInfo for CamelIMAPXServer in each link. */ - for (link = list; link != NULL; link = g_list_next (link)) { - ConnectionInfo *cinfo = link->data; - link->data = g_object_ref (cinfo->is); - connection_info_unref (cinfo); + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error); + + camel_imapx_job_unref (job); + + return success; +} + +static gboolean +imapx_conn_manager_unsubscribe_mailbox_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXMailbox *mailbox; + GError *local_error = NULL; + gboolean success; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); + + mailbox = camel_imapx_job_get_mailbox (job); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + + success = camel_imapx_server_unsubscribe_mailbox_sync (server, mailbox, cancellable, &local_error); + + camel_imapx_job_set_result (job, success, NULL, local_error, NULL); + + if (local_error) + g_propagate_error (error, local_error); + + return success; +} + +gboolean +camel_imapx_conn_manager_unsubscribe_mailbox_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXJob *job; + gboolean success; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_UNSUBSCRIBE_MAILBOX, mailbox, + imapx_conn_manager_unsubscribe_mailbox_run_sync, NULL, NULL); + + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error); + + camel_imapx_job_unref (job); + + return success; +} + +static gboolean +imapx_conn_manager_update_quota_info_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXMailbox *mailbox; + GError *local_error = NULL; + gboolean success; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); + + mailbox = camel_imapx_job_get_mailbox (job); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + + success = camel_imapx_server_update_quota_info_sync (server, mailbox, cancellable, &local_error); + + camel_imapx_job_set_result (job, success, NULL, local_error, NULL); + + if (local_error) + g_propagate_error (error, local_error); + + return success; +} + +gboolean +camel_imapx_conn_manager_update_quota_info_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXJob *job; + gboolean success; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), FALSE); + + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_UPDATE_QUOTA_INFO, mailbox, + imapx_conn_manager_update_quota_info_run_sync, NULL, NULL); + + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error); + + camel_imapx_job_unref (job); + + return success; +} + +static gchar ** +imapx_copy_strv (const gchar * const *words) +{ + gchar **copy; + gint ii; + + if (!words || !*words) + return NULL; + + copy = g_new0 (gchar *, g_strv_length ((gchar **) words) + 1); + + for (ii = 0; words[ii]; ii++) { + copy[ii] = g_strdup (words[ii]); } - return list; + copy[ii] = NULL; + + return copy; } -/* Used for handling operations that fails to execute and that needs to removed from folder list */ -void -camel_imapx_conn_manager_update_con_info (CamelIMAPXConnManager *con_man, - CamelIMAPXServer *is, - const gchar *folder_name) +static gboolean +imapx_equal_strv (const gchar * const *words1, + const gchar * const *words2) { - ConnectionInfo *cinfo; + gint ii; - g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man)); + if (words1 == words2) + return TRUE; - /* Returns a new ConnectionInfo reference. */ - cinfo = imapx_conn_manager_lookup_info (con_man, is); + if (!words1 || !words2) + return FALSE; - if (cinfo == NULL) - return; + for (ii = 0; words1[ii] && words2[ii]; ii++) { + if (g_strcmp0 (words1[ii], words2[ii]) != 0) + return FALSE; + } + + return !words1[ii] && !words2[ii]; +} + +struct UidSearchJobData { + gchar *criteria_prefix; + gchar *search_key; + gchar **words; +}; + +static void +uid_search_job_data_free (gpointer ptr) +{ + struct UidSearchJobData *job_data = ptr; - if (camel_imapx_server_folder_name_in_jobs (is, folder_name)) { - connection_info_remove_folder_name (cinfo, folder_name); - c (is->tagprefix, "Removed folder %s from connection folder list - op done \n", folder_name); + if (ptr) { + g_free (job_data->criteria_prefix); + g_free (job_data->search_key); + g_strfreev (job_data->words); + g_free (job_data); } +} + +static gboolean +imapx_conn_manager_uid_search_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) +{ + struct UidSearchJobData *job_data; + CamelIMAPXMailbox *mailbox; + GPtrArray *uids = NULL; + GError *local_error = NULL; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); + + mailbox = camel_imapx_job_get_mailbox (job); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + + job_data = camel_imapx_job_get_user_data (job); + g_return_val_if_fail (job_data != NULL, FALSE); + + uids = camel_imapx_server_uid_search_sync (server, mailbox, job_data->criteria_prefix, + job_data->search_key, (const gchar * const *) job_data->words, cancellable, &local_error); + + camel_imapx_job_set_result (job, uids != NULL, uids, local_error, uids ? (GDestroyNotify) g_ptr_array_free : NULL); + + if (local_error) + g_propagate_error (error, local_error); - connection_info_unref (cinfo); + return uids != NULL; } -void -camel_imapx_conn_manager_close_connections (CamelIMAPXConnManager *con_man, - const GError *error) +static gboolean +imapx_conn_manager_uid_search_matches (CamelIMAPXJob *job, + CamelIMAPXJob *other_job) { - GList *iter, *connections; + struct UidSearchJobData *job_data, *other_job_data; - g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man)); + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (other_job != NULL, FALSE); - /* Do this before acquiring the write lock, because any pending - connection holds the write lock, thus makes this request starve. */ - imax_conn_manager_cancel_pending_connections (con_man); + if (camel_imapx_job_get_kind (job) != CAMEL_IMAPX_JOB_UID_SEARCH || + camel_imapx_job_get_kind (job) != camel_imapx_job_get_kind (other_job)) + return FALSE; + + job_data = camel_imapx_job_get_user_data (job); + other_job_data = camel_imapx_job_get_user_data (other_job); + + if (!job_data || !other_job_data) + return job_data == other_job_data; + + return g_strcmp0 (job_data->criteria_prefix, other_job_data->criteria_prefix) == 0 && + g_strcmp0 (job_data->search_key, other_job_data->search_key) == 0 && + imapx_equal_strv ((const gchar * const *) job_data->words, (const gchar * const *) other_job_data->words); +} + +GPtrArray * +camel_imapx_conn_manager_uid_search_sync (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + const gchar *criteria_prefix, + const gchar *search_key, + const gchar * const *words, + GCancellable *cancellable, + GError **error) +{ + struct UidSearchJobData *job_data; + GPtrArray *uids = NULL; + CamelIMAPXJob *job; + gboolean success; + + g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man), NULL); - CON_WRITE_LOCK (con_man); + job_data = g_new0 (struct UidSearchJobData, 1); + job_data->criteria_prefix = g_strdup (criteria_prefix); + job_data->search_key = g_strdup (search_key); + job_data->words = imapx_copy_strv (words); - c('*', "Closing all %d connections, with propagated error: %s\n", g_list_length (con_man->priv->connections), error ? error->message : "none"); + job = camel_imapx_job_new (CAMEL_IMAPX_JOB_UID_SEARCH, mailbox, + imapx_conn_manager_uid_search_run_sync, + imapx_conn_manager_uid_search_matches, + NULL); - connections = con_man->priv->connections; - con_man->priv->connections = NULL; + camel_imapx_job_set_user_data (job, job_data, uid_search_job_data_free); - CON_WRITE_UNLOCK (con_man); + success = camel_imapx_conn_manager_run_job_sync (conn_man, job, NULL, cancellable, error); + if (success) { + gpointer result_data = NULL; - for (iter = connections; iter; iter = g_list_next (iter)) { - connection_info_set_shutdown_error (iter->data, error); + success = camel_imapx_job_take_result_data (job, &result_data); + if (success) + uids = result_data; } - g_list_free_full (connections, (GDestroyNotify) connection_info_cancel_and_unref); + camel_imapx_job_unref (job); + + return uids; } /* for debugging purposes only */ void -camel_imapx_conn_manager_dump_queue_status (CamelIMAPXConnManager *con_man) +camel_imapx_conn_manager_dump_queue_status (CamelIMAPXConnManager *conn_man) { - GList *list, *link; + GList *llink; + GSList *slink; - g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man)); + g_return_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (conn_man)); - list = imapx_conn_manager_list_info (con_man); + CON_READ_LOCK (conn_man); - for (link = list; link != NULL; link = g_list_next (link)) { - ConnectionInfo *cinfo = link->data; - camel_imapx_server_dump_queue_status (cinfo->is); - connection_info_unref (cinfo); + printf ("%s: opened connections:%d\n", G_STRFUNC, g_list_length (conn_man->priv->connections)); + + for (llink = conn_man->priv->connections; llink != NULL; llink = g_list_next (llink)) { + ConnectionInfo *cinfo = llink->data; + CamelIMAPXCommand *cmd = NULL; + + if (cinfo) + cmd = cinfo->is ? camel_imapx_server_ref_current_command (cinfo->is) : NULL; + + printf (" connection:%p server:[%c] %p busy:%d command:%s\n", cinfo, + cinfo && cinfo->is ? camel_imapx_server_get_tagprefix (cinfo->is) : '?', + cinfo ? cinfo->is : NULL, cinfo ? cinfo->busy : FALSE, + cmd ? camel_imapx_job_get_kind_name (cmd->job_kind) : "[null]"); + + if (cmd) + camel_imapx_command_unref (cmd); + } + + CON_READ_UNLOCK (conn_man); + + JOB_QUEUE_LOCK (conn_man); + + printf ("Queued jobs:%d\n", g_slist_length (conn_man->priv->job_queue)); + for (slink = conn_man->priv->job_queue; slink; slink = g_slist_next (slink)) { + CamelIMAPXJob *job = slink->data; + + printf (" job:%p kind:%s mailbox:%s\n", job, + job ? camel_imapx_job_get_kind_name (camel_imapx_job_get_kind (job)) : "[null]", + job && camel_imapx_job_get_mailbox (job) ? camel_imapx_mailbox_get_name (camel_imapx_job_get_mailbox (job)) : "[null]"); } - g_list_free (list); + JOB_QUEUE_UNLOCK (conn_man); } diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.h.imapx-update-to-upstream 2014-11-07 08:34:49.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-conn-manager.h 2016-08-15 13:52:41.949976330 +0200 @@ -21,6 +21,8 @@ #ifndef _CAMEL_IMAPX_CONN_MANAGER_H #define _CAMEL_IMAPX_CONN_MANAGER_H +#include "camel-imapx-job.h" +#include "camel-imapx-mailbox.h" #include "camel-imapx-server.h" G_BEGIN_DECLS @@ -44,6 +46,8 @@ G_BEGIN_DECLS (G_TYPE_INSTANCE_GET_CLASS \ ((obj), CAMEL_TYPE_IMAPX_CONN_MANAGER, CamelIMAPXConnManagerClass)) +struct _CamelIMAPXStore; + typedef struct _CamelIMAPXConnManager CamelIMAPXConnManager; typedef struct _CamelIMAPXConnManagerClass CamelIMAPXConnManagerClass; typedef struct _CamelIMAPXConnManagerPrivate CamelIMAPXConnManagerPrivate; @@ -56,33 +60,131 @@ struct _CamelIMAPXConnManager { struct _CamelIMAPXConnManagerClass { GObjectClass parent_class; + + /* Signals */ + void (* connection_created) (CamelIMAPXConnManager *conn_man, + CamelIMAPXServer *server); }; GType camel_imapx_conn_manager_get_type (void); CamelIMAPXConnManager * camel_imapx_conn_manager_new (CamelStore *store); -CamelStore * camel_imapx_conn_manager_ref_store - (CamelIMAPXConnManager *con_man); -CamelIMAPXServer * - camel_imapx_conn_manager_get_connection - (CamelIMAPXConnManager *con_man, - const gchar *folder_name, - gboolean for_expensive_job, - GCancellable *cancellable, - GError **error); -void camel_imapx_conn_manager_close_connections - (CamelIMAPXConnManager *con_man, - const GError *error); -GList * camel_imapx_conn_manager_get_connections - (CamelIMAPXConnManager *con_man); -void camel_imapx_conn_manager_update_con_info - (CamelIMAPXConnManager *con_man, - CamelIMAPXServer *server, - const gchar *folder_name); +struct _CamelIMAPXStore * + camel_imapx_conn_manager_ref_store + (CamelIMAPXConnManager *conn_man); +gboolean camel_imapx_conn_manager_connect_sync + (CamelIMAPXConnManager *conn_man, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_disconnect_sync + (CamelIMAPXConnManager *conn_man, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_run_job_sync + (CamelIMAPXConnManager *conn_man, + CamelIMAPXJob *job, + CamelIMAPXJobMatchesFunc finish_before_job, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_list_sync + (CamelIMAPXConnManager *conn_man, + const gchar *pattern, + CamelStoreGetFolderInfoFlags flags, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_refresh_info_sync + (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_sync_changes_sync + (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_expunge_sync + (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error); +CamelStream * camel_imapx_conn_manager_get_message_sync + (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + CamelFolderSummary *summary, + CamelDataCache *message_cache, + const gchar *message_uid, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_copy_message_sync + (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + CamelIMAPXMailbox *destination, + GPtrArray *uids, + gboolean delete_originals, + gboolean remove_deleted_flags, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_append_message_sync + (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + CamelFolderSummary *summary, + CamelDataCache *message_cache, + CamelMimeMessage *message, + const CamelMessageInfo *mi, + gchar **append_uid, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_sync_message_sync + (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + CamelFolderSummary *summary, + CamelDataCache *message_cache, + const gchar *message_uid, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_create_mailbox_sync + (CamelIMAPXConnManager *conn_man, + const gchar *mailbox_name, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_delete_mailbox_sync + (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_rename_mailbox_sync + (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + const gchar *new_mailbox_name, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_subscribe_mailbox_sync + (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_unsubscribe_mailbox_sync + (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_conn_manager_update_quota_info_sync + (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error); +GPtrArray * camel_imapx_conn_manager_uid_search_sync + (CamelIMAPXConnManager *conn_man, + CamelIMAPXMailbox *mailbox, + const gchar *criteria_prefix, + const gchar *search_key, + const gchar * const *words, + GCancellable *cancellable, + GError **error); /* for debugging purposes only */ void camel_imapx_conn_manager_dump_queue_status - (CamelIMAPXConnManager *con_man); + (CamelIMAPXConnManager *conn_man); G_END_DECLS #endif /* _CAMEL_IMAPX_SERVER_H */ diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.c.imapx-update-to-upstream 2014-09-02 17:58:14.000000000 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.c 2016-08-15 13:52:41.950976330 +0200 @@ -1,21 +1,21 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* camel-imap-folder.c : class for a imap folder */ -/* - * Authors: Michael Zucchi +/* camel-imap-folder.c : class for a imap folder * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * - * This library is free software; you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . + * + * Authors: Michael Zucchi */ #ifdef HAVE_CONFIG_H @@ -42,6 +42,8 @@ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), CAMEL_TYPE_IMAPX_FOLDER, CamelIMAPXFolderPrivate)) +extern gint camel_application_is_exiting; + struct _CamelIMAPXFolderPrivate { GMutex property_lock; GWeakRef mailbox; @@ -49,6 +51,8 @@ struct _CamelIMAPXFolderPrivate { GMutex move_to_hash_table_lock; GHashTable *move_to_real_junk_uids; GHashTable *move_to_real_trash_uids; + + gboolean check_folder; }; /* The custom property ID is a CamelArg artifact. @@ -56,16 +60,17 @@ struct _CamelIMAPXFolderPrivate { enum { PROP_0, PROP_MAILBOX, - PROP_APPLY_FILTERS = 0x2501 + PROP_APPLY_FILTERS = 0x2501, + PROP_CHECK_FOLDER = 0x2502 }; G_DEFINE_TYPE (CamelIMAPXFolder, camel_imapx_folder, CAMEL_TYPE_OFFLINE_FOLDER) static gboolean imapx_folder_get_apply_filters (CamelIMAPXFolder *folder); -static void -imapx_folder_claim_move_to_real_junk_uids (CamelIMAPXFolder *folder, - GPtrArray *out_uids_to_copy) +void +camel_imapx_folder_claim_move_to_real_junk_uids (CamelIMAPXFolder *folder, + GPtrArray *out_uids_to_copy) { GList *keys; @@ -82,9 +87,9 @@ imapx_folder_claim_move_to_real_junk_uid } } -static void -imapx_folder_claim_move_to_real_trash_uids (CamelIMAPXFolder *folder, - GPtrArray *out_uids_to_copy) +void +camel_imapx_folder_claim_move_to_real_trash_uids (CamelIMAPXFolder *folder, + GPtrArray *out_uids_to_copy) { GList *keys; @@ -138,6 +143,12 @@ imapx_folder_set_property (GObject *obje g_value_get_boolean (value)); return; + case PROP_CHECK_FOLDER: + camel_imapx_folder_set_check_folder ( + CAMEL_IMAPX_FOLDER (object), + g_value_get_boolean (value)); + return; + case PROP_MAILBOX: camel_imapx_folder_set_mailbox ( CAMEL_IMAPX_FOLDER (object), @@ -162,6 +173,13 @@ imapx_folder_get_property (GObject *obje CAMEL_IMAPX_FOLDER (object))); return; + case PROP_CHECK_FOLDER: + g_value_set_boolean ( + value, + camel_imapx_folder_get_check_folder ( + CAMEL_IMAPX_FOLDER (object))); + return; + case PROP_MAILBOX: g_value_take_object ( value, @@ -409,60 +427,31 @@ imapx_append_message_sync (CamelFolder * { CamelStore *store; CamelIMAPXStore *imapx_store; - CamelIMAPXServer *imapx_server; + CamelIMAPXConnManager *conn_man; CamelIMAPXMailbox *mailbox = NULL; - const gchar *folder_name; - GError *local_error = NULL; gboolean success = FALSE; if (appended_uid != NULL) *appended_uid = NULL; store = camel_folder_get_parent_store (folder); - folder_name = camel_folder_get_full_name (folder); imapx_store = CAMEL_IMAPX_STORE (store); - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error); - - if (imapx_server == NULL) - goto exit; + conn_man = camel_imapx_store_get_conn_manager (imapx_store); mailbox = camel_imapx_folder_list_mailbox ( CAMEL_IMAPX_FOLDER (folder), cancellable, error); - if (mailbox == NULL) { - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); + if (mailbox == NULL) goto exit; - } - success = camel_imapx_server_append_message ( - imapx_server, mailbox, folder->summary, + success = camel_imapx_conn_manager_append_message_sync ( + conn_man, mailbox, folder->summary, CAMEL_IMAPX_FOLDER (folder)->cache, message, - info, appended_uid, cancellable, &local_error); - - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, &local_error); - if (imapx_server) { - success = camel_imapx_server_append_message ( - imapx_server, mailbox, folder->summary, - CAMEL_IMAPX_FOLDER (folder)->cache, message, - info, appended_uid, cancellable, &local_error); - - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - } - } - - if (local_error) - g_propagate_error (error, local_error); + info, appended_uid, cancellable, error); exit: g_clear_object (&mailbox); - g_clear_object (&imapx_server); return success; } @@ -474,28 +463,21 @@ imapx_expunge_sync (CamelFolder *folder, { CamelStore *store; CamelIMAPXStore *imapx_store; - CamelIMAPXServer *imapx_server; + CamelIMAPXConnManager *conn_man; CamelIMAPXMailbox *mailbox = NULL; GError *local_error = NULL; - const gchar *folder_name; gboolean success = FALSE; store = camel_folder_get_parent_store (folder); - folder_name = camel_folder_get_full_name (folder); imapx_store = CAMEL_IMAPX_STORE (store); - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error); - - if (imapx_server == NULL) - goto exit; + conn_man = camel_imapx_store_get_conn_manager (imapx_store); mailbox = camel_imapx_folder_list_mailbox ( CAMEL_IMAPX_FOLDER (folder), cancellable, error); - if (mailbox == NULL) { - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); + if (mailbox == NULL) goto exit; - } if ((store->flags & CAMEL_STORE_VTRASH) == 0) { CamelFolder *trash; @@ -533,28 +515,10 @@ imapx_expunge_sync (CamelFolder *folder, g_clear_error (&local_error); } - success = camel_imapx_server_expunge (imapx_server, mailbox, cancellable, &local_error); - - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, &local_error); - if (imapx_server) { - success = camel_imapx_server_expunge (imapx_server, mailbox, cancellable, &local_error); - - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - } - } - - if (local_error) - g_propagate_error (error, local_error); + success = camel_imapx_conn_manager_expunge_sync (conn_man, mailbox, cancellable, error); exit: g_clear_object (&mailbox); - g_clear_object (&imapx_server); return success; } @@ -612,7 +576,6 @@ imapx_get_message_sync (CamelFolder *fol GIOStream *base_stream; const gchar *path = NULL; gboolean offline_message = FALSE; - GError *local_error = NULL; imapx_folder = CAMEL_IMAPX_FOLDER (folder); store = camel_folder_get_parent_store (folder); @@ -630,9 +593,8 @@ imapx_get_message_sync (CamelFolder *fol stream = camel_stream_new (base_stream); g_object_unref (base_stream); } else { - CamelIMAPXServer *imapx_server; + CamelIMAPXConnManager *conn_man; CamelIMAPXMailbox *mailbox; - const gchar *folder_name; if (offline_message) { g_set_error ( @@ -642,49 +604,20 @@ imapx_get_message_sync (CamelFolder *fol return NULL; } - folder_name = camel_folder_get_full_name (folder); - imapx_server = camel_imapx_store_ref_server ( - CAMEL_IMAPX_STORE (store), folder_name, FALSE, cancellable, error); - - if (imapx_server == NULL) - return NULL; + conn_man = camel_imapx_store_get_conn_manager (CAMEL_IMAPX_STORE (store)); mailbox = camel_imapx_folder_list_mailbox ( CAMEL_IMAPX_FOLDER (folder), cancellable, error); - if (mailbox == NULL) { - camel_imapx_store_folder_op_done (CAMEL_IMAPX_STORE (store), imapx_server, folder_name); - g_object_unref (imapx_server); + if (mailbox == NULL) return NULL; - } - stream = camel_imapx_server_get_message ( - imapx_server, mailbox, folder->summary, + stream = camel_imapx_conn_manager_get_message_sync ( + conn_man, mailbox, folder->summary, CAMEL_IMAPX_FOLDER (folder)->cache, uid, - cancellable, &local_error); - - camel_imapx_store_folder_op_done (CAMEL_IMAPX_STORE (store), imapx_server, folder_name); - - while (!stream && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (CAMEL_IMAPX_STORE (store), folder_name, FALSE, cancellable, &local_error); - if (imapx_server) { - stream = camel_imapx_server_get_message ( - imapx_server, mailbox, folder->summary, - CAMEL_IMAPX_FOLDER (folder)->cache, uid, - cancellable, &local_error); - - camel_imapx_store_folder_op_done (CAMEL_IMAPX_STORE (store), imapx_server, folder_name); - } - } - - if (local_error) - g_propagate_error (error, local_error); + cancellable, error); g_clear_object (&mailbox); - g_clear_object (&imapx_server); } if (stream != NULL) { @@ -734,48 +667,23 @@ imapx_get_quota_info_sync (CamelFolder * { CamelStore *store; CamelIMAPXStore *imapx_store; - CamelIMAPXServer *imapx_server; + CamelIMAPXConnManager *conn_man; CamelIMAPXMailbox *mailbox = NULL; CamelFolderQuotaInfo *quota_info = NULL; - const gchar *folder_name; gchar **quota_roots; gboolean success = FALSE; - GError *local_error = NULL; store = camel_folder_get_parent_store (folder); - folder_name = camel_folder_get_full_name (folder); imapx_store = CAMEL_IMAPX_STORE (store); - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error); - - if (imapx_server == NULL) - goto exit; + conn_man = camel_imapx_store_get_conn_manager (imapx_store); mailbox = camel_imapx_folder_list_mailbox ( CAMEL_IMAPX_FOLDER (folder), cancellable, error); - if (mailbox == NULL) { - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); + if (mailbox == NULL) goto exit; - } - - success = camel_imapx_server_update_quota_info (imapx_server, mailbox, cancellable, &local_error); - - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, &local_error); - if (imapx_server) { - success = camel_imapx_server_update_quota_info (imapx_server, mailbox, cancellable, &local_error); - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - } - } - - if (local_error) - g_propagate_error (error, local_error); + success = camel_imapx_conn_manager_update_quota_info_sync (conn_man, mailbox, cancellable, error); if (!success) goto exit; @@ -798,7 +706,6 @@ imapx_get_quota_info_sync (CamelFolder * exit: g_clear_object (&mailbox); - g_clear_object (&imapx_server); return quota_info; } @@ -821,12 +728,9 @@ imapx_refresh_info_sync (CamelFolder *fo { CamelStore *store; CamelIMAPXStore *imapx_store; - CamelIMAPXServer *imapx_server; + CamelIMAPXConnManager *conn_man; CamelIMAPXMailbox *mailbox = NULL; - CamelFolderChangeInfo *changes; - gchar *folder_name; gboolean success = FALSE; - GError *local_error = NULL; store = camel_folder_get_parent_store (folder); @@ -834,235 +738,14 @@ imapx_refresh_info_sync (CamelFolder *fo if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) return TRUE; - folder_name = g_strdup (camel_folder_get_full_name (folder)); imapx_store = CAMEL_IMAPX_STORE (store); - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, TRUE, cancellable, error); - - if (imapx_server == NULL) - goto exit; - - mailbox = camel_imapx_folder_list_mailbox ( - CAMEL_IMAPX_FOLDER (folder), cancellable, error); - - if (mailbox == NULL) { - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - goto exit; - } - - changes = camel_imapx_server_refresh_info (imapx_server, mailbox, cancellable, &local_error); - - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, TRUE, cancellable, &local_error); - if (imapx_server) { - changes = camel_imapx_server_refresh_info (imapx_server, mailbox, cancellable, &local_error); - - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - } - } - - if (local_error) - g_propagate_error (error, local_error); - - if (changes != NULL) { - if (camel_folder_change_info_changed (changes)) - camel_folder_changed (folder, changes); - camel_folder_change_info_free (changes); - success = TRUE; - } - -exit: - g_clear_object (&mailbox); - g_clear_object (&imapx_server); - g_free (folder_name); - - return success; -} - -/* Helper for imapx_synchronize_sync() */ -static gboolean -imapx_move_to_real_junk (CamelIMAPXServer *imapx_server, - CamelFolder *folder, - GCancellable *cancellable, - gboolean *out_need_to_expunge, - GError **error) -{ - CamelIMAPXFolder *imapx_folder; - CamelIMAPXMailbox *mailbox; - CamelIMAPXSettings *settings; - GPtrArray *uids_to_copy; - gchar *real_junk_path = NULL; - gboolean success = TRUE; + conn_man = camel_imapx_store_get_conn_manager (imapx_store); - *out_need_to_expunge = FALSE; + mailbox = camel_imapx_folder_list_mailbox (CAMEL_IMAPX_FOLDER (folder), cancellable, error); - /* Caller already obtained the mailbox from the folder, - * so the folder should still have it readily available. */ - imapx_folder = CAMEL_IMAPX_FOLDER (folder); - mailbox = camel_imapx_folder_ref_mailbox (imapx_folder); - g_return_val_if_fail (mailbox != NULL, FALSE); - - uids_to_copy = g_ptr_array_new_with_free_func ( - (GDestroyNotify) camel_pstring_free); - - settings = camel_imapx_server_ref_settings (imapx_server); - if (camel_imapx_settings_get_use_real_junk_path (settings)) { - real_junk_path = - camel_imapx_settings_dup_real_junk_path (settings); - imapx_folder_claim_move_to_real_junk_uids ( - imapx_folder, uids_to_copy); + if (mailbox) { + success = camel_imapx_conn_manager_refresh_info_sync (conn_man, mailbox, cancellable, error); } - g_object_unref (settings); - - if (uids_to_copy->len > 0) { - CamelIMAPXStore *imapx_store; - CamelIMAPXMailbox *destination = NULL; - - imapx_store = camel_imapx_server_ref_store (imapx_server); - - if (real_junk_path != NULL) { - folder = camel_store_get_folder_sync ( - CAMEL_STORE (imapx_store), - real_junk_path, 0, - cancellable, error); - } else { - g_set_error ( - error, CAMEL_FOLDER_ERROR, - CAMEL_FOLDER_ERROR_INVALID_PATH, - _("No destination folder specified")); - folder = NULL; - } - - if (folder != NULL) { - destination = camel_imapx_folder_list_mailbox ( - CAMEL_IMAPX_FOLDER (folder), - cancellable, error); - g_object_unref (folder); - } - - /* Avoid duplicating messages in the Junk folder. */ - if (destination == mailbox) { - success = TRUE; - } else if (destination != NULL) { - success = camel_imapx_server_copy_message ( - imapx_server, - mailbox, destination, - uids_to_copy, TRUE, - cancellable, error); - *out_need_to_expunge = success; - } else { - success = FALSE; - } - - if (!success) { - g_prefix_error ( - error, "%s: ", - _("Unable to move junk messages")); - } - - g_clear_object (&destination); - g_clear_object (&imapx_store); - } - - g_ptr_array_unref (uids_to_copy); - g_free (real_junk_path); - - g_clear_object (&mailbox); - - return success; -} - -/* Helper for imapx_synchronize_sync() */ -static gboolean -imapx_move_to_real_trash (CamelIMAPXServer *imapx_server, - CamelFolder *folder, - GCancellable *cancellable, - gboolean *out_need_to_expunge, - GError **error) -{ - CamelIMAPXFolder *imapx_folder; - CamelIMAPXMailbox *mailbox; - CamelIMAPXSettings *settings; - GPtrArray *uids_to_copy; - gchar *real_trash_path = NULL; - gboolean success = TRUE; - - *out_need_to_expunge = FALSE; - - /* Caller already obtained the mailbox from the folder, - * so the folder should still have it readily available. */ - imapx_folder = CAMEL_IMAPX_FOLDER (folder); - mailbox = camel_imapx_folder_ref_mailbox (imapx_folder); - g_return_val_if_fail (mailbox != NULL, FALSE); - - uids_to_copy = g_ptr_array_new_with_free_func ( - (GDestroyNotify) camel_pstring_free); - - settings = camel_imapx_server_ref_settings (imapx_server); - if (camel_imapx_settings_get_use_real_trash_path (settings)) { - real_trash_path = - camel_imapx_settings_dup_real_trash_path (settings); - imapx_folder_claim_move_to_real_trash_uids ( - CAMEL_IMAPX_FOLDER (folder), uids_to_copy); - } - g_object_unref (settings); - - if (uids_to_copy->len > 0) { - CamelIMAPXStore *imapx_store; - CamelIMAPXMailbox *destination = NULL; - - imapx_store = camel_imapx_server_ref_store (imapx_server); - - if (real_trash_path != NULL) { - folder = camel_store_get_folder_sync ( - CAMEL_STORE (imapx_store), - real_trash_path, 0, - cancellable, error); - } else { - g_set_error ( - error, CAMEL_FOLDER_ERROR, - CAMEL_FOLDER_ERROR_INVALID_PATH, - _("No destination folder specified")); - folder = NULL; - } - - if (folder != NULL) { - destination = camel_imapx_folder_list_mailbox ( - CAMEL_IMAPX_FOLDER (folder), - cancellable, error); - g_object_unref (folder); - } - - /* Avoid duplicating messages in the Trash folder. */ - if (destination == mailbox) { - success = TRUE; - } else if (destination != NULL) { - success = camel_imapx_server_copy_message ( - imapx_server, - mailbox, destination, - uids_to_copy, TRUE, - cancellable, error); - *out_need_to_expunge = success; - } else { - success = FALSE; - } - - if (!success) { - g_prefix_error ( - error, "%s: ", - _("Unable to move deleted messages")); - } - - g_clear_object (&destination); - g_clear_object (&imapx_store); - } - - g_ptr_array_unref (uids_to_copy); - g_free (real_trash_path); g_clear_object (&mailbox); @@ -1077,91 +760,33 @@ imapx_synchronize_sync (CamelFolder *fol { CamelStore *store; CamelIMAPXStore *imapx_store; - CamelIMAPXServer *imapx_server; + CamelIMAPXConnManager *conn_man; CamelIMAPXMailbox *mailbox = NULL; - const gchar *folder_name; - gboolean need_to_expunge; gboolean success = FALSE; - GError *local_error = NULL; store = camel_folder_get_parent_store (folder); - folder_name = camel_folder_get_full_name (folder); /* Not connected, thus skip the operation */ if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) return TRUE; imapx_store = CAMEL_IMAPX_STORE (store); - /* while it can be expensive job, do not treat it as such, to avoid a blockage - by really expensive jobs */ - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error); - - if (imapx_server == NULL) - goto exit; - - mailbox = camel_imapx_folder_list_mailbox ( - CAMEL_IMAPX_FOLDER (folder), cancellable, error); - if (mailbox == NULL) { - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - goto exit; - } - - success = camel_imapx_server_sync_changes (imapx_server, mailbox, cancellable, &local_error); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, &local_error); - if (imapx_server) { - success = camel_imapx_server_sync_changes (imapx_server, mailbox, cancellable, &local_error); - } - } - - if (success) { - success = imapx_move_to_real_junk ( - imapx_server, folder, cancellable, - &need_to_expunge, error); - expunge |= need_to_expunge; - } - - if (success) { - success = imapx_move_to_real_trash ( - imapx_server, folder, cancellable, - &need_to_expunge, error); - expunge |= need_to_expunge; - } - - /* Sync twice - make sure deleted flags are written out, - * then sync again incase expunge changed anything */ + conn_man = camel_imapx_store_get_conn_manager (imapx_store); - if (success && expunge) { - success = camel_imapx_server_expunge (imapx_server, mailbox, cancellable, &local_error); + mailbox = camel_imapx_folder_list_mailbox (CAMEL_IMAPX_FOLDER (folder), cancellable, error); - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, &local_error); - if (imapx_server) { - success = camel_imapx_server_expunge (imapx_server, mailbox, cancellable, &local_error); - } + /* Do not update mailboxes on exit which were not entered yet */ + if (mailbox == NULL || (camel_application_is_exiting && + camel_imapx_mailbox_get_permanentflags (mailbox) == ~0)) { + success = mailbox != NULL; + } else { + success = camel_imapx_conn_manager_sync_changes_sync (conn_man, mailbox, cancellable, error); + if (success && expunge && camel_folder_summary_get_deleted_count (folder->summary) > 0) { + success = camel_imapx_conn_manager_expunge_sync (conn_man, mailbox, cancellable, error); } } - if (local_error) - g_propagate_error (error, local_error); - - if (imapx_server) - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - -exit: g_clear_object (&mailbox); - g_clear_object (&imapx_server); return success; } @@ -1174,57 +799,28 @@ imapx_synchronize_message_sync (CamelFol { CamelStore *store; CamelIMAPXStore *imapx_store; - CamelIMAPXServer *imapx_server; + CamelIMAPXConnManager *conn_man; CamelIMAPXMailbox *mailbox = NULL; - const gchar *folder_name; gboolean success = FALSE; - GError *local_error = NULL; store = camel_folder_get_parent_store (folder); - folder_name = camel_folder_get_full_name (folder); imapx_store = CAMEL_IMAPX_STORE (store); - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error); - - if (imapx_server == NULL) - goto exit; + conn_man = camel_imapx_store_get_conn_manager (imapx_store); mailbox = camel_imapx_folder_list_mailbox ( CAMEL_IMAPX_FOLDER (folder), cancellable, error); - if (mailbox == NULL) { - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); + if (mailbox == NULL) goto exit; - } - success = camel_imapx_server_sync_message ( - imapx_server, mailbox, folder->summary, + success = camel_imapx_conn_manager_sync_message_sync ( + conn_man, mailbox, folder->summary, CAMEL_IMAPX_FOLDER (folder)->cache, uid, - cancellable, &local_error); - - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, &local_error); - if (imapx_server) { - success = camel_imapx_server_sync_message ( - imapx_server, mailbox, folder->summary, - CAMEL_IMAPX_FOLDER (folder)->cache, uid, - cancellable, &local_error); - - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - } - } - - if (local_error) - g_propagate_error (error, local_error); + cancellable, error); exit: g_clear_object (&mailbox); - g_clear_object (&imapx_server); return success; } @@ -1240,73 +836,68 @@ imapx_transfer_messages_to_sync (CamelFo { CamelStore *store; CamelIMAPXStore *imapx_store; - CamelIMAPXServer *imapx_server; + CamelIMAPXConnManager *conn_man; CamelIMAPXMailbox *src_mailbox = NULL; CamelIMAPXMailbox *dst_mailbox = NULL; - const gchar *folder_name; gboolean success = FALSE; - GError *local_error = NULL; store = camel_folder_get_parent_store (source); - folder_name = camel_folder_get_full_name (source); imapx_store = CAMEL_IMAPX_STORE (store); - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, error); - - if (imapx_server == NULL) - goto exit; + conn_man = camel_imapx_store_get_conn_manager (imapx_store); src_mailbox = camel_imapx_folder_list_mailbox ( CAMEL_IMAPX_FOLDER (source), cancellable, error); - if (src_mailbox == NULL) { - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); + if (src_mailbox == NULL) goto exit; - } dst_mailbox = camel_imapx_folder_list_mailbox ( CAMEL_IMAPX_FOLDER (dest), cancellable, error); - if (dst_mailbox == NULL) { - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); + if (dst_mailbox == NULL) goto exit; - } - success = camel_imapx_server_copy_message ( - imapx_server, src_mailbox, dst_mailbox, uids, - delete_originals, cancellable, &local_error); + success = camel_imapx_conn_manager_copy_message_sync ( + conn_man, src_mailbox, dst_mailbox, uids, + delete_originals, FALSE, cancellable, error); - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); +exit: + g_clear_object (&src_mailbox); + g_clear_object (&dst_mailbox); - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); + return success; +} - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, FALSE, cancellable, &local_error); - if (imapx_server) { - success = camel_imapx_server_copy_message ( - imapx_server, src_mailbox, dst_mailbox, uids, - delete_originals, cancellable, &local_error); +static void +imapx_folder_changed (CamelFolder *folder, + CamelFolderChangeInfo *info) +{ + g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder)); - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - } - } + if (info && info->uid_removed && info->uid_removed->len) { + CamelIMAPXFolder *imapx_folder; + guint ii; - if (local_error) - g_propagate_error (error, local_error); + imapx_folder = CAMEL_IMAPX_FOLDER (folder); - /* Update destination folder only if it's not frozen, - * to avoid updating for each "move" action on a single - * message while filtering. */ - if (!camel_folder_is_frozen (dest)) - imapx_refresh_info_sync (dest, cancellable, NULL); + g_mutex_lock (&imapx_folder->priv->move_to_hash_table_lock); -exit: - g_clear_object (&src_mailbox); - g_clear_object (&dst_mailbox); - g_clear_object (&imapx_server); + for (ii = 0; ii < info->uid_removed->len; ii++) { + const gchar *message_uid = info->uid_removed->pdata[ii]; - return success; + if (!message_uid) + continue; + + g_hash_table_remove (imapx_folder->priv->move_to_real_trash_uids, message_uid); + g_hash_table_remove (imapx_folder->priv->move_to_real_junk_uids, message_uid); + } + + g_mutex_unlock (&imapx_folder->priv->move_to_hash_table_lock); + } + + /* Chain up to parent's method. */ + CAMEL_FOLDER_CLASS (camel_imapx_folder_parent_class)->changed (folder, info); } static void @@ -1364,6 +955,7 @@ camel_imapx_folder_class_init (CamelIMAP folder_class->synchronize_sync = imapx_synchronize_sync; folder_class->synchronize_message_sync = imapx_synchronize_message_sync; folder_class->transfer_messages_to_sync = imapx_transfer_messages_to_sync; + folder_class->changed = imapx_folder_changed; g_object_class_install_property ( object_class, @@ -1378,6 +970,17 @@ camel_imapx_folder_class_init (CamelIMAP g_object_class_install_property ( object_class, + PROP_CHECK_FOLDER, + g_param_spec_boolean ( + "check-folder", + "Check Folder", + _("Always check for _new mail in this folder"), + FALSE, + G_PARAM_READWRITE | + CAMEL_PARAM_PERSISTENT)); + + g_object_class_install_property ( + object_class, PROP_MAILBOX, g_param_spec_object ( "mailbox", @@ -1396,14 +999,14 @@ camel_imapx_folder_init (CamelIMAPXFolde GHashTable *move_to_real_trash_uids; move_to_real_junk_uids = g_hash_table_new_full ( - (GHashFunc) g_direct_hash, - (GEqualFunc) g_direct_equal, + (GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, (GDestroyNotify) camel_pstring_free, (GDestroyNotify) NULL); move_to_real_trash_uids = g_hash_table_new_full ( - (GHashFunc) g_direct_hash, - (GEqualFunc) g_direct_equal, + (GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, (GDestroyNotify) camel_pstring_free, (GDestroyNotify) NULL); @@ -1449,6 +1052,7 @@ camel_imapx_folder_new (CamelStore *stor gboolean filter_inbox; gboolean filter_junk; gboolean filter_junk_inbox; + gboolean store_offline_sync = FALSE; d ("opening imap folder '%s'\n", folder_dir); @@ -1462,6 +1066,7 @@ camel_imapx_folder_new (CamelStore *stor "filter-inbox", &filter_inbox, "filter-junk", &filter_junk, "filter-junk-inbox", &filter_junk_inbox, + "stay-synchronized", &store_offline_sync, NULL); g_object_unref (settings); @@ -1496,16 +1101,22 @@ camel_imapx_folder_new (CamelStore *stor return NULL; } - /* Ensure cache will never expire, otherwise - * it causes redownload of messages. */ - camel_data_cache_set_expire_age (imapx_folder->cache, -1); - camel_data_cache_set_expire_access (imapx_folder->cache, -1); - state_file = g_build_filename (folder_dir, "cmeta", NULL); camel_object_set_state_filename (CAMEL_OBJECT (folder), state_file); g_free (state_file); camel_object_state_read (CAMEL_OBJECT (folder)); + if (store_offline_sync || camel_offline_folder_get_offline_sync (CAMEL_OFFLINE_FOLDER (folder))) { + /* Ensure cache will never expire, otherwise + * it causes redownload of messages. */ + camel_data_cache_set_expire_age (imapx_folder->cache, -1); + camel_data_cache_set_expire_access (imapx_folder->cache, -1); + } else { + /* Set cache expiration for one week. */ + camel_data_cache_set_expire_age (imapx_folder->cache, 60 * 60 * 24 * 7); + camel_data_cache_set_expire_access (imapx_folder->cache, 60 * 60 * 24 * 7); + } + imapx_folder->search = camel_imapx_search_new (CAMEL_IMAPX_STORE (store)); if (filter_all) @@ -1615,7 +1226,7 @@ camel_imapx_folder_list_mailbox (CamelIM GError **error) { CamelIMAPXStore *imapx_store; - CamelIMAPXServer *server = NULL; + CamelIMAPXConnManager *conn_man; CamelIMAPXMailbox *mailbox; CamelStore *parent_store; CamelStoreInfo *store_info; @@ -1624,7 +1235,6 @@ camel_imapx_folder_list_mailbox (CamelIM gchar *mailbox_name = NULL; gchar *pattern; gboolean success; - GError *local_error = NULL; g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), FALSE); @@ -1661,36 +1271,14 @@ camel_imapx_folder_list_mailbox (CamelIM goto exit; } - server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error); - if (server == NULL) - goto exit; - - mailbox = camel_imapx_store_ref_mailbox (imapx_store, mailbox_name); - if (mailbox != NULL) { - camel_imapx_folder_set_mailbox (folder, mailbox); - goto exit; - } - /* Last resort is to issue a LIST command. Maintainer should * monitor IMAP logs to make sure this is rarely if ever used. */ pattern = camel_utf8_utf7 (mailbox_name); /* This creates a mailbox instance from the LIST response. */ - success = camel_imapx_server_list (server, pattern, 0, cancellable, &local_error); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&server); - - server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error); - if (server) { - success = camel_imapx_server_list (server, pattern, 0, cancellable, &local_error); - } - } - - if (local_error) - g_propagate_error (error, local_error); + conn_man = camel_imapx_store_get_conn_manager (imapx_store); + success = camel_imapx_conn_manager_list_sync (conn_man, pattern, 0, cancellable, error); g_free (pattern); @@ -1711,8 +1299,6 @@ camel_imapx_folder_list_mailbox (CamelIM } exit: - g_clear_object (&server); - g_free (folder_path); g_free (mailbox_name); @@ -1778,6 +1364,7 @@ camel_imapx_folder_add_move_to_real_junk { g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder)); g_return_if_fail (message_uid != NULL); + g_return_if_fail (camel_folder_summary_check_uid (CAMEL_FOLDER (folder)->summary, message_uid)); g_mutex_lock (&folder->priv->move_to_hash_table_lock); @@ -1808,6 +1395,7 @@ camel_imapx_folder_add_move_to_real_tras { g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder)); g_return_if_fail (message_uid != NULL); + g_return_if_fail (camel_folder_summary_check_uid (CAMEL_FOLDER (folder)->summary, message_uid)); g_mutex_lock (&folder->priv->move_to_hash_table_lock); @@ -1867,3 +1455,26 @@ camel_imapx_folder_invalidate_local_cach camel_folder_summary_free_array (array); } +gboolean +camel_imapx_folder_get_check_folder (CamelIMAPXFolder *folder) +{ + g_return_val_if_fail (folder != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), FALSE); + + return folder->priv->check_folder; +} + +void +camel_imapx_folder_set_check_folder (CamelIMAPXFolder *folder, + gboolean check_folder) +{ + g_return_if_fail (folder != NULL); + g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder)); + + if (folder->priv->check_folder == check_folder) + return; + + folder->priv->check_folder = check_folder; + + g_object_notify (G_OBJECT (folder), "check-folder"); +} diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.h.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-folder.h 2016-08-15 13:52:41.950976330 +0200 @@ -1,22 +1,21 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* camel-imap-folder.h : Class for a IMAP folder */ - -/* - * Authors: Michael Zucchi +/* camel-imap-folder.h : Class for a IMAP folder * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * - * This library is free software; you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . + * + * Authors: Michael Zucchi */ #ifndef CAMEL_IMAPX_FOLDER_H @@ -92,6 +91,17 @@ void camel_imapx_folder_add_move_to_rea void camel_imapx_folder_invalidate_local_cache (CamelIMAPXFolder *folder, guint64 new_uidvalidity); +gboolean camel_imapx_folder_get_check_folder + (CamelIMAPXFolder *folder); +void camel_imapx_folder_set_check_folder + (CamelIMAPXFolder *folder, + gboolean check_folder); +void camel_imapx_folder_claim_move_to_real_junk_uids + (CamelIMAPXFolder *folder, + GPtrArray *out_uids_to_copy); +void camel_imapx_folder_claim_move_to_real_trash_uids + (CamelIMAPXFolder *folder, + GPtrArray *out_uids_to_copy); G_END_DECLS diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.c.imapx-update-to-upstream 2014-07-22 12:03:27.000000000 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.c 2016-08-15 13:52:41.951976330 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-input-stream.h * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ @@ -67,9 +67,17 @@ imapx_input_stream_fill (CamelIMAPXInput GInputStream *base_stream; gint left = 0; + if (g_cancellable_is_cancelled (cancellable)) + return -1; + base_stream = g_filter_input_stream_get_base_stream ( G_FILTER_INPUT_STREAM (is)); + if (error && *error) { + g_warning ("%s: Avoiding GIO call with a filled error '%s'", G_STRFUNC, (*error)->message); + error = NULL; + } + left = is->priv->end - is->priv->ptr; memcpy (is->priv->buf, is->priv->ptr, left); is->priv->end = is->priv->buf + left; @@ -136,6 +144,11 @@ imapx_input_stream_read (GInputStream *s memcpy (buffer, priv->ptr, max); priv->ptr += max; } else { + if (error && *error) { + g_warning ("%s: Avoiding GIO call with a filled error '%s'", G_STRFUNC, (*error)->message); + error = NULL; + } + max = MIN (priv->literal, count); max = g_input_stream_read ( base_stream, buffer, max, cancellable, error); @@ -202,6 +215,11 @@ imapx_input_stream_read_nonblocking (GPo pollable_stream = G_POLLABLE_INPUT_STREAM (base_stream); + if (error && *error) { + g_warning ("%s: Avoiding GIO call with a filled error '%s'", G_STRFUNC, (*error)->message); + error = NULL; + } + /* XXX The function takes a GCancellable but the class method * does not. Should be okay to pass NULL here since this * is just a pass-through. */ @@ -344,7 +362,7 @@ camel_imapx_input_stream_atom (CamelIMAP default: g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "expecting atom"); return FALSE; } @@ -451,7 +469,7 @@ camel_imapx_input_stream_astring (CamelI default: g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "expecting astring"); return FALSE; } @@ -511,7 +529,7 @@ camel_imapx_input_stream_nstring (CamelI default: g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "expecting nstring"); return FALSE; } @@ -579,7 +597,7 @@ camel_imapx_input_stream_nstring_bytes ( default: g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "nstring: token not string"); return FALSE; } @@ -611,7 +629,7 @@ camel_imapx_input_stream_number (CamelIM default: g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "expecting number"); return FALSE; } @@ -692,7 +710,10 @@ camel_imapx_input_stream_token (CamelIMA return is->priv->unget_tok; } - if (is->priv->literal > 0) + *data = NULL; + *len = 0; + + if (is->priv->literal > 0 && !g_cancellable_is_cancelled (cancellable)) g_warning ( "stream_token called with literal %d", is->priv->literal); @@ -859,7 +880,7 @@ protocol_error: else is->priv->ptr = p; - g_set_error (error, CAMEL_IMAPX_ERROR, 1, "protocol error"); + g_set_error (error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "protocol error"); return IMAPX_TOK_ERROR; } diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.h.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-input-stream.h 2016-08-15 13:52:41.951976330 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-input-stream.h * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ @@ -49,6 +49,11 @@ typedef struct _CamelIMAPXInputStreamCla typedef struct _CamelIMAPXInputStreamPrivate CamelIMAPXInputStreamPrivate; typedef enum { + CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED = 1, + CAMEL_IMAPX_ERROR_IGNORE /* may ignore such errors */ +} CamelIMAPXError; + +typedef enum { IMAPX_TOK_ERROR = -1, IMAPX_TOK_TOKEN = 256, IMAPX_TOK_STRING, diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.c.imapx-update-to-upstream 2016-08-15 13:52:41.891976333 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.c 2016-08-15 13:52:41.952976330 +0200 @@ -1,495 +1,545 @@ /* * camel-imapx-job.c * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ - -#include "camel-imapx-job.h" +#ifdef HAVE_CONFIG_H +#include +#endif #include -#include "camel-imapx-folder.h" - -typedef struct _CamelIMAPXRealJob CamelIMAPXRealJob; +#include "camel-imapx-job.h" -/* CamelIMAPXJob + some private bits */ -struct _CamelIMAPXRealJob { - CamelIMAPXJob public; +G_LOCK_DEFINE_STATIC (get_kind_name_funcs); +static GSList *get_kind_name_funcs = NULL; - volatile gint ref_count; +const gchar * +camel_imapx_job_get_kind_name (guint32 job_kind) +{ + GSList *link; - GCancellable *cancellable; + switch ((CamelIMAPXJobKind) job_kind) { + case CAMEL_IMAPX_JOB_UNKNOWN: + return "UNKNOWN"; + case CAMEL_IMAPX_JOB_CAPABILITY: + return "CAPABILITY"; + case CAMEL_IMAPX_JOB_STARTTLS: + return "STARTTLS"; + case CAMEL_IMAPX_JOB_AUTHENTICATE: + return "AUTHENTICATE"; + case CAMEL_IMAPX_JOB_LOGIN: + return "LOGIN"; + case CAMEL_IMAPX_JOB_NAMESPACE: + return "NAMESPACE"; + case CAMEL_IMAPX_JOB_SELECT: + return "SELECT"; + case CAMEL_IMAPX_JOB_STATUS: + return "STATUS"; + case CAMEL_IMAPX_JOB_ENABLE: + return "ENABLE"; + case CAMEL_IMAPX_JOB_NOTIFY: + return "NOTIFY"; + case CAMEL_IMAPX_JOB_GET_MESSAGE: + return "GET_MESSAGE"; + case CAMEL_IMAPX_JOB_SYNC_MESSAGE: + return "SYNC_MESSAGE"; + case CAMEL_IMAPX_JOB_APPEND_MESSAGE: + return "APPEND_MESSAGE"; + case CAMEL_IMAPX_JOB_COPY_MESSAGE: + return "COPY_MESSAGE"; + case CAMEL_IMAPX_JOB_MOVE_MESSAGE: + return "MOVE_MESSAGE"; + case CAMEL_IMAPX_JOB_FETCH_NEW_MESSAGES: + return "FETCH_NEW_MESSAGES"; + case CAMEL_IMAPX_JOB_REFRESH_INFO: + return "REFRESH_INFO"; + case CAMEL_IMAPX_JOB_SYNC_CHANGES: + return "SYNC_CHANGES"; + case CAMEL_IMAPX_JOB_EXPUNGE: + return "EXPUNGE"; + case CAMEL_IMAPX_JOB_NOOP: + return "NOOP"; + case CAMEL_IMAPX_JOB_IDLE: + return "IDLE"; + case CAMEL_IMAPX_JOB_DONE: + return "DONE"; + case CAMEL_IMAPX_JOB_LIST: + return "LIST"; + case CAMEL_IMAPX_JOB_LSUB: + return "LSUB"; + case CAMEL_IMAPX_JOB_CREATE_MAILBOX: + return "CREATE_MAILBOX"; + case CAMEL_IMAPX_JOB_DELETE_MAILBOX: + return "DELETE_MAILBOX"; + case CAMEL_IMAPX_JOB_RENAME_MAILBOX: + return "RENAME_MAILBOX"; + case CAMEL_IMAPX_JOB_SUBSCRIBE_MAILBOX: + return "SUBSCRIBE_MAILBOX"; + case CAMEL_IMAPX_JOB_UNSUBSCRIBE_MAILBOX: + return "UNSUBSCRIBE_MAILBOX"; + case CAMEL_IMAPX_JOB_UPDATE_QUOTA_INFO: + return "UPDATE_QUOTA_INFO"; + case CAMEL_IMAPX_JOB_UID_SEARCH: + return "UID_SEARCH"; + case CAMEL_IMAPX_JOB_LAST: + break; + } - /* This is set by camel_imapx_job_take_error(), - * and propagated through camel_imapx_job_wait(). */ - GError *error; + G_LOCK (get_kind_name_funcs); - /* Used for running some jobs synchronously. */ - GCond done_cond; - GMutex done_mutex; - gboolean done_flag; + for (link = get_kind_name_funcs; link; link = g_slist_next (link)) { + CamelIMAPXJobGetKindNameFunc get_kind_name = link->data; - /* Extra job-specific data. */ - gpointer data; - GDestroyNotify destroy_data; + if (get_kind_name) { + const gchar *name = get_kind_name (job_kind); - CamelIMAPXMailbox *mailbox; - GMutex mailbox_lock; + if (name) { + G_UNLOCK (get_kind_name_funcs); + return name; + } + } + } - CamelIMAPXMailbox *guard_mailbox_update; /* uses the mailbox_lock */ -}; + G_UNLOCK (get_kind_name_funcs); -static void -imapx_job_cancelled_cb (GCancellable *cancellable, - CamelIMAPXJob *job) -{ - /* Unblock camel_imapx_run_job() immediately. - * - * If camel_imapx_job_done() is called sometime later, - * the GCond will broadcast but no one will be listening. */ + if (job_kind == CAMEL_IMAPX_JOB_LAST) + return "LAST"; - camel_imapx_job_done (job); + return "???"; } -CamelIMAPXJob * -camel_imapx_job_new (GCancellable *cancellable) +void +camel_imapx_job_register_get_kind_name_func (CamelIMAPXJobGetKindNameFunc get_kind_name) { - CamelIMAPXRealJob *real_job; - - real_job = g_slice_new0 (CamelIMAPXRealJob); - - /* Initialize private bits. */ - real_job->ref_count = 1; - g_cond_init (&real_job->done_cond); - g_mutex_init (&real_job->done_mutex); + g_return_if_fail (get_kind_name != NULL); - if (cancellable != NULL) - g_object_ref (cancellable); - real_job->cancellable = cancellable; + G_LOCK (get_kind_name_funcs); - g_mutex_init (&real_job->mailbox_lock); + if (!g_slist_find (get_kind_name_funcs, get_kind_name)) + get_kind_name_funcs = g_slist_prepend (get_kind_name_funcs, get_kind_name); - return (CamelIMAPXJob *) real_job; + G_UNLOCK (get_kind_name_funcs); } -CamelIMAPXJob * -camel_imapx_job_ref (CamelIMAPXJob *job) +void +camel_imapx_job_unregister_get_kind_name_func (CamelIMAPXJobGetKindNameFunc get_kind_name) { - CamelIMAPXRealJob *real_job; - - g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), NULL); + g_return_if_fail (get_kind_name != NULL); - real_job = (CamelIMAPXRealJob *) job; + G_LOCK (get_kind_name_funcs); - g_atomic_int_inc (&real_job->ref_count); + g_warn_if_fail (g_slist_find (get_kind_name_funcs, get_kind_name)); + get_kind_name_funcs = g_slist_remove (get_kind_name_funcs, get_kind_name); - return job; + G_UNLOCK (get_kind_name_funcs); } -void -camel_imapx_job_unref (CamelIMAPXJob *job) -{ - CamelIMAPXRealJob *real_job; +struct _CamelIMAPXJob { + volatile gint ref_count; - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); + guint32 job_kind; + CamelIMAPXMailbox *mailbox; - real_job = (CamelIMAPXRealJob *) job; + CamelIMAPXJobRunSyncFunc run_sync; + CamelIMAPXJobMatchesFunc matches; + CamelIMAPXJobCopyResultFunc copy_result; - if (g_atomic_int_dec_and_test (&real_job->ref_count)) { + /* Extra job-specific data. */ + gpointer user_data; + GDestroyNotify destroy_user_data; - /* Free the public stuff. */ + gboolean result_is_set; + gboolean result_success; + gpointer result_data; + GError *result_error; + GDestroyNotify destroy_result_data; - if (real_job->public.pop_operation_msg) - camel_operation_pop_message (real_job->cancellable); + GCond done_cond; + GMutex done_mutex; + gboolean is_done; - /* Free the private stuff. */ + GCancellable *abort_cancellable; +}; - if (real_job->cancellable != NULL) - g_object_unref (real_job->cancellable); +CamelIMAPXJob * +camel_imapx_job_new (guint32 job_kind, + CamelIMAPXMailbox *mailbox, + CamelIMAPXJobRunSyncFunc run_sync, + CamelIMAPXJobMatchesFunc matches, + CamelIMAPXJobCopyResultFunc copy_result) +{ + CamelIMAPXJob *job; + + g_return_val_if_fail (run_sync != NULL, NULL); + + job = g_new0 (CamelIMAPXJob, 1); + job->ref_count = 1; + job->job_kind = job_kind; + job->mailbox = mailbox ? g_object_ref (mailbox) : NULL; + job->run_sync = run_sync; + job->matches = matches; + job->copy_result = copy_result; + job->abort_cancellable = camel_operation_new (); + job->is_done = FALSE; - g_clear_error (&real_job->error); + g_cond_init (&job->done_cond); + g_mutex_init (&job->done_mutex); - g_cond_clear (&real_job->done_cond); - g_mutex_clear (&real_job->done_mutex); + return job; +} - if (real_job->destroy_data != NULL) - real_job->destroy_data (real_job->data); +CamelIMAPXJob * +camel_imapx_job_ref (CamelIMAPXJob *job) +{ + g_return_val_if_fail (job != NULL, NULL); - g_mutex_lock (&real_job->mailbox_lock); - if (real_job->guard_mailbox_update) { - camel_imapx_mailbox_unlock_update (real_job->guard_mailbox_update); - g_clear_object (&real_job->guard_mailbox_update); - } - g_mutex_unlock (&real_job->mailbox_lock); + g_atomic_int_inc (&job->ref_count); - g_clear_object (&real_job->mailbox); - g_mutex_clear (&real_job->mailbox_lock); + return job; +} - /* Fill the memory with a bit pattern before releasing - * it back to the slab allocator, so we can more easily - * identify dangling CamelIMAPXJob pointers. */ - memset (real_job, 0xaa, sizeof (CamelIMAPXRealJob)); +void +camel_imapx_job_unref (CamelIMAPXJob *job) +{ + g_return_if_fail (job != NULL); - /* But leave the reference count set to zero, so - * CAMEL_IS_IMAPX_JOB can identify it as bad. */ - real_job->ref_count = 0; + if (g_atomic_int_dec_and_test (&job->ref_count)) { + if (job->destroy_user_data) + job->destroy_user_data (job->user_data); - g_slice_free (CamelIMAPXRealJob, real_job); - } -} + if (job->result_is_set && job->destroy_result_data) + job->destroy_result_data (job->result_data); -gboolean -camel_imapx_job_check (CamelIMAPXJob *job) -{ - CamelIMAPXRealJob *real_job; + g_clear_object (&job->mailbox); + g_clear_object (&job->abort_cancellable); + g_clear_error (&job->result_error); + + g_cond_clear (&job->done_cond); + g_mutex_clear (&job->done_mutex); - real_job = (CamelIMAPXRealJob *) job; + job->ref_count = 0xdeadbeef; - return (real_job != NULL && real_job->ref_count > 0); + g_free (job); + } } -void -camel_imapx_job_cancel (CamelIMAPXJob *job) +guint32 +camel_imapx_job_get_kind (CamelIMAPXJob *job) { - CamelIMAPXRealJob *real_job; + g_return_val_if_fail (job != NULL, CAMEL_IMAPX_JOB_UNKNOWN); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); + return job->job_kind; +} - real_job = (CamelIMAPXRealJob *) job; +CamelIMAPXMailbox * +camel_imapx_job_get_mailbox (CamelIMAPXJob *job) +{ + g_return_val_if_fail (job != NULL, NULL); - g_cancellable_cancel (real_job->cancellable); + return job->mailbox; } -/** - * camel_imapx_job_wait: - * @job: a #CamelIMAPXJob - * @error: return location for a #GError, or %NULL - * - * Blocks until @job completes by way of camel_imapx_job_done(). If @job - * completed successfully, the function returns %TRUE. If @job was given - * a #GError by way of camel_imapx_job_take_error(), or its #GCancellable - * was cancelled, the function sets @error and returns %FALSE. - * - * Returns: whether @job completed successfully - * - * Since: 3.10 - **/ -gboolean -camel_imapx_job_wait (CamelIMAPXJob *job, - GError **error) +gpointer +camel_imapx_job_get_user_data (CamelIMAPXJob *job) { - CamelIMAPXRealJob *real_job; - GCancellable *cancellable; - gulong cancel_id = 0; - gboolean success = TRUE; - - g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE); - - real_job = (CamelIMAPXRealJob *) job; - cancellable = camel_imapx_job_get_cancellable (job); - - if (G_IS_CANCELLABLE (cancellable)) - cancel_id = g_cancellable_connect ( - cancellable, - G_CALLBACK (imapx_job_cancelled_cb), - camel_imapx_job_ref (job), - (GDestroyNotify) camel_imapx_job_unref); - - g_mutex_lock (&real_job->done_mutex); - while (!real_job->done_flag && !g_cancellable_is_cancelled (cancellable)) - g_cond_wait ( - &real_job->done_cond, - &real_job->done_mutex); - g_mutex_unlock (&real_job->done_mutex); - - if (cancel_id > 0) - g_cancellable_disconnect (cancellable, cancel_id); - - /* Cancellation takes priority over other errors. */ - if (g_cancellable_set_error_if_cancelled (cancellable, error)) { - success = FALSE; - } else if (real_job->error != NULL) { - /* Copy the error, don't propagate it. - * We want our GError to remain intact. */ - if (error != NULL) { - g_warn_if_fail (*error == NULL); - *error = g_error_copy (real_job->error); - } - success = FALSE; - } + g_return_val_if_fail (job != NULL, NULL); - return success; + return job->user_data; } void -camel_imapx_job_done (CamelIMAPXJob *job) +camel_imapx_job_set_user_data (CamelIMAPXJob *job, + gpointer user_data, + GDestroyNotify destroy_user_data) { - CamelIMAPXRealJob *real_job; - - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); + g_return_if_fail (job != NULL); - real_job = (CamelIMAPXRealJob *) job; - - g_mutex_lock (&real_job->mailbox_lock); - if (real_job->guard_mailbox_update) { - camel_imapx_mailbox_unlock_update (real_job->guard_mailbox_update); - g_clear_object (&real_job->guard_mailbox_update); - } - g_mutex_unlock (&real_job->mailbox_lock); + if (job->destroy_user_data) + job->destroy_user_data (job->user_data); - g_mutex_lock (&real_job->done_mutex); - real_job->done_flag = TRUE; - g_cond_broadcast (&real_job->done_cond); - g_mutex_unlock (&real_job->done_mutex); + job->user_data = user_data; + job->destroy_user_data = destroy_user_data; } gboolean -camel_imapx_job_run (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GError **error) +camel_imapx_job_was_cancelled (CamelIMAPXJob *job) { - GCancellable *cancellable; - gboolean success; + g_return_val_if_fail (job != NULL, FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_return_val_if_fail (job->start != NULL, FALSE); - - cancellable = ((CamelIMAPXRealJob *) job)->cancellable; - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) + if (!job->result_is_set) return FALSE; - success = job->start (job, is, cancellable, error); - - if (success && !job->noreply) - success = camel_imapx_job_wait (job, error); - - return success; + return g_error_matches (job->result_error, G_IO_ERROR, G_IO_ERROR_CANCELLED); } void -camel_imapx_job_guard_mailbox_update (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox) -{ - CamelIMAPXRealJob *real_job; - - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); - - if (mailbox) - g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); - - real_job = (CamelIMAPXRealJob *) job; - - g_mutex_lock (&real_job->mailbox_lock); - - if (mailbox != real_job->guard_mailbox_update) { - if (real_job->guard_mailbox_update) { - camel_imapx_mailbox_unlock_update (real_job->guard_mailbox_update); - g_clear_object (&real_job->guard_mailbox_update); - } - - if (mailbox) { - real_job->guard_mailbox_update = g_object_ref (mailbox); - camel_imapx_mailbox_lock_update (real_job->guard_mailbox_update); - } +camel_imapx_job_set_result (CamelIMAPXJob *job, + gboolean success, + gpointer result, + const GError *error, + GDestroyNotify destroy_result) +{ + g_return_if_fail (job != NULL); + + if (job->result_is_set) { + if (job->destroy_result_data) + job->destroy_result_data (job->result_data); + g_clear_error (&job->result_error); } - g_mutex_unlock (&real_job->mailbox_lock); + job->result_is_set = TRUE; + job->result_success = success; + job->result_data = result; + job->destroy_result_data = destroy_result; + + if (error) + job->result_error = g_error_copy (error); } +/* This doesn't return whether the job succeeded, but whether the result + was set for the job, thus some result copied. All out-arguments are optional. */ gboolean -camel_imapx_job_matches (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox, - const gchar *uid) +camel_imapx_job_copy_result (CamelIMAPXJob *job, + gboolean *out_success, + gpointer *out_result, + GError **out_error, + GDestroyNotify *out_destroy_result) { - /* XXX CamelIMAPXMailbox can be NULL. I'm less sure about - * the message UID but let's assume that can be NULL too. */ + g_return_val_if_fail (job != NULL, FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE); + if (!job->result_is_set) + return FALSE; - if (mailbox != NULL) - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + if (out_success) + *out_success = job->result_success; - if (job->matches == NULL) - return FALSE; + if (out_result) { + *out_result = NULL; - return job->matches (job, mailbox, uid); -} + if (job->copy_result) { + job->copy_result (job, job->result_data, out_result); + } else if (job->result_data) { + g_warn_if_reached (); + } + } -gpointer -camel_imapx_job_get_data (CamelIMAPXJob *job) -{ - CamelIMAPXRealJob *real_job; + if (out_error) { + g_warn_if_fail (*out_error == NULL); - g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), NULL); + if (job->result_error) + *out_error = g_error_copy (job->result_error); + } - real_job = (CamelIMAPXRealJob *) job; + if (out_destroy_result) + *out_destroy_result = job->destroy_result_data; - return real_job->data; + return TRUE; } -void -camel_imapx_job_set_data (CamelIMAPXJob *job, - gpointer data, - GDestroyNotify destroy_data) +/* Similar to camel_imapx_job_copy_result() except it gives result data + to the caller and unsets (not frees) the data in the job. */ +gboolean +camel_imapx_job_take_result_data (CamelIMAPXJob *job, + gpointer *out_result) { - CamelIMAPXRealJob *real_job; + g_return_val_if_fail (job != NULL, FALSE); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); + if (!job->result_is_set) + return FALSE; + + if (out_result) { + *out_result = job->result_data; + } else if (job->destroy_result_data) { + job->destroy_result_data (job->result_data); + } - real_job = (CamelIMAPXRealJob *) job; + job->result_data = NULL; + g_clear_error (&job->result_error); - if (real_job->destroy_data != NULL) - real_job->destroy_data (real_job->data); + job->result_is_set = FALSE; - real_job->data = data; - real_job->destroy_data = destroy_data; + return TRUE; } gboolean -camel_imapx_job_has_mailbox (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox) +camel_imapx_job_matches (CamelIMAPXJob *job, + CamelIMAPXJob *other_job) { - CamelIMAPXRealJob *real_job; + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (other_job != NULL, FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE); - - if (mailbox != NULL) - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + if (job->job_kind != other_job->job_kind) + return FALSE; - real_job = (CamelIMAPXRealJob *) job; + if (job->mailbox != other_job->mailbox) + return FALSE; - /* Not necessary to lock the mutex since - * we're just comparing memory addresses. */ + if (job->matches) + return job->matches (job, other_job); - return (mailbox == real_job->mailbox); + return TRUE; } -CamelIMAPXMailbox * -camel_imapx_job_ref_mailbox (CamelIMAPXJob *job) +static void +imapx_job_cancelled_cb (GCancellable *cancellable, + CamelIMAPXJob *job) { - CamelIMAPXRealJob *real_job; - CamelIMAPXMailbox *mailbox = NULL; - - g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), NULL); - - real_job = (CamelIMAPXRealJob *) job; + camel_imapx_job_abort (job); +} - g_mutex_lock (&real_job->mailbox_lock); +static void +imapx_job_push_message_cb (CamelOperation *operation, + const gchar *message, + GCancellable *job_cancellable) +{ + g_return_if_fail (CAMEL_IS_OPERATION (operation)); + g_return_if_fail (CAMEL_IS_OPERATION (job_cancellable)); - if (real_job->mailbox != NULL) - mailbox = g_object_ref (real_job->mailbox); + camel_operation_push_message (job_cancellable, "%s", message); +} - g_mutex_unlock (&real_job->mailbox_lock); +static void +imapx_job_pop_message_cb (CamelOperation *operation, + GCancellable *job_cancellable) +{ + g_return_if_fail (CAMEL_IS_OPERATION (operation)); + g_return_if_fail (CAMEL_IS_OPERATION (job_cancellable)); - return mailbox; + camel_operation_pop_message (job_cancellable); } -void -camel_imapx_job_set_mailbox (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox) +static void +imapx_job_progress_cb (CamelOperation *operation, + gint percent, + GCancellable *job_cancellable) { - CamelIMAPXRealJob *real_job; + g_return_if_fail (CAMEL_IS_OPERATION (operation)); + g_return_if_fail (CAMEL_IS_OPERATION (job_cancellable)); + + camel_operation_progress (job_cancellable, percent); +} - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); +gboolean +camel_imapx_job_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error) +{ + GError *local_error = NULL; + gboolean success = FALSE; + + g_return_val_if_fail (job != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), FALSE); + g_return_val_if_fail (job->run_sync != NULL, FALSE); + + g_mutex_lock (&job->done_mutex); + job->is_done = FALSE; + g_mutex_unlock (&job->done_mutex); + + g_cancellable_reset (job->abort_cancellable); + + if (!g_cancellable_set_error_if_cancelled (cancellable, error)) { + gulong cancelled_handler_id = 0; + gulong push_message_handler_id = 0; + gulong pop_message_handler_id = 0; + gulong progress_handler_id = 0; + + /* Proxy signals between job's cancellable and the abort_cancellable */ + if (cancellable) + cancelled_handler_id = g_cancellable_connect (cancellable, + G_CALLBACK (imapx_job_cancelled_cb), job, NULL); + + if (CAMEL_IS_OPERATION (cancellable)) { + push_message_handler_id = g_signal_connect (job->abort_cancellable, "push-message", + G_CALLBACK (imapx_job_push_message_cb), cancellable); + pop_message_handler_id = g_signal_connect (job->abort_cancellable, "pop-message", + G_CALLBACK (imapx_job_pop_message_cb), cancellable); + progress_handler_id = g_signal_connect (job->abort_cancellable, "progress", + G_CALLBACK (imapx_job_progress_cb), cancellable); + } - real_job = (CamelIMAPXRealJob *) job; + success = job->run_sync (job, server, job->abort_cancellable, &local_error); - if (mailbox != NULL) { - g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); - g_object_ref (mailbox); + if (push_message_handler_id) + g_signal_handler_disconnect (job->abort_cancellable, push_message_handler_id); + if (pop_message_handler_id) + g_signal_handler_disconnect (job->abort_cancellable, pop_message_handler_id); + if (progress_handler_id) + g_signal_handler_disconnect (job->abort_cancellable, progress_handler_id); + if (cancelled_handler_id) + g_cancellable_disconnect (cancellable, cancelled_handler_id); } - g_mutex_lock (&real_job->mailbox_lock); - - g_clear_object (&real_job->mailbox); - real_job->mailbox = mailbox; + if (local_error) + g_propagate_error (error, local_error); - g_mutex_unlock (&real_job->mailbox_lock); + return success; } -GCancellable * -camel_imapx_job_get_cancellable (CamelIMAPXJob *job) +void +camel_imapx_job_done (CamelIMAPXJob *job) { - CamelIMAPXRealJob *real_job; - - g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), NULL); - - real_job = (CamelIMAPXRealJob *) job; + g_return_if_fail (job != NULL); - return real_job->cancellable; + g_mutex_lock (&job->done_mutex); + job->is_done = TRUE; + g_cond_broadcast (&job->done_cond); + g_mutex_unlock (&job->done_mutex); } -/** - * camel_imapx_job_take_error: - * @job: a #CamelIMAPXJob - * @error: a #GError - * - * Takes over the caller's ownership of @error, so the caller does not - * need to free it any more. Call this when a #CamelIMAPXCommand fails - * and the @job is to be aborted. - * - * The @error will be returned to callers of camel_imapx_job_wait() or - * camel_imapx_job_run(). - * - * Since: 3.10 - **/ void -camel_imapx_job_take_error (CamelIMAPXJob *job, - GError *error) +camel_imapx_job_abort (CamelIMAPXJob *job) { - CamelIMAPXRealJob *real_job; + g_return_if_fail (job != NULL); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); - g_return_if_fail (error != NULL); + g_cancellable_cancel (job->abort_cancellable); +} - real_job = (CamelIMAPXRealJob *) job; - g_return_if_fail (real_job->error != error); +static void +camel_imapx_job_wait_cancelled_cb (GCancellable *cancellable, + gpointer user_data) +{ + CamelIMAPXJob *job = user_data; - g_clear_error (&real_job->error); + g_return_if_fail (job != NULL); - real_job->error = error; /* takes ownership */ + g_mutex_lock (&job->done_mutex); + g_cond_broadcast (&job->done_cond); + g_mutex_unlock (&job->done_mutex); } -/** - * camel_imapx_job_set_error_if_failed: - * @job: a #CamelIMAPXJob - * @error: a location for a #GError - * - * Sets @error to a new GError instance and returns TRUE, if the job has set - * an error or when it was cancelled. - * - * Returns: Whether the job failed. - * - * Since: 3.12.4 - **/ -gboolean -camel_imapx_job_set_error_if_failed (CamelIMAPXJob *job, - GError **error) +void +camel_imapx_job_wait_sync (CamelIMAPXJob *job, + GCancellable *cancellable) { - CamelIMAPXRealJob *real_job; + gulong handler_id = 0; + + g_return_if_fail (job != NULL); - g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), TRUE); - g_return_val_if_fail (error != NULL, TRUE); + if (g_cancellable_is_cancelled (cancellable)) + return; - real_job = (CamelIMAPXRealJob *) job; + if (cancellable) + handler_id = g_cancellable_connect (cancellable, G_CALLBACK (camel_imapx_job_wait_cancelled_cb), job, NULL); - if (real_job->error) { - g_propagate_error (error, g_error_copy (real_job->error)); - return TRUE; + g_mutex_lock (&job->done_mutex); + while (!job->is_done && !g_cancellable_is_cancelled (cancellable)) { + g_cond_wait (&job->done_cond, &job->done_mutex); } + g_mutex_unlock (&job->done_mutex); - return g_cancellable_set_error_if_cancelled (real_job->cancellable, error); + if (handler_id) + g_cancellable_disconnect (cancellable, handler_id); } diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.h.imapx-update-to-upstream 2016-08-15 13:52:41.892976333 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-job.h 2016-08-15 13:52:41.952976330 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-job.h * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ @@ -20,72 +20,103 @@ #include "camel-imapx-server.h" -#define CAMEL_IS_IMAPX_JOB(job) \ - (camel_imapx_job_check (job)) - G_BEGIN_DECLS typedef struct _CamelIMAPXJob CamelIMAPXJob; -struct _uidset_state { - gint entries, uids; - gint total, limit; - guint32 start; - guint32 last; -}; - -struct _CamelIMAPXJob { - /* Whether to pop a status message off the - * GCancellable when the job is finalized. */ - gboolean pop_operation_msg; - - gboolean (*start) (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error); - gboolean (*matches) (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox, - const gchar *uid); +struct _CamelIMAPXJob; - guint noreply:1; /* dont wait for reply */ - guint32 type; /* operation type */ - gint pri; /* the command priority */ - volatile gint commands; /* counts how many commands are outstanding */ -}; +typedef enum { + CAMEL_IMAPX_JOB_UNKNOWN = 0, + CAMEL_IMAPX_JOB_CAPABILITY, + CAMEL_IMAPX_JOB_STARTTLS, + CAMEL_IMAPX_JOB_AUTHENTICATE, + CAMEL_IMAPX_JOB_LOGIN, + CAMEL_IMAPX_JOB_NAMESPACE, + CAMEL_IMAPX_JOB_SELECT, + CAMEL_IMAPX_JOB_STATUS, + CAMEL_IMAPX_JOB_ENABLE, + CAMEL_IMAPX_JOB_NOTIFY, + CAMEL_IMAPX_JOB_GET_MESSAGE, + CAMEL_IMAPX_JOB_SYNC_MESSAGE, + CAMEL_IMAPX_JOB_APPEND_MESSAGE, + CAMEL_IMAPX_JOB_COPY_MESSAGE, + CAMEL_IMAPX_JOB_MOVE_MESSAGE, + CAMEL_IMAPX_JOB_FETCH_NEW_MESSAGES, + CAMEL_IMAPX_JOB_REFRESH_INFO, + CAMEL_IMAPX_JOB_SYNC_CHANGES, + CAMEL_IMAPX_JOB_EXPUNGE, + CAMEL_IMAPX_JOB_NOOP, + CAMEL_IMAPX_JOB_IDLE, + CAMEL_IMAPX_JOB_DONE, + CAMEL_IMAPX_JOB_LIST, + CAMEL_IMAPX_JOB_LSUB, + CAMEL_IMAPX_JOB_CREATE_MAILBOX, + CAMEL_IMAPX_JOB_DELETE_MAILBOX, + CAMEL_IMAPX_JOB_RENAME_MAILBOX, + CAMEL_IMAPX_JOB_SUBSCRIBE_MAILBOX, + CAMEL_IMAPX_JOB_UNSUBSCRIBE_MAILBOX, + CAMEL_IMAPX_JOB_UPDATE_QUOTA_INFO, + CAMEL_IMAPX_JOB_UID_SEARCH, + CAMEL_IMAPX_JOB_LAST +} CamelIMAPXJobKind; + +typedef const gchar * (* CamelIMAPXJobGetKindNameFunc)(guint32 job_kind); + +const gchar * camel_imapx_job_get_kind_name (guint32 job_kind); +void camel_imapx_job_register_get_kind_name_func + (CamelIMAPXJobGetKindNameFunc get_kind_name); +void camel_imapx_job_unregister_get_kind_name_func + (CamelIMAPXJobGetKindNameFunc get_kind_name); + +typedef gboolean (* CamelIMAPXJobRunSyncFunc) (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, + GError **error); +typedef gboolean (* CamelIMAPXJobMatchesFunc) (CamelIMAPXJob *job, + CamelIMAPXJob *other_job); +typedef void (* CamelIMAPXJobCopyResultFunc) (CamelIMAPXJob *job, + gconstpointer set_result, + gpointer *out_result); -CamelIMAPXJob * camel_imapx_job_new (GCancellable *cancellable); +CamelIMAPXJob * camel_imapx_job_new (guint32 job_kind, + CamelIMAPXMailbox *mailbox, + CamelIMAPXJobRunSyncFunc run_sync, + CamelIMAPXJobMatchesFunc matches, + CamelIMAPXJobCopyResultFunc copy_result); CamelIMAPXJob * camel_imapx_job_ref (CamelIMAPXJob *job); void camel_imapx_job_unref (CamelIMAPXJob *job); -gboolean camel_imapx_job_check (CamelIMAPXJob *job); -void camel_imapx_job_cancel (CamelIMAPXJob *job); -gboolean camel_imapx_job_wait (CamelIMAPXJob *job, - GError **error); -void camel_imapx_job_done (CamelIMAPXJob *job); -gboolean camel_imapx_job_run (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GError **error); -void camel_imapx_job_guard_mailbox_update - (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox); -gboolean camel_imapx_job_matches (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox, - const gchar *uid); -gpointer camel_imapx_job_get_data (CamelIMAPXJob *job); -void camel_imapx_job_set_data (CamelIMAPXJob *job, - gpointer data, - GDestroyNotify destroy_data); -gboolean camel_imapx_job_has_mailbox (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox); +guint32 camel_imapx_job_get_kind (CamelIMAPXJob *job); CamelIMAPXMailbox * - camel_imapx_job_ref_mailbox (CamelIMAPXJob *job); -void camel_imapx_job_set_mailbox (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox); -GCancellable * camel_imapx_job_get_cancellable (CamelIMAPXJob *job); -void camel_imapx_job_take_error (CamelIMAPXJob *job, - GError *error); -gboolean camel_imapx_job_set_error_if_failed + camel_imapx_job_get_mailbox (CamelIMAPXJob *job); +gpointer camel_imapx_job_get_user_data (CamelIMAPXJob *job); +void camel_imapx_job_set_user_data (CamelIMAPXJob *job, + gpointer user_data, + GDestroyNotify destroy_user_data); +gboolean camel_imapx_job_was_cancelled (CamelIMAPXJob *job); +void camel_imapx_job_set_result (CamelIMAPXJob *job, + gboolean success, + gpointer result, + const GError *error, + GDestroyNotify destroy_result); +gboolean camel_imapx_job_copy_result (CamelIMAPXJob *job, + gboolean *out_success, + gpointer *out_result, + GError **out_error, + GDestroyNotify *out_destroy_result); +gboolean camel_imapx_job_take_result_data (CamelIMAPXJob *job, + gpointer *out_result); +gboolean camel_imapx_job_matches (CamelIMAPXJob *job, + CamelIMAPXJob *other_job); +gboolean camel_imapx_job_run_sync (CamelIMAPXJob *job, + CamelIMAPXServer *server, + GCancellable *cancellable, GError **error); +void camel_imapx_job_done (CamelIMAPXJob *job); +void camel_imapx_job_abort (CamelIMAPXJob *job); +void camel_imapx_job_wait_sync (CamelIMAPXJob *job, + GCancellable *cancellable); G_END_DECLS diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.c.imapx-update-to-upstream 2014-05-22 08:45:46.000000000 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.c 2016-08-15 13:52:41.952976330 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-list-response.c * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ @@ -147,7 +147,7 @@ imapx_list_response_parse_childinfo (Cam goto fail; if (tok != '(') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "list childinfo: expecting ')'"); goto fail; } @@ -197,7 +197,7 @@ imapx_list_response_parse_oldname (Camel goto fail; if (tok != '(') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "list oldname: expecting ')'"); goto fail; } @@ -214,7 +214,7 @@ imapx_list_response_parse_oldname (Camel goto fail; if (tok != ')') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "list oldname: expecting ')'"); goto fail; } @@ -325,7 +325,7 @@ camel_imapx_list_response_new (CamelIMAP goto fail; if (tok != '(') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "list: expecting '('"); goto fail; } @@ -343,7 +343,7 @@ camel_imapx_list_response_new (CamelIMAP goto fail; if (tok != ')') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "list: expecting ')'"); goto fail; } @@ -414,7 +414,7 @@ extended_item_repeat: } else { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "list: expecting '(' or NEWLINE"); goto fail; } diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.h.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-list-response.h 2016-08-15 13:52:41.953976330 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-list-response.h * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.c.imapx-update-to-upstream 2014-05-22 08:45:46.000000000 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.c 2016-08-15 13:52:41.953976330 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-logger.c * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.h.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-logger.h 2016-08-15 13:52:41.953976330 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-logger.h * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.c.imapx-update-to-upstream 2016-08-15 13:52:41.892976333 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.c 2016-08-15 14:38:22.156860220 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-mailbox.c * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ @@ -48,12 +48,13 @@ struct _CamelIMAPXMailboxPrivate { guint64 highestmodseq; guint32 permanentflags; + volatile gint change_stamp; + CamelIMAPXMailboxState state; GMutex property_lock; GMutex update_lock; - GCond update_cond; - gboolean update_is_locked; + gint update_count; /* Protected by the "property_lock". */ GHashTable *attributes; @@ -101,7 +102,6 @@ imapx_mailbox_finalize (GObject *object) g_mutex_clear (&priv->property_lock); g_mutex_clear (&priv->update_lock); - g_cond_clear (&priv->update_cond); g_hash_table_destroy (priv->attributes); g_sequence_free (priv->message_map); g_strfreev (priv->quota_roots); @@ -129,11 +129,11 @@ camel_imapx_mailbox_init (CamelIMAPXMail g_mutex_init (&mailbox->priv->property_lock); g_mutex_init (&mailbox->priv->update_lock); - g_cond_init (&mailbox->priv->update_cond); - mailbox->priv->update_is_locked = FALSE; mailbox->priv->message_map = g_sequence_new (NULL); mailbox->priv->permanentflags = ~0; mailbox->priv->state = CAMEL_IMAPX_MAILBOX_STATE_CREATED; + mailbox->priv->update_count = 0; + mailbox->priv->change_stamp = 0; } /** @@ -256,7 +256,7 @@ camel_imapx_mailbox_clone (CamelIMAPXMai * * Returns: Current (update) state of the mailbox. * - * Since: 3.12.9 + * Since: 3.16 **/ CamelIMAPXMailboxState camel_imapx_mailbox_get_state (CamelIMAPXMailbox *mailbox) @@ -275,7 +275,7 @@ camel_imapx_mailbox_get_state (CamelIMAP * structure updates, to identify newly created, updated, renamed * or removed mailboxes. * - * Since: 3.12.9 + * Since: 3.16 **/ void camel_imapx_mailbox_set_state (CamelIMAPXMailbox *mailbox, @@ -497,7 +497,12 @@ camel_imapx_mailbox_set_messages (CamelI { g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); + if (mailbox->priv->messages == messages) + return; + mailbox->priv->messages = messages; + + g_atomic_int_add (&mailbox->priv->change_stamp, 1); } /** @@ -539,7 +544,12 @@ camel_imapx_mailbox_set_recent (CamelIMA { g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); + if (mailbox->priv->recent == recent) + return; + mailbox->priv->recent = recent; + + g_atomic_int_add (&mailbox->priv->change_stamp, 1); } /** @@ -583,7 +593,12 @@ camel_imapx_mailbox_set_unseen (CamelIMA { g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); + if (mailbox->priv->unseen == unseen) + return; + mailbox->priv->unseen = unseen; + + g_atomic_int_add (&mailbox->priv->change_stamp, 1); } /** @@ -625,7 +640,12 @@ camel_imapx_mailbox_set_uidnext (CamelIM { g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); + if (mailbox->priv->uidnext == uidnext) + return; + mailbox->priv->uidnext = uidnext; + + g_atomic_int_add (&mailbox->priv->change_stamp, 1); } /** @@ -667,7 +687,12 @@ camel_imapx_mailbox_set_uidvalidity (Cam { g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); + if (mailbox->priv->uidvalidity == uidvalidity) + return; + mailbox->priv->uidvalidity = uidvalidity; + + g_atomic_int_add (&mailbox->priv->change_stamp, 1); } /** @@ -713,7 +738,12 @@ camel_imapx_mailbox_set_highestmodseq (C { g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); + if (mailbox->priv->highestmodseq == highestmodseq) + return; + mailbox->priv->highestmodseq = highestmodseq; + + g_atomic_int_add (&mailbox->priv->change_stamp, 1); } /** @@ -723,7 +753,7 @@ camel_imapx_mailbox_set_highestmodseq (C * Returns: PERMANENTFLAGS response for the mailbox, or ~0, if the mailbox * was not selected yet. * - * Since: 3.12.8 + * Since: 3.16 **/ guint32 camel_imapx_mailbox_get_permanentflags (CamelIMAPXMailbox *mailbox) @@ -740,7 +770,7 @@ camel_imapx_mailbox_get_permanentflags ( * * Updates the last know value for PERMANENTFLAGS for this mailbox. * - * Since: 3.12.8 + * Since: 3.16 **/ void camel_imapx_mailbox_set_permanentflags (CamelIMAPXMailbox *mailbox, @@ -748,6 +778,11 @@ camel_imapx_mailbox_set_permanentflags ( { g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); + if ((permanentflags & CAMEL_MESSAGE_USER) != 0) { + permanentflags |= CAMEL_MESSAGE_JUNK; + permanentflags |= CAMEL_MESSAGE_NOTJUNK; + } + mailbox->priv->permanentflags = permanentflags; } @@ -1179,53 +1214,49 @@ camel_imapx_mailbox_handle_status_respon g_return_if_fail (CAMEL_IS_IMAPX_STATUS_RESPONSE (response)); if (camel_imapx_status_response_get_messages (response, &value32)) - mailbox->priv->messages = value32; + camel_imapx_mailbox_set_messages (mailbox, value32); if (camel_imapx_status_response_get_recent (response, &value32)) - mailbox->priv->recent = value32; + camel_imapx_mailbox_set_recent (mailbox, value32); if (camel_imapx_status_response_get_unseen (response, &value32)) - mailbox->priv->unseen = value32; + camel_imapx_mailbox_set_unseen (mailbox, value32); if (camel_imapx_status_response_get_uidnext (response, &value32)) - mailbox->priv->uidnext = value32; + camel_imapx_mailbox_set_uidnext (mailbox, value32); if (camel_imapx_status_response_get_uidvalidity (response, &value32)) - mailbox->priv->uidvalidity = value32; + camel_imapx_mailbox_set_uidvalidity (mailbox, value32); if (camel_imapx_status_response_get_highestmodseq (response, &value64)) - mailbox->priv->highestmodseq = value64; + camel_imapx_mailbox_set_highestmodseq (mailbox, value64); } -/* Prevents running FETCH and STORE at the same time for the given mailbox */ -void -camel_imapx_mailbox_lock_update (CamelIMAPXMailbox *mailbox) +gint +camel_imapx_mailbox_get_update_count (CamelIMAPXMailbox *mailbox) { - g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); + gint res; g_mutex_lock (&mailbox->priv->update_lock); - - while (mailbox->priv->update_is_locked) { - g_cond_wait (&mailbox->priv->update_cond, &mailbox->priv->update_lock); - } - - mailbox->priv->update_is_locked = TRUE; - + res = mailbox->priv->update_count; g_mutex_unlock (&mailbox->priv->update_lock); + + return res; } -/* Prevents running FETCH and STORE at the same time for the given mailbox */ void -camel_imapx_mailbox_unlock_update (CamelIMAPXMailbox *mailbox) +camel_imapx_mailbox_inc_update_count (CamelIMAPXMailbox *mailbox, + gint inc) { - g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); - g_mutex_lock (&mailbox->priv->update_lock); + mailbox->priv->update_count += inc; + g_mutex_unlock (&mailbox->priv->update_lock); +} - if (mailbox->priv->update_is_locked) { - mailbox->priv->update_is_locked = FALSE; - g_cond_signal (&mailbox->priv->update_cond); - } +gint +camel_imapx_mailbox_get_change_stamp (CamelIMAPXMailbox *mailbox) +{ + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), 0); - g_mutex_unlock (&mailbox->priv->update_lock); + return mailbox->priv->change_stamp; } diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.h.imapx-update-to-upstream 2014-11-20 17:14:49.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-mailbox.h 2016-08-15 14:38:22.157860220 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-mailbox.h * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ @@ -175,9 +175,12 @@ void camel_imapx_mailbox_handle_status_ (CamelIMAPXMailbox *mailbox, CamelIMAPXStatusResponse *response); -void camel_imapx_mailbox_lock_update +gint camel_imapx_mailbox_get_update_count (CamelIMAPXMailbox *mailbox); -void camel_imapx_mailbox_unlock_update +void camel_imapx_mailbox_inc_update_count + (CamelIMAPXMailbox *mailbox, + gint inc); +gint camel_imapx_mailbox_get_change_stamp (CamelIMAPXMailbox *mailbox); G_END_DECLS diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.c.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.c 2016-08-15 13:52:41.957976330 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-namespace.c * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.h.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace.h 2016-08-15 13:52:41.957976330 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-namespace.h * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.c.imapx-update-to-upstream 2014-12-02 16:08:21.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.c 2016-08-15 13:52:41.958976330 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-namespace-response.c * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ @@ -117,7 +117,7 @@ imapx_namespace_response_parse_namespace } if (tok != '(') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "namespace: expecting NIL or '('"); return FALSE; } @@ -129,7 +129,7 @@ repeat: return FALSE; if (tok != '(') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "namespace: expecting '('"); return FALSE; } @@ -140,7 +140,7 @@ repeat: return FALSE; if (tok != IMAPX_TOK_STRING) { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "namespace: expecting string"); return FALSE; } @@ -169,7 +169,7 @@ repeat: return FALSE; if (tok != ')') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "namespace: expecting ')'"); return FALSE; } @@ -184,7 +184,7 @@ repeat: } if (tok != ')') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "namespace: expecting '(' or ')'"); return FALSE; } @@ -331,7 +331,7 @@ camel_imapx_namespace_response_list (Cam * Adds a @namespace into the list of namespaces. It adds its own * reference on the @namespace. * - * Since: 3.12.9 + * Since: 3.16 **/ void camel_imapx_namespace_response_add (CamelIMAPXNamespaceResponse *response, @@ -351,7 +351,7 @@ camel_imapx_namespace_response_add (Came * Removes @namespace from the list of namespaces in the @response. * If no such namespace exists then does nothing. * - * Since: 3.12.9 + * Since: 3.16 **/ void camel_imapx_namespace_response_remove (CamelIMAPXNamespaceResponse *response, diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.h.imapx-update-to-upstream 2014-12-02 16:07:55.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-namespace-response.h 2016-08-15 13:52:41.958976330 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-namespace-response.h * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-provider.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-provider.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-provider.c.imapx-update-to-upstream 2014-05-22 08:45:46.000000000 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-provider.c 2016-08-15 13:52:41.958976330 +0200 @@ -1,23 +1,23 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* camel-pop3-provider.c: pop3 provider registration code */ -/* - * Authors : - * Dan Winship - * Michael Zucchi +/* camel-pop3-provider.c: pop3 provider registration code * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * - * This library is free software; you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . + * + * Authors : + * Dan Winship + * Michael Zucchi */ #ifdef HAVE_CONFIG_H @@ -69,16 +69,16 @@ CamelProviderConfEntry imapx_conf_entrie { CAMEL_PROVIDER_CONF_CHECKBOX, "filter-junk", NULL, N_("Check new messages for _Junk contents"), "0" }, { CAMEL_PROVIDER_CONF_CHECKBOX, "filter-junk-inbox", "filter-junk", - N_("Only check for Junk messages in the IN_BOX folder"), "0" }, + N_("Only check for Junk messages in the In_box folder"), "0" }, { CAMEL_PROVIDER_CONF_CHECKBOX, "stay-synchronized", NULL, - N_("Automatically synchroni_ze remote mail locally"), "0" }, + N_("Synchroni_ze remote mail locally in all folders"), "0" }, { CAMEL_PROVIDER_CONF_SECTION_END }, { CAMEL_PROVIDER_CONF_END } }; CamelProviderPortEntry imapx_port_entries[] = { { 143, N_("Default IMAP port"), FALSE }, - { 993, N_("IMAP over SSL"), TRUE }, + { 993, N_("IMAP over TLS"), TRUE }, { 0, NULL, 0 } }; diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.c.imapx-update-to-upstream 2014-06-06 16:08:31.000000000 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.c 2016-08-15 13:52:41.958976330 +0200 @@ -1,22 +1,27 @@ /* * camel-imapx-search.c * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ +#ifdef HAVE_CONFIG_H +#include +#endif + #include "camel-imapx-search.h" +#include #include #include @@ -154,7 +159,9 @@ static CamelSExpResult * imapx_search_process_criteria (CamelSExp *sexp, CamelFolderSearch *search, CamelIMAPXStore *imapx_store, - const GString *criteria, + const GString *criteria_prefix, + const gchar *search_key, + const GPtrArray *words, const gchar *from_function) { CamelSExpResult *result; @@ -173,33 +180,17 @@ imapx_search_process_criteria (CamelSExp if (mailbox != NULL) { CamelIMAPXStore *imapx_store; - CamelIMAPXServer *imapx_server; - const gchar *folder_name; + CamelIMAPXConnManager *conn_man; imapx_store = camel_imapx_search_ref_store (imapx_search); /* there should always be one, held by one of the callers of this function */ g_warn_if_fail (imapx_store != NULL); - folder_name = camel_folder_get_full_name (search->folder); - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, TRUE, imapx_search->priv->cancellable, &local_error); - if (imapx_server) { - uids = camel_imapx_server_uid_search (imapx_server, mailbox, criteria->str, imapx_search->priv->cancellable, &local_error); - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - - while (!uids && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, folder_name, TRUE, imapx_search->priv->cancellable, &local_error); - if (imapx_server) { - uids = camel_imapx_server_uid_search (imapx_server, mailbox, criteria->str, imapx_search->priv->cancellable, &local_error); - camel_imapx_store_folder_op_done (imapx_store, imapx_server, folder_name); - } - } - } + conn_man = camel_imapx_store_get_conn_manager (imapx_store); + uids = camel_imapx_conn_manager_uid_search_sync (conn_man, mailbox, criteria_prefix->str, search_key, + words ? (const gchar * const *) words->pdata : NULL, imapx_search->priv->cancellable, &local_error); - g_clear_object (&imapx_server); g_clear_object (&imapx_store); g_object_unref (mailbox); } @@ -296,6 +287,58 @@ imapx_search_match_all (CamelSExp *sexp, return result; } +static GPtrArray * +imapx_search_gather_words (CamelSExpResult **argv, + gint from_index, + gint argc) +{ + GPtrArray *ptrs; + GHashTable *words_hash; + GHashTableIter iter; + gpointer key, value; + gint ii, jj; + + g_return_val_if_fail (argv != 0, NULL); + + words_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + for (ii = from_index; ii < argc; ii++) { + struct _camel_search_words *words; + + if (argv[ii]->type != CAMEL_SEXP_RES_STRING) + continue; + + /* Handle multiple search words within a single term. */ + words = camel_search_words_split ((const guchar *) argv[ii]->value.string); + + for (jj = 0; jj < words->len; jj++) { + const gchar *word = words->words[jj]->word; + + g_hash_table_insert (words_hash, g_strdup (word), NULL); + } + + camel_search_words_free (words); + } + + ptrs = g_ptr_array_new_full (g_hash_table_size (words_hash), g_free); + + g_hash_table_iter_init (&iter, words_hash); + while (g_hash_table_iter_next (&iter, &key, &value)) { + g_ptr_array_add (ptrs, g_strdup (key)); + } + + if (ptrs->len == 0) { + g_ptr_array_free (ptrs, TRUE); + ptrs = NULL; + } else { + g_ptr_array_add (ptrs, NULL); + } + + g_hash_table_destroy (words_hash); + + return ptrs; +} + static CamelSExpResult * imapx_search_body_contains (CamelSExp *sexp, gint argc, @@ -306,7 +349,7 @@ imapx_search_body_contains (CamelSExp *s CamelIMAPXStore *imapx_store; CamelSExpResult *result; GString *criteria; - gint ii, jj; + GPtrArray *words; /* Always do body-search server-side */ if (imapx_search->priv->local_data_search) { @@ -343,39 +386,12 @@ imapx_search_body_contains (CamelSExp *s g_string_append_printf (criteria, "UID %s", uid); } - for (ii = 0; ii < argc; ii++) { - struct _camel_search_words *words; - const guchar *term; - - if (argv[ii]->type != CAMEL_SEXP_RES_STRING) - continue; - - /* Handle multiple search words within a single term. */ - term = (const guchar *) argv[ii]->value.string; - words = camel_search_words_split (term); - - for (jj = 0; jj < words->len; jj++) { - gchar *cp; - - if (criteria->len > 0) - g_string_append_c (criteria, ' '); - - g_string_append (criteria, "BODY \""); - - cp = words->words[jj]->word; - for (; *cp != '\0'; cp++) { - if (*cp == '\\' || *cp == '"') - g_string_append_c (criteria, '\\'); - g_string_append_c (criteria, *cp); - } - - g_string_append_c (criteria, '"'); - } - } + words = imapx_search_gather_words (argv, 0, argc); - result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, G_STRFUNC); + result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, "BODY", words, G_STRFUNC); g_string_free (criteria, TRUE); + g_ptr_array_free (words, TRUE); g_object_unref (imapx_store); return result; @@ -401,7 +417,8 @@ imapx_search_header_contains (CamelSExp CamelSExpResult *result; const gchar *headername, *command = NULL; GString *criteria; - gint ii, jj; + gchar *search_key = NULL; + GPtrArray *words; /* Match nothing if empty argv or empty summary. */ if (argc <= 1 || @@ -458,45 +475,17 @@ imapx_search_header_contains (CamelSExp else if (g_ascii_strcasecmp (headername, "Subject") == 0) command = "SUBJECT"; - for (ii = 1; ii < argc; ii++) { - struct _camel_search_words *words; - const guchar *term; - - if (argv[ii]->type != CAMEL_SEXP_RES_STRING) - continue; - - /* Handle multiple search words within a single term. */ - term = (const guchar *) argv[ii]->value.string; - words = camel_search_words_split (term); - - for (jj = 0; jj < words->len; jj++) { - gchar *cp; - - if (criteria->len > 0) - g_string_append_c (criteria, ' '); + words = imapx_search_gather_words (argv, 1, argc); - if (command) - g_string_append (criteria, command); - else - g_string_append_printf (criteria, "HEADER \"%s\"", headername); - - g_string_append (criteria, " \""); - - cp = words->words[jj]->word; - for (; *cp != '\0'; cp++) { - if (*cp == '\\' || *cp == '"') - g_string_append_c (criteria, '\\'); - g_string_append_c (criteria, *cp); - } - - g_string_append_c (criteria, '"'); - } - } + if (!command) + search_key = g_strdup_printf ("HEADER \"%s\"", headername); - result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, G_STRFUNC); + result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, command ? command : search_key, words, G_STRFUNC); g_string_free (criteria, TRUE); + g_ptr_array_free (words, TRUE); g_object_unref (imapx_store); + g_free (search_key); return result; } @@ -578,7 +567,7 @@ imapx_search_header_exists (CamelSExp *s g_string_append_printf (criteria, "HEADER \"%s\" \"\"", headername); } - result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, G_STRFUNC); + result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, NULL, NULL, G_STRFUNC); g_string_free (criteria, TRUE); g_object_unref (imapx_store); @@ -629,7 +618,7 @@ camel_imapx_search_init (CamelIMAPXSearc /** * camel_imapx_search_new: - * imapx_store: a #CamelIMAPXStore to which the search belongs + * @imapx_store: a #CamelIMAPXStore to which the search belongs * * Returns a new #CamelIMAPXSearch instance. * @@ -716,7 +705,7 @@ camel_imapx_search_set_store (CamelIMAPX * for the whole run of the search and reset them both to NULL after * the search is finished. * - * Since: 3.14 + * Since: 3.16 **/ void camel_imapx_search_set_cancellable_and_error (CamelIMAPXSearch *search, diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.h.imapx-update-to-upstream 2014-05-22 08:45:46.000000000 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-search.h 2016-08-15 13:52:41.959976330 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-search.h * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.c.imapx-update-to-upstream 2016-08-15 13:52:41.893976333 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.c 2016-08-15 14:38:22.158860220 +0200 @@ -2,30 +2,24 @@ /* * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * - * This library is free software; you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . + * */ #ifdef HAVE_CONFIG_H #include #endif -/* XXX Disable deprecation warnings until we require GLib 2.40. - * - * This silences warnings for using GSubprocess functions, which are - * only available as of GLib 2.39. But we do so conditionally, with - * GLIB_CHECK_VERSION macros. */ -#define GLIB_DISABLE_DEPRECATION_WARNINGS - #include #include #include @@ -35,10 +29,14 @@ #include #include +#include + #ifndef G_OS_WIN32 #include #endif /* G_OS_WIN32 */ +#include + #include "camel-imapx-server.h" #include "camel-imapx-folder.h" @@ -57,21 +55,22 @@ #define c(...) camel_imapx_debug(command, __VA_ARGS__) #define e(...) camel_imapx_debug(extra, __VA_ARGS__) -#define QUEUE_LOCK(x) g_rec_mutex_lock (&(x)->queue_lock) -#define QUEUE_UNLOCK(x) g_rec_mutex_unlock (&(x)->queue_lock) +#define COMMAND_LOCK(x) g_rec_mutex_lock (&(x)->priv->command_lock) +#define COMMAND_UNLOCK(x) g_rec_mutex_unlock (&(x)->priv->command_lock) /* Try pipelining fetch requests, 'in bits' */ #define MULTI_SIZE (32768 * 8) -/* How many outstanding commands do we allow before we just queue them? */ -#define MAX_COMMANDS (10) - #define MAX_COMMAND_LEN 1000 /* Ping the server after a period of inactivity to avoid being logged off. * Using a 29 minute inactivity timeout as recommended in RFC 2177 (IDLE). */ #define INACTIVITY_TIMEOUT_SECONDS (29 * 60) +/* Number of seconds to remain in PENDING state waiting for other commands + to be queued, before actually sending IDLE */ +#define IMAPX_IDLE_WAIT_SECONDS 2 + #ifdef G_OS_WIN32 #ifdef gmtime_r #undef gmtime_r @@ -83,92 +82,6 @@ G_DEFINE_QUARK (camel-imapx-server-error-quark, camel_imapx_server_error) -extern gint camel_application_is_exiting; - -/* Job-specific structs */ -typedef struct _GetMessageData GetMessageData; -typedef struct _RefreshInfoData RefreshInfoData; -typedef struct _SyncChangesData SyncChangesData; -typedef struct _AppendMessageData AppendMessageData; -typedef struct _CopyMessagesData CopyMessagesData; -typedef struct _ListData ListData; -typedef struct _MailboxData MailboxData; -typedef struct _SearchData SearchData; - -struct _GetMessageData { - /* in: uid requested */ - gchar *uid; - CamelDataCache *message_cache; - /* in/out: message content stream output */ - GIOStream *stream; - /* working variables */ - gsize body_offset; - gsize fetch_offset; - gsize size; - gboolean use_multi_fetch; -}; - -struct _RefreshInfoData { - /* array of refresh info's */ - GArray *infos; - /* used for building uidset stuff */ - gint index; - gint last_index; - CamelFetchType fetch_type; - gboolean update_unseen; - gboolean scan_changes; - struct _uidset_state uidset; - /* changes during refresh */ - CamelFolderChangeInfo *changes; -}; - -struct _SyncChangesData { - CamelFolder *folder; - GPtrArray *changed_uids; - gboolean own_allocated_changed_uids; - guint32 on_set; - guint32 off_set; - GArray *on_user; /* imapx_flag_change */ - GArray *off_user; - gint unread_change; - - /* Remove recently set DELETED flags before synchronizing. - * This is only set when using a real Trash folder and NOT - * about to expunge the folder. */ - gboolean remove_deleted_flags; -}; - -struct _AppendMessageData { - gchar *path; - CamelMessageInfo *info; - gchar *appended_uid; - time_t date_time; /* message's date/time, in UTC */ -}; - -struct _CopyMessagesData { - CamelIMAPXMailbox *destination; - GPtrArray *uids; - gboolean delete_originals; - gboolean use_move_command; - gint index; - gint last_index; - struct _uidset_state uidset; -}; - -struct _ListData { - gchar *pattern; -}; - -struct _MailboxData { - CamelIMAPXMailbox *mailbox; - gchar *mailbox_name; -}; - -struct _SearchData { - gchar *criteria; - GArray *results; -}; - /* untagged response handling */ /* May need to turn this into separate, @@ -314,29 +227,16 @@ static const CamelIMAPXUntaggedRespHandl }; typedef enum { - IMAPX_IDLE_OFF, - IMAPX_IDLE_PENDING, /* Queue is idle; waiting to send IDLE command - soon if nothing more interesting happens */ - IMAPX_IDLE_ISSUED, /* Sent IDLE command; waiting for response */ - IMAPX_IDLE_STARTED, /* IDLE continuation received; IDLE active */ - IMAPX_IDLE_CANCEL, /* Cancelled from ISSUED state; need to send - DONE as soon as we receive continuation */ - IMAPX_IDLE_WAIT_DONE /* DONE was issued, waiting for a confirmation response */ -} CamelIMAPXIdleState; - -#define IMAPX_IDLE_DWELL_TIME 2 /* Number of seconds to remain in PENDING - state waiting for other commands to be - queued, before actually sending IDLE */ - -typedef enum { - IMAPX_IDLE_STOP_NOOP, - IMAPX_IDLE_STOP_WAIT_DONE, - IMAPX_IDLE_STOP_SUCCESS, - IMAPX_IDLE_STOP_ERROR -} CamelIMAPXIdleStopResult; + IMAPX_IDLE_STATE_OFF, /* no IDLE running at all */ + IMAPX_IDLE_STATE_SCHEDULED, /* IDLE scheduled, but still waiting */ + IMAPX_IDLE_STATE_PREPARING, /* IDLE command going to be processed */ + IMAPX_IDLE_STATE_RUNNING, /* IDLE command had been processed, server responded */ + IMAPX_IDLE_STATE_STOPPING /* DONE had been issued, waiting for completion */ +} IMAPXIdleState; struct _CamelIMAPXServerPrivate { GWeakRef store; + GCancellable *cancellable; /* the main connection cancellable, it's cancelled on disconnect */ CamelIMAPXServerUntaggedContext *context; GHashTable *untagged_handlers; @@ -345,27 +245,19 @@ struct _CamelIMAPXServerPrivate { GInputStream *input_stream; GOutputStream *output_stream; GIOStream *connection; -#if GLIB_CHECK_VERSION(2,39,0) GSubprocess *subprocess; -#endif GMutex stream_lock; - GThread *parser_thread; - GMainLoop *parser_main_loop; - GMainContext *parser_main_context; - GWeakRef parser_cancellable; - - GMutex shutdown_error_lock; - GError *shutdown_error; - GSource *inactivity_timeout; GMutex inactivity_timeout_lock; /* Info on currently selected folder. */ GMutex select_lock; GWeakRef select_mailbox; - GWeakRef select_closing; GWeakRef select_pending; + gint last_selected_mailbox_change_stamp; + + GMutex changes_lock; CamelFolderChangeInfo *changes; /* Data items to request in STATUS commands: @@ -392,17 +284,38 @@ struct _CamelIMAPXServerPrivate { gchar inbox_separator; /* IDLE support */ - GRecMutex idle_lock; - GThread *idle_thread; - GMainLoop *idle_main_loop; - GMainContext *idle_main_context; + GMutex idle_lock; + GCond idle_cond; + IMAPXIdleState idle_state; GSource *idle_pending; - CamelIMAPXIdleState idle_state; + CamelIMAPXMailbox *idle_mailbox; + GCancellable *idle_cancellable; + guint idle_stamp; + + gboolean is_cyrus; + + /* Info about the current connection; guarded by priv->stream_lock */ + struct _capability_info *cinfo; - GMutex jobs_prop_lock; - GHashTable *jobs_prop_folder_paths; - gint jobs_prop_command_count; /* without IDLE command */ - gint jobs_prop_expensive_command_count; + GRecMutex command_lock; + + gchar tagprefix; + guint32 state; + + gboolean use_qresync; + + CamelIMAPXCommand *current_command; + CamelIMAPXCommand *continuation_command; + + /* operation data */ + GIOStream *get_message_stream; + + CamelIMAPXMailbox *fetch_changes_mailbox; /* not referenced */ + CamelFolder *fetch_changes_folder; /* not referenced */ + GHashTable *fetch_changes_infos; /* gchar *uid ~> FetchChangesInfo-s */ + gint64 fetch_changes_last_progress; /* when was called last progress */ + + struct _status_info *copyuid_status; }; enum { @@ -411,25 +324,12 @@ enum { }; enum { - MAILBOX_SELECT, - MAILBOX_CLOSED, - SHUTDOWN, + REFRESH_MAILBOX, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; -static void imapx_uidset_init (struct _uidset_state *ss, - gint total, - gint limit); -static gint imapx_uidset_done (struct _uidset_state *ss, - CamelIMAPXCommand *ic); -static gint imapx_uidset_add (struct _uidset_state *ss, - CamelIMAPXCommand *ic, - const gchar *uid); - -static gboolean imapx_command_idle_stop (CamelIMAPXServer *is, - GError **error); static gboolean imapx_continuation (CamelIMAPXServer *is, GInputStream *input_stream, GOutputStream *output_stream, @@ -437,11 +337,6 @@ static gboolean imapx_continuation (Cam GCancellable *cancellable, GError **error); static void imapx_disconnect (CamelIMAPXServer *is); -static gboolean imapx_is_command_queue_empty (CamelIMAPXServer *is); -static gint imapx_uid_cmp (gconstpointer ap, - gconstpointer bp, - gpointer data); -static void imapx_command_start_next (CamelIMAPXServer *is); /* states for the connection? */ enum { @@ -453,105 +348,64 @@ enum { IMAPX_SELECTED }; -struct _refresh_info { - gchar *uid; - gboolean exists; - guint32 server_flags; - CamelFlag *server_user_flags; -}; - -enum { - IMAPX_JOB_GET_MESSAGE = 1 << 0, - IMAPX_JOB_APPEND_MESSAGE = 1 << 1, - IMAPX_JOB_COPY_MESSAGE = 1 << 2, - IMAPX_JOB_FETCH_NEW_MESSAGES = 1 << 3, - IMAPX_JOB_REFRESH_INFO = 1 << 4, - IMAPX_JOB_SYNC_CHANGES = 1 << 5, - IMAPX_JOB_EXPUNGE = 1 << 6, - IMAPX_JOB_NOOP = 1 << 7, - IMAPX_JOB_IDLE = 1 << 8, - IMAPX_JOB_LIST = 1 << 9, - IMAPX_JOB_CREATE_MAILBOX = 1 << 10, - IMAPX_JOB_DELETE_MAILBOX = 1 << 11, - IMAPX_JOB_RENAME_MAILBOX = 1 << 12, - IMAPX_JOB_SUBSCRIBE_MAILBOX = 1 << 13, - IMAPX_JOB_UNSUBSCRIBE_MAILBOX = 1 << 14, - IMAPX_JOB_UPDATE_QUOTA_INFO = 1 << 15, - IMAPX_JOB_UID_SEARCH = 1 << 16 -}; - -/* Mailbox management operations have highest priority - * since we know for sure that they are user triggered. */ -enum { - IMAPX_PRIORITY_MAILBOX_MGMT = 200, - IMAPX_PRIORITY_SYNC_CHANGES = 150, - IMAPX_PRIORITY_EXPUNGE = 150, - IMAPX_PRIORITY_SEARCH = 150, - IMAPX_PRIORITY_GET_MESSAGE = 100, - IMAPX_PRIORITY_REFRESH_INFO = 0, - IMAPX_PRIORITY_NOOP = 0, - IMAPX_PRIORITY_NEW_MESSAGES = 0, - IMAPX_PRIORITY_APPEND_MESSAGE = -60, - IMAPX_PRIORITY_COPY_MESSAGE = -60, - IMAPX_PRIORITY_LIST = -80, - IMAPX_PRIORITY_IDLE = -100, - IMAPX_PRIORITY_SYNC_MESSAGE = -120, - IMAPX_PRIORITY_UPDATE_QUOTA_INFO = -80 -}; - struct _imapx_flag_change { GPtrArray *infos; gchar *name; }; -static CamelIMAPXJob * - imapx_match_active_job (CamelIMAPXServer *is, - guint32 type, - const gchar *uid); -static gboolean imapx_job_fetch_new_messages_start - (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error); static gint imapx_refresh_info_uid_cmp (gconstpointer ap, gconstpointer bp, gboolean ascending); static gint imapx_uids_array_cmp (gconstpointer ap, gconstpointer bp); -static gboolean imapx_server_sync_changes (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - guint32 job_type, - gint pri, - GCancellable *cancellable, - GError **error); static void imapx_sync_free_user (GArray *user_set); -static gboolean imapx_command_copy_messages_step_start - (CamelIMAPXServer *is, - CamelIMAPXJob *job, - gint index, - GError **error); -static gboolean imapx_job_noop_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error); +G_DEFINE_TYPE (CamelIMAPXServer, camel_imapx_server, G_TYPE_OBJECT) -static gboolean imapx_in_idle (CamelIMAPXServer *is); -static gboolean imapx_use_idle (CamelIMAPXServer *is); -static void imapx_start_idle (CamelIMAPXServer *is); -static CamelIMAPXIdleStopResult - imapx_stop_idle (CamelIMAPXServer *is, - GError **error); -static gboolean camel_imapx_server_idle (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - GCancellable *cancellable, - GError **error); +/** + * camel_binding_bind_property: + * + * Thread safe variant of g_object_bind_property(). See its documentation + * for more information on arguments and return value. + * + * Returns: (transfer none): + * + * Since: 3.16 + **/ +static GBinding * +camel_binding_bind_property (gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags) +{ + static GRecMutex camel_binding_lock; + GBinding *binding; -static void imapx_maybe_select (CamelIMAPXServer *is, - CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox); + g_rec_mutex_lock (&camel_binding_lock); -G_DEFINE_TYPE (CamelIMAPXServer, camel_imapx_server, G_TYPE_OBJECT) + binding = g_object_bind_property (source, source_property, target, target_property, flags); + + g_rec_mutex_unlock (&camel_binding_lock); + + return binding; +} + +typedef struct _FetchChangesInfo { + guint32 server_flags; + CamelFlag *server_user_flags; +} FetchChangesInfo; + +static void +fetch_changes_info_free (gpointer ptr) +{ + FetchChangesInfo *nfo = ptr; + + if (nfo) { + camel_flag_list_free (&nfo->server_user_flags); + g_free (nfo); + } +} static GWeakRef * imapx_weak_ref_new (gpointer object) @@ -579,169 +433,6 @@ imapx_weak_ref_free (GWeakRef *weak_ref) g_slice_free (GWeakRef, weak_ref); } -static void -imapx_server_set_shutdown_error (CamelIMAPXServer *imapx_server, - const GError *error) -{ - g_mutex_lock (&imapx_server->priv->shutdown_error_lock); - - if (error != imapx_server->priv->shutdown_error) { - g_clear_error (&imapx_server->priv->shutdown_error); - if (error) - imapx_server->priv->shutdown_error = g_error_copy (error); - } - - g_mutex_unlock (&imapx_server->priv->shutdown_error_lock); -} - -static GError * -imapx_server_dup_shutdown_error (CamelIMAPXServer *imapx_server) -{ - GError *error = NULL; - - g_mutex_lock (&imapx_server->priv->shutdown_error_lock); - - if (imapx_server->priv->shutdown_error) - error = g_error_copy (imapx_server->priv->shutdown_error); - - g_mutex_unlock (&imapx_server->priv->shutdown_error_lock); - - return error; -} - -static void -imapx_server_command_added (CamelIMAPXServer *imapx_server, - CamelIMAPXCommand *command) -{ - CamelIMAPXJob *job; - - g_return_if_fail (command != NULL); - - g_mutex_lock (&imapx_server->priv->jobs_prop_lock); - - job = camel_imapx_command_get_job (command); - - if (job) { - /* without IDLE commands */ - if (!(job->type & IMAPX_JOB_IDLE)) - imapx_server->priv->jobs_prop_command_count++; - - if ((job->type & (IMAPX_JOB_FETCH_NEW_MESSAGES | IMAPX_JOB_REFRESH_INFO)) != 0) - imapx_server->priv->jobs_prop_expensive_command_count++; - } - - g_mutex_unlock (&imapx_server->priv->jobs_prop_lock); -} - -static void -imapx_server_command_removed (CamelIMAPXServer *imapx_server, - CamelIMAPXCommand *command) -{ - CamelIMAPXJob *job; - - g_return_if_fail (command != NULL); - - g_mutex_lock (&imapx_server->priv->jobs_prop_lock); - - job = camel_imapx_command_get_job (command); - - if (job) { - /* without IDLE commands */ - if (!(job->type & IMAPX_JOB_IDLE)) { - imapx_server->priv->jobs_prop_command_count--; - g_warn_if_fail (imapx_server->priv->jobs_prop_command_count >= 0); - } - - if ((job->type & (IMAPX_JOB_FETCH_NEW_MESSAGES | IMAPX_JOB_REFRESH_INFO)) != 0) { - imapx_server->priv->jobs_prop_expensive_command_count--; - g_warn_if_fail (imapx_server->priv->jobs_prop_expensive_command_count >= 0); - } - } - - g_mutex_unlock (&imapx_server->priv->jobs_prop_lock); -} - -static void -imapx_server_add_job_mailbox (CamelIMAPXServer *imapx_server, - CamelIMAPXMailbox *mailbox) -{ - gchar *folder_path; - gint n_stored; - - g_return_if_fail (mailbox != NULL); - - g_mutex_lock (&imapx_server->priv->jobs_prop_lock); - - folder_path = camel_imapx_mailbox_dup_folder_path (mailbox); - - n_stored = GPOINTER_TO_INT (g_hash_table_lookup (imapx_server->priv->jobs_prop_folder_paths, folder_path)); - /* takes ownership of folder_path */ - g_hash_table_insert (imapx_server->priv->jobs_prop_folder_paths, folder_path, GINT_TO_POINTER (n_stored + 1)); - - g_mutex_unlock (&imapx_server->priv->jobs_prop_lock); -} - -static void -imapx_server_remove_job_mailbox (CamelIMAPXServer *imapx_server, - CamelIMAPXMailbox *mailbox) -{ - gchar *folder_path; - gint n_stored; - - g_return_if_fail (mailbox != NULL); - - g_mutex_lock (&imapx_server->priv->jobs_prop_lock); - - folder_path = camel_imapx_mailbox_dup_folder_path (mailbox); - - n_stored = GPOINTER_TO_INT (g_hash_table_lookup (imapx_server->priv->jobs_prop_folder_paths, folder_path)); - if (!camel_imapx_mailbox_is_inbox (camel_imapx_mailbox_get_name (mailbox))) - g_warn_if_fail (n_stored >= 1); - - n_stored--; - if (n_stored > 0) { - /* takes ownership of folder_path */ - g_hash_table_insert (imapx_server->priv->jobs_prop_folder_paths, folder_path, GINT_TO_POINTER (n_stored)); - } else { - g_hash_table_remove (imapx_server->priv->jobs_prop_folder_paths, folder_path); - g_free (folder_path); - } - - g_mutex_unlock (&imapx_server->priv->jobs_prop_lock); -} - -static void -imapx_server_job_added (CamelIMAPXServer *imapx_server, - CamelIMAPXJob *job) -{ - CamelIMAPXMailbox *mailbox; - - g_return_if_fail (job != NULL); - - mailbox = camel_imapx_job_ref_mailbox (job); - - if (mailbox != NULL) { - imapx_server_add_job_mailbox (imapx_server, mailbox); - g_object_unref (mailbox); - } -} - -static void -imapx_server_job_removed (CamelIMAPXServer *imapx_server, - CamelIMAPXJob *job) -{ - CamelIMAPXMailbox *mailbox; - - g_return_if_fail (job != NULL); - - mailbox = camel_imapx_job_ref_mailbox (job); - - if (mailbox != NULL) { - imapx_server_remove_job_mailbox (imapx_server, mailbox); - g_object_unref (mailbox); - } -} - static const CamelIMAPXUntaggedRespHandlerDesc * replace_untagged_descriptor (GHashTable *untagged_handlers, const gchar *key, @@ -799,125 +490,19 @@ create_initial_untagged_handler_table (v return uh; } -static void -get_message_data_free (GetMessageData *data) -{ - g_free (data->uid); - - g_clear_object (&data->message_cache); - g_clear_object (&data->stream); - - g_slice_free (GetMessageData, data); -} - -static void -refresh_info_data_infos_free (RefreshInfoData *data) -{ - gint ii; - - if (!data || !data->infos) - return; - - for (ii = 0; ii < data->infos->len; ii++) { - struct _refresh_info *r = &g_array_index (data->infos, struct _refresh_info, ii); - - camel_flag_list_free (&r->server_user_flags); - g_free (r->uid); - } - - g_array_free (data->infos, TRUE); - data->infos = NULL; -} - -static void -refresh_info_data_free (RefreshInfoData *data) -{ - if (data->changes != NULL) - camel_folder_change_info_free (data->changes); - - refresh_info_data_infos_free (data); - - g_slice_free (RefreshInfoData, data); -} - -static void -sync_changes_data_free (SyncChangesData *data) -{ - if (data->folder != NULL) { - if (!data->own_allocated_changed_uids) - camel_folder_free_uids (data->folder, data->changed_uids); - g_object_unref (data->folder); - } - - if (data->own_allocated_changed_uids && data->changed_uids) { - g_ptr_array_foreach (data->changed_uids, (GFunc) camel_pstring_free, NULL); - g_ptr_array_free (data->changed_uids, TRUE); - } - - imapx_sync_free_user (data->on_user); - imapx_sync_free_user (data->off_user); - - g_slice_free (SyncChangesData, data); -} - -static void -append_message_data_free (AppendMessageData *data) -{ - g_free (data->path); - g_free (data->appended_uid); - - camel_message_info_unref (data->info); - - g_slice_free (AppendMessageData, data); -} - -static void -copy_messages_data_free (CopyMessagesData *data) -{ - g_clear_object (&data->destination); - - if (data->uids != NULL) { - g_ptr_array_foreach (data->uids, (GFunc) g_free, NULL); - g_ptr_array_free (data->uids, TRUE); - } - - g_slice_free (CopyMessagesData, data); -} - -static void -list_data_free (ListData *data) -{ - g_free (data->pattern); - - g_slice_free (ListData, data); -} - -static void -mailbox_data_free (MailboxData *data) -{ - g_clear_object (&data->mailbox); - g_free (data->mailbox_name); - - g_slice_free (MailboxData, data); -} - -static void -search_data_free (SearchData *data) -{ - g_free (data->criteria); - - if (data->results != NULL) - g_array_unref (data->results); - - g_slice_free (SearchData, data); -} +struct _uidset_state { + gint entries, uids; + gint total, limit; + guint32 start; + guint32 last; +}; /* this creates a uid (or sequence number) set directly into a command, if total is set, then we break it up into total uids. (i.e. command time) if limit is set, then we break it up into limit entries (i.e. command length) */ -void +static void imapx_uidset_init (struct _uidset_state *ss, gint total, gint limit) @@ -930,14 +515,19 @@ imapx_uidset_init (struct _uidset_state ss->limit = limit; } -gboolean +static gboolean imapx_uidset_done (struct _uidset_state *ss, CamelIMAPXCommand *ic) { gint ret = FALSE; - if (ss->last != 0 && ss->last != ss->start) { - camel_imapx_command_add (ic, ":%d", ss->last); + if (ss->last != 0) { + if (ss->entries > 0) + camel_imapx_command_add (ic, ","); + if (ss->last == ss->start) + camel_imapx_command_add (ic, "%d", ss->last); + else + camel_imapx_command_add (ic, "%d:%d", ss->start, ss->last); } ret = ss->last != 0; @@ -950,7 +540,7 @@ imapx_uidset_done (struct _uidset_state return ret; } -gint +static gint imapx_uidset_add (struct _uidset_state *ss, CamelIMAPXCommand *ic, const gchar *uid) @@ -963,33 +553,41 @@ imapx_uidset_add (struct _uidset_state * ss->uids++; - e (ic->is->tagprefix, "uidset add '%s'\n", uid); + e (ic->is->priv->tagprefix, "uidset add '%s'\n", uid); if (ss->last == 0) { - e (ic->is->tagprefix, " start\n"); - camel_imapx_command_add (ic, "%d", uidn); - ss->entries++; + e (ic->is->priv->tagprefix, " start\n"); ss->start = uidn; + ss->last = uidn; } else { - if (ss->last != uidn - 1) { - if (ss->last == ss->start) { - e (ic->is->tagprefix, " ,next\n"); - camel_imapx_command_add (ic, ",%d", uidn); - ss->entries++; - } else { - e (ic->is->tagprefix, " :range\n"); - camel_imapx_command_add (ic, ":%d,%d", ss->last, uidn); - ss->entries+=2; - } + if (ss->start - 1 == uidn) { ss->start = uidn; + } else { + if (ss->last != uidn - 1) { + if (ss->last == ss->start) { + e (ic->is->priv->tagprefix, " ,next\n"); + if (ss->entries > 0) + camel_imapx_command_add (ic, ","); + camel_imapx_command_add (ic, "%d", ss->start); + ss->entries++; + } else { + e (ic->is->priv->tagprefix, " :range\n"); + if (ss->entries > 0) + camel_imapx_command_add (ic, ","); + camel_imapx_command_add (ic, "%d:%d", ss->start, ss->last); + ss->entries += 2; + } + ss->start = uidn; + } + + ss->last = uidn; } } - ss->last = uidn; - if ((ss->limit && ss->entries >= ss->limit) + || (ss->limit && ss->uids >= ss->limit) || (ss->total && ss->uids >= ss->total)) { - e (ic->is->tagprefix, " done, %d entries, %d uids\n", ss->entries, ss->uids); + e (ic->is->priv->tagprefix, " done, %d entries, %d uids\n", ss->entries, ss->uids); if (!imapx_uidset_done (ss, ic)) return -1; return 1; @@ -998,74 +596,9 @@ imapx_uidset_add (struct _uidset_state * return 0; } -static gboolean -imapx_register_job (CamelIMAPXServer *is, - CamelIMAPXJob *job, - GError **error) -{ - if (is->state >= IMAPX_INITIALISED) { - QUEUE_LOCK (is); - g_queue_push_head (&is->jobs, camel_imapx_job_ref (job)); - imapx_server_job_added (is, job); - QUEUE_UNLOCK (is); - - } else if (is->state <= IMAPX_SHUTDOWN) { - e (is->tagprefix, "Server is shutdown/disconnected, try reconnect."); - g_set_error (error, - CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT, - _("Not authenticated")); - return FALSE; - } else { - e (is->tagprefix, "Not connected yet, maybe user cancelled jobs earlier?"); - g_set_error ( - error, CAMEL_SERVICE_ERROR, - CAMEL_SERVICE_ERROR_NOT_CONNECTED, - _("Not authenticated")); - return FALSE; - } - - return TRUE; -} - -static void -imapx_unregister_job (CamelIMAPXServer *is, - CamelIMAPXJob *job) -{ - camel_imapx_job_done (job); - - QUEUE_LOCK (is); - - if (g_queue_remove (&is->jobs, job)) { - imapx_server_job_removed (is, job); - camel_imapx_job_unref (job); - } - - imapx_command_start_next (is); - - QUEUE_UNLOCK (is); -} - -static gboolean -imapx_submit_job (CamelIMAPXServer *is, - CamelIMAPXJob *job, - GError **error) -{ - gboolean success; - - if (!imapx_register_job (is, job, error)) - return FALSE; - - success = camel_imapx_job_run (job, is, error); - - if (!success) - imapx_unregister_job (is, job); - - return success; -} - -static CamelFolder * -imapx_server_ref_folder (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox) +static CamelFolder * +imapx_server_ref_folder (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox) { CamelFolder *folder; CamelIMAPXStore *store; @@ -1106,19 +639,19 @@ imapx_server_stash_command_arguments (Ca /* Stash some reusable capability-based command arguments. */ buffer = g_string_new ("MESSAGES UNSEEN UIDVALIDITY UIDNEXT"); - if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, CONDSTORE)) + if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, CONDSTORE)) g_string_append (buffer, " HIGHESTMODSEQ"); g_free (is->priv->status_data_items); is->priv->status_data_items = g_string_free (buffer, FALSE); g_free (is->priv->list_return_opts); - if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, LIST_EXTENDED)) { + if (!is->priv->is_cyrus && CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, LIST_EXTENDED)) { buffer = g_string_new ("CHILDREN SUBSCRIBED"); - if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, LIST_STATUS)) + if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, LIST_STATUS)) g_string_append_printf ( buffer, " STATUS (%s)", is->priv->status_data_items); - if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, SPECIAL_USE)) + if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, SPECIAL_USE)) g_string_append_printf (buffer, " SPECIAL-USE"); is->priv->list_return_opts = g_string_free (buffer, FALSE); } else { @@ -1126,73 +659,57 @@ imapx_server_stash_command_arguments (Ca } } -static gboolean -imapx_server_inactivity_timeout_cb (gpointer data) +static gpointer +imapx_server_inactivity_thread (gpointer user_data) { - CamelIMAPXServer *is; - gboolean result = G_SOURCE_REMOVE; - - is = g_weak_ref_get (data); - - if (is == NULL) - return result; - - /* IDLE command may still be active, and any other active - * commands would have reset this timeout. So just check - * for any queued-but-not-yet-active commands. */ - - if (!camel_imapx_command_queue_is_empty (is->queue)) { - /* Do nothing. */ + CamelIMAPXServer *is = user_data; + GError *local_error = NULL; - } else if (imapx_in_idle (is)) { + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); + if (camel_imapx_server_is_in_idle (is)) { /* Stop and restart the IDLE command. */ - switch (imapx_stop_idle (is, NULL)) { - case IMAPX_IDLE_STOP_SUCCESS: - imapx_start_idle (is); - result = G_SOURCE_CONTINUE; - break; - - case IMAPX_IDLE_STOP_WAIT_DONE: - case IMAPX_IDLE_STOP_NOOP: - result = G_SOURCE_CONTINUE; - break; - - default: - break; - } - + if (!camel_imapx_server_schedule_idle_sync (is, NULL, is->priv->cancellable, &local_error) && + !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + camel_imapx_debug (io, camel_imapx_server_get_tagprefix (is), + "%s: Failed to restart IDLE: %s\n", G_STRFUNC, local_error ? local_error->message : "Unknown error"); } else { - CamelIMAPXJob *job; - GCancellable *cancellable; - GError *local_error = NULL; - - /* Submit a NOOP job but indicate we don't need a - * reply when finished. So this should NOT block. */ + if (!camel_imapx_server_noop_sync (is, NULL, is->priv->cancellable, &local_error) && + !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + camel_imapx_debug (io, camel_imapx_server_get_tagprefix (is), + "%s: Failed to issue NOOP: %s\n", G_STRFUNC, local_error ? local_error->message : "Unknown error"); + } - cancellable = g_weak_ref_get (&is->priv->parser_cancellable); + g_clear_error (&local_error); + g_object_unref (is); - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_NOOP; - job->start = imapx_job_noop_start; - job->pri = IMAPX_PRIORITY_NOOP; - job->noreply = TRUE; + return NULL; +} - imapx_submit_job (is, job, &local_error); +static gboolean +imapx_server_inactivity_timeout_cb (gpointer data) +{ + CamelIMAPXServer *is; + GThread *thread; + GError *local_error = NULL; - if (local_error != NULL) { - g_warning ("%s: %s", G_STRFUNC, local_error->message); - g_error_free (local_error); - } + is = g_weak_ref_get (data); - camel_imapx_job_unref (job); + if (is == NULL) + return G_SOURCE_REMOVE; - g_clear_object (&cancellable); + thread = g_thread_try_new (NULL, imapx_server_inactivity_thread, g_object_ref (is), &local_error); + if (!thread) { + g_warning ("%s: Failed to start inactivity thread: %s", G_STRFUNC, local_error ? local_error->message : "Unknown error"); + g_object_unref (is); + } else { + g_thread_unref (thread); } + g_clear_error (&local_error); g_object_unref (is); - return result; + return G_SOURCE_REMOVE; } static void @@ -1212,9 +729,7 @@ imapx_server_reset_inactivity_timer (Cam imapx_server_inactivity_timeout_cb, imapx_weak_ref_new (is), (GDestroyNotify) imapx_weak_ref_free); - g_source_attach ( - is->priv->inactivity_timeout, - is->priv->parser_main_context); + g_source_attach (is->priv->inactivity_timeout, NULL); g_mutex_unlock (&is->priv->inactivity_timeout_lock); } @@ -1226,8 +741,21 @@ imapx_server_set_connection_timeout (GIO GSocket *socket; gint previous_timeout = -1; - if (!G_IS_SOCKET_CONNECTION (connection)) + if (G_IS_TLS_CONNECTION (connection)) { + GIOStream *base_io_stream = NULL; + + g_object_get (G_OBJECT (connection), "base-io-stream", &base_io_stream, NULL); + + connection = base_io_stream; + } else if (connection) { + /* Connection can be NULL, when a custom command (GSubProcess) is used instead */ + g_object_ref (connection); + } + + if (!G_IS_SOCKET_CONNECTION (connection)) { + g_clear_object (&connection); return previous_timeout; + } socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (connection)); if (socket) { @@ -1235,758 +763,170 @@ imapx_server_set_connection_timeout (GIO g_socket_set_timeout (socket, timeout_seconds); } + g_clear_object (&connection); + return previous_timeout; } -/* Must hold QUEUE_LOCK */ static void -imapx_command_start (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) +imapx_expunge_uid_from_summary (CamelIMAPXServer *is, + const gchar *uid, + gboolean unsolicited) { - CamelIMAPXCommandPart *cp; - CamelIMAPXJob *job; - GInputStream *input_stream = NULL; - GOutputStream *output_stream = NULL; - GCancellable *cancellable = NULL; - gboolean cp_continuation; - gboolean cp_literal_plus; - gboolean success; - GList *head; - gchar *string; - GError *local_error = NULL; - - camel_imapx_command_close (ic); - - head = g_queue_peek_head_link (&ic->parts); - g_return_if_fail (head != NULL); - cp = (CamelIMAPXCommandPart *) head->data; - ic->current_part = head; - - cp_continuation = ((cp->type & CAMEL_IMAPX_COMMAND_CONTINUATION) != 0); - cp_literal_plus = ((cp->type & CAMEL_IMAPX_COMMAND_LITERAL_PLUS) != 0); - - /* TODO: If we support literal+ we should be able to write the whole command out - * at this point .... >here< */ + CamelFolder *folder; + CamelIMAPXMailbox *mailbox; + guint32 messages; - if (cp_continuation || cp_literal_plus) - is->literal = ic; + mailbox = camel_imapx_server_ref_pending_or_selected (is); - camel_imapx_command_queue_push_tail (is->active, ic); - imapx_server_command_added (is, ic); + g_return_if_fail (mailbox != NULL); - job = camel_imapx_command_get_job (ic); - if (job && g_cancellable_set_error_if_cancelled (camel_imapx_job_get_cancellable (job), &local_error)) { - camel_imapx_job_take_error (job, local_error); - local_error = NULL; + folder = imapx_server_ref_folder (is, mailbox); + g_return_if_fail (folder != NULL); - camel_imapx_command_queue_remove (is->active, ic); - imapx_server_command_removed (is, ic); + messages = camel_imapx_mailbox_get_messages (mailbox); - if (ic->complete != NULL) - ic->complete (is, ic); + if (unsolicited && messages > 0) + camel_imapx_mailbox_set_messages (mailbox, messages - 1); - if (is->literal == ic) - is->literal = NULL; + g_return_if_fail (is->priv->changes != NULL); - goto exit; - } + camel_folder_summary_remove_uid (folder->summary, uid); + g_mutex_lock (&is->priv->changes_lock); - input_stream = camel_imapx_server_ref_input_stream (is); - output_stream = camel_imapx_server_ref_output_stream (is); - cancellable = g_weak_ref_get (&is->priv->parser_cancellable); + camel_folder_change_info_remove_uid (is->priv->changes, uid); - if (output_stream == NULL) { - local_error = g_error_new_literal ( - CAMEL_IMAPX_ERROR, 1, - "Cannot issue command, no stream available"); - goto fail; - } + if (camel_imapx_server_is_in_idle (is)) { + CamelFolderChangeInfo *changes; - c ( - is->tagprefix, - "Starting command (active=%d,%s) %c%05u %s\r\n", - camel_imapx_command_queue_get_length (is->active), - is->literal ? " literal" : "", - is->tagprefix, - ic->tag, - cp->data && g_str_has_prefix (cp->data, "LOGIN") ? - "LOGIN..." : cp->data); + changes = is->priv->changes; + is->priv->changes = camel_folder_change_info_new (); - string = g_strdup_printf ( - "%c%05u %s\r\n", is->tagprefix, ic->tag, cp->data); - g_mutex_lock (&is->priv->stream_lock); - success = g_output_stream_write_all ( - output_stream, string, strlen (string), - NULL, cancellable, &local_error); - g_mutex_unlock (&is->priv->stream_lock); - g_free (string); + g_mutex_unlock (&is->priv->changes_lock); - if (local_error != NULL || !success) - goto fail; + camel_folder_summary_save_to_db (folder->summary, NULL); + imapx_update_store_summary (folder); + camel_folder_changed (folder, changes); - while (is->literal == ic && cp_literal_plus) { - /* Sent LITERAL+ continuation immediately */ - imapx_continuation ( - is, input_stream, output_stream, - TRUE, cancellable, &local_error); - if (local_error != NULL) - goto fail; + camel_folder_change_info_free (changes); + } else { + g_mutex_unlock (&is->priv->changes_lock); } - imapx_server_reset_inactivity_timer (is); + g_object_unref (folder); + g_object_unref (mailbox); +} - goto exit; +/* untagged response handler functions */ -fail: - camel_imapx_command_queue_remove (is->active, ic); - imapx_server_command_removed (is, ic); - - /* Break the parser thread out of its loop so it disconnects. */ - g_main_loop_quit (is->priv->parser_main_loop); - g_cancellable_cancel (cancellable); +static gboolean +imapx_untagged_capability (CamelIMAPXServer *is, + GInputStream *input_stream, + GCancellable *cancellable, + GError **error) +{ + struct _capability_info *cinfo; - /* Hand the error off to the command that we failed to start. */ - camel_imapx_command_failed (ic, local_error); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - if (ic->complete != NULL) - ic->complete (is, ic); + g_mutex_lock (&is->priv->stream_lock); - g_clear_error (&local_error); + if (is->priv->cinfo != NULL) { + imapx_free_capability (is->priv->cinfo); + is->priv->cinfo = NULL; + } -exit: - g_clear_object (&input_stream); - g_clear_object (&output_stream); - g_clear_object (&cancellable); -} + g_mutex_unlock (&is->priv->stream_lock); -static gboolean -imapx_is_duplicate_fetch_or_refresh (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - guint32 job_types; + cinfo = imapx_parse_capability (CAMEL_IMAPX_INPUT_STREAM (input_stream), cancellable, error); - /* Job types to match. */ - job_types = - IMAPX_JOB_FETCH_NEW_MESSAGES | - IMAPX_JOB_REFRESH_INFO; + if (!cinfo) + return FALSE; - job = camel_imapx_command_get_job (ic); + g_mutex_lock (&is->priv->stream_lock); - if (job == NULL) - return FALSE; + if (is->priv->cinfo != NULL) + imapx_free_capability (is->priv->cinfo); + is->priv->cinfo = cinfo; - if ((job->type & job_types) == 0) - return FALSE; + c (is->priv->tagprefix, "got capability flags %08x\n", is->priv->cinfo->capa); - if (imapx_match_active_job (is, job_types, NULL) == NULL) - return FALSE; + imapx_server_stash_command_arguments (is); - c (is->tagprefix, "Not yet sending duplicate fetch/refresh %s command\n", ic->name); + g_mutex_unlock (&is->priv->stream_lock); return TRUE; } -/* See if we can start another task yet. - * - * If we're waiting for a literal, we cannot proceed. - * - * If we're about to change the folder we're - * looking at from user-direction, we dont proceed. - * - * If we have a folder selected, first see if any - * jobs are waiting on it, but only if they are - * at least as high priority as anything we - * have running. - * - * If we dont, select the first folder required, - * then queue all the outstanding jobs on it, that - * are at least as high priority as the first. - * - * must have QUEUE lock */ - -static void -imapx_command_start_next (CamelIMAPXServer *is) +static gboolean +imapx_untagged_expunge (CamelIMAPXServer *is, + GInputStream *input_stream, + GCancellable *cancellable, + GError **error) { - CamelIMAPXCommand *first_ic; CamelIMAPXMailbox *mailbox; - gint min_pri = -128; - - c (is->tagprefix, "** Starting next command\n"); - if (is->literal) { - c ( - is->tagprefix, - "* no; waiting for literal '%s'\n", - is->literal->name); - return; - } - - g_mutex_lock (&is->priv->select_lock); - mailbox = g_weak_ref_get (&is->priv->select_pending); - g_mutex_unlock (&is->priv->select_lock); - if (mailbox != NULL) { - CamelIMAPXCommand *start_ic = NULL; - GList *head, *link; - - c ( - is->tagprefix, - "-- Checking job queue for non-mailbox jobs\n"); - - head = camel_imapx_command_queue_peek_head_link (is->queue); + gulong expunge = 0; - /* Tag which commands in the queue to start. */ - for (link = head; link != NULL && !start_ic; link = g_list_next (link)) { - CamelIMAPXCommand *ic = link->data; - CamelIMAPXMailbox *ic_mailbox; - - if (ic->pri < min_pri) - break; - - c ( - is->tagprefix, - "-- %3d '%s'?\n", - (gint) ic->pri, ic->name); - - ic_mailbox = camel_imapx_command_ref_mailbox (ic); - - if (ic_mailbox == NULL) { - c ( - is->tagprefix, - "--> starting '%s'\n", - ic->name); - min_pri = ic->pri; - - /* Each command must be removed from 'is->queue' before - * starting it, so we temporarily reference the command - * to avoid accidentally finalizing it. */ - start_ic = camel_imapx_command_ref (ic); - } + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_clear_object (&ic_mailbox); - } + expunge = is->priv->context->id; - if (!start_ic) - c ( - is->tagprefix, - "* no, waiting for pending select '%s'\n", - camel_imapx_mailbox_get_name (mailbox)); - - /* Start the tagged command */ - if (start_ic) { - camel_imapx_command_queue_remove (is->queue, start_ic); - imapx_server_command_removed (is, start_ic); - imapx_command_start (is, start_ic); - camel_imapx_command_unref (start_ic); - } + COMMAND_LOCK (is); - g_clear_object (&mailbox); + /* Ignore EXPUNGE responses when not running a COPY(MOVE)_MESSAGE job */ + if (!is->priv->current_command || (is->priv->current_command->job_kind != CAMEL_IMAPX_JOB_COPY_MESSAGE && + is->priv->current_command->job_kind != CAMEL_IMAPX_JOB_MOVE_MESSAGE)) { + COMMAND_UNLOCK (is); - return; + c (is->priv->tagprefix, "ignoring untagged expunge: %lu\n", expunge); + return TRUE; } - if (is->state == IMAPX_SELECTED) { - gboolean stop_idle; - gboolean start_idle; - - stop_idle = - imapx_in_idle (is) && - !camel_imapx_command_queue_is_empty (is->queue); - - start_idle = - imapx_use_idle (is) && - !imapx_in_idle (is) && - imapx_is_command_queue_empty (is); - - if (stop_idle) { - switch (imapx_stop_idle (is, NULL)) { - /* Proceed with the next queued command. */ - case IMAPX_IDLE_STOP_NOOP: - break; - - case IMAPX_IDLE_STOP_WAIT_DONE: - case IMAPX_IDLE_STOP_SUCCESS: - c ( - is->tagprefix, - "waiting for idle to stop \n"); - /* if there are more pending commands, - * then they should be processed too */ - return; + COMMAND_UNLOCK (is); - case IMAPX_IDLE_STOP_ERROR: - return; - } - - } else if (start_idle) { - imapx_start_idle (is); - c (is->tagprefix, "starting idle \n"); - return; - } - } + c (is->priv->tagprefix, "expunged: %lu\n", expunge); - if (camel_imapx_command_queue_is_empty (is->queue)) { - c (is->tagprefix, "* no, no jobs\n"); - return; - } + mailbox = camel_imapx_server_ref_pending_or_selected (is); - /* See if any queued jobs on this select first */ - g_mutex_lock (&is->priv->select_lock); - mailbox = g_weak_ref_get (&is->priv->select_mailbox); - g_mutex_unlock (&is->priv->select_lock); if (mailbox != NULL) { - CamelIMAPXCommand *start_ic = NULL; - GList *head, *link; + CamelFolder *folder; + gchar *uid; - c ( - is->tagprefix, - "- we're selected on '%s', current jobs?\n", - camel_imapx_mailbox_get_name (mailbox)); + folder = imapx_server_ref_folder (is, mailbox); + g_return_val_if_fail (folder != NULL, FALSE); - head = camel_imapx_command_queue_peek_head_link (is->active); + uid = camel_imapx_dup_uid_from_summary_index (folder, expunge - 1); - /* Find the highest priority in the active queue. */ - for (link = head; link != NULL; link = g_list_next (link)) { - CamelIMAPXCommand *ic = link->data; + if (uid != NULL) + imapx_expunge_uid_from_summary (is, uid, TRUE); - min_pri = MAX (min_pri, ic->pri); - c ( - is->tagprefix, - "- %3d '%s'\n", - (gint) ic->pri, ic->name); - } + g_object_unref (folder); + g_free (uid); + } - if (camel_imapx_command_queue_get_length (is->active) >= MAX_COMMANDS) { - c ( - is->tagprefix, - "** too many jobs busy, " - "waiting for results for now\n"); - g_object_unref (mailbox); - return; - } + g_clear_object (&mailbox); - c (is->tagprefix, "-- Checking job queue\n"); + return TRUE; +} - head = camel_imapx_command_queue_peek_head_link (is->queue); +static gboolean +imapx_untagged_vanished (CamelIMAPXServer *is, + GInputStream *input_stream, + GCancellable *cancellable, + GError **error) +{ + CamelFolder *folder; + CamelIMAPXMailbox *mailbox; + GArray *uids; + GList *uid_list = NULL; + gboolean unsolicited = TRUE; + guint ii = 0; + guint len = 0; + guchar *token = NULL; + gint tok = 0; - /* Tag which commands in the queue to start. */ - for (link = head; link != NULL && !start_ic; link = g_list_next (link)) { - CamelIMAPXCommand *ic = link->data; - CamelIMAPXMailbox *ic_mailbox; - gboolean okay_to_start; - - if (is->literal != NULL) - break; - - if (ic->pri < min_pri) - break; - - c ( - is->tagprefix, - "-- %3d '%s'?\n", - (gint) ic->pri, ic->name); - - ic_mailbox = camel_imapx_command_ref_mailbox (ic); - - okay_to_start = - (ic_mailbox == NULL) || - (ic_mailbox == mailbox && - !imapx_is_duplicate_fetch_or_refresh (is, ic)); - - if (okay_to_start) { - c ( - is->tagprefix, - "--> starting '%s'\n", - ic->name); - min_pri = ic->pri; - /* Each command must be removed from 'is->queue' before - * starting it, so we temporarily reference the command - * to avoid accidentally finalizing it. */ - start_ic = camel_imapx_command_ref (ic); - } else { - /* This job isn't for the selected mailbox, - * but we don't want to consider jobs with - * lower priority than this, even if they - * are for the selected mailbox. */ - min_pri = ic->pri; - } - - g_clear_object (&ic_mailbox); - } - - g_clear_object (&mailbox); - - /* Start the tagged command */ - if (start_ic) { - camel_imapx_command_queue_remove (is->queue, start_ic); - imapx_server_command_removed (is, start_ic); - imapx_command_start (is, start_ic); - camel_imapx_command_unref (start_ic); - - return; - } - } - - /* This won't be NULL because we checked for an empty queue above. */ - first_ic = camel_imapx_command_queue_peek_head (is->queue); - - /* If we need to select a mailbox for the first command, do - * so now. It will re-call us if it completes successfully. */ - mailbox = camel_imapx_command_ref_mailbox (first_ic); - if (mailbox != NULL) { - CamelIMAPXJob *job; - - c ( - is->tagprefix, - "Selecting mailbox '%s' for command '%s'(%p)\n", - camel_imapx_mailbox_get_name (mailbox), - first_ic->name, first_ic); - - /* Associate the SELECT command with the CamelIMAPXJob - * that triggered it. Then if the SELECT command fails - * we have some destination to propagate the GError to. */ - job = camel_imapx_command_get_job (first_ic); - imapx_maybe_select (is, job, mailbox); - - g_clear_object (&mailbox); - - } else { - CamelIMAPXCommand *start_ic = NULL; - GList *head, *link; - - min_pri = first_ic->pri; - - g_mutex_lock (&is->priv->select_lock); - mailbox = g_weak_ref_get (&is->priv->select_mailbox); - g_mutex_unlock (&is->priv->select_lock); - - head = camel_imapx_command_queue_peek_head_link (is->queue); - - /* Tag which commands in the queue to start. */ - for (link = head; link != NULL && !start_ic; link = g_list_next (link)) { - CamelIMAPXCommand *ic = link->data; - CamelIMAPXMailbox *ic_mailbox; - gboolean okay_to_start; - - if (is->literal != NULL) - break; - - if (ic->pri < min_pri) - break; - - ic_mailbox = camel_imapx_command_ref_mailbox (ic); - - okay_to_start = - (ic_mailbox == NULL) || - (ic_mailbox == mailbox && - !imapx_is_duplicate_fetch_or_refresh (is, ic)); - - if (okay_to_start) { - c ( - is->tagprefix, - "* queueing job %3d '%s'\n", - (gint) ic->pri, ic->name); - min_pri = ic->pri; - /* Each command must be removed from 'is->queue' before - * starting it, so we temporarily reference the command - * to avoid accidentally finalizing it. */ - start_ic = camel_imapx_command_ref (ic); - } - - g_clear_object (&ic_mailbox); - } - - g_clear_object (&mailbox); - - /* Start the tagged command */ - if (start_ic) { - camel_imapx_command_queue_remove (is->queue, start_ic); - imapx_server_command_removed (is, start_ic); - imapx_command_start (is, start_ic); - camel_imapx_command_unref (start_ic); - } - } -} - -static gboolean -imapx_is_command_queue_empty (CamelIMAPXServer *is) -{ - if (!camel_imapx_command_queue_is_empty (is->queue)) - return FALSE; - - if (!camel_imapx_command_queue_is_empty (is->active)) - return FALSE; - - return TRUE; -} - -static void -imapx_command_queue (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - - /* We enqueue in priority order, new messages have - * higher priority than older messages with the same priority */ - - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); - - camel_imapx_command_close (ic); - - c ( - is->tagprefix, - "enqueue job '%.*s'\n", - ((CamelIMAPXCommandPart *) ic->parts.head->data)->data_size, - ((CamelIMAPXCommandPart *) ic->parts.head->data)->data); - - QUEUE_LOCK (is); - - if (is->state == IMAPX_SHUTDOWN) { - GError *local_error = NULL; - - c (is->tagprefix, "refuse to queue job on disconnected server\n"); - - local_error = g_error_new ( - CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT, - "%s", _("Server disconnected")); - camel_imapx_command_failed (ic, local_error); - g_error_free (local_error); - - QUEUE_UNLOCK (is); - - if (ic->complete != NULL) - ic->complete (is, ic); - - return; - } - - camel_imapx_command_queue_insert_sorted (is->queue, ic); - imapx_server_command_added (is, ic); - - imapx_command_start_next (is); - - QUEUE_UNLOCK (is); -} - -/* Must not have QUEUE lock */ -static CamelIMAPXJob * -imapx_match_active_job (CamelIMAPXServer *is, - guint32 type, - const gchar *uid) -{ - CamelIMAPXJob *match = NULL; - GList *head, *link; - - QUEUE_LOCK (is); - - head = camel_imapx_command_queue_peek_head_link (is->active); - - for (link = head; link != NULL; link = g_list_next (link)) { - CamelIMAPXCommand *ic = link->data; - CamelIMAPXMailbox *mailbox; - CamelIMAPXJob *job; - gboolean job_matches; - - job = camel_imapx_command_get_job (ic); - - if (job == NULL) - continue; - - if (!(job->type & type)) - continue; - - g_mutex_lock (&is->priv->select_lock); - mailbox = g_weak_ref_get (&is->priv->select_mailbox); - g_mutex_unlock (&is->priv->select_lock); - - job_matches = camel_imapx_job_matches (job, mailbox, uid); - g_clear_object (&mailbox); - - if (job_matches) { - match = job; - break; - } - } - - QUEUE_UNLOCK (is); - - return match; -} - -/* Do *not* call this when the queue_lock is held, it can cause - deadlock when searching between multiple servers */ -static CamelIMAPXJob * -imapx_server_ref_job (CamelIMAPXServer *imapx_server, - CamelIMAPXMailbox *mailbox, - guint32 job_type, - const gchar *uid) -{ - CamelIMAPXStore *imapx_store; - CamelIMAPXJob *job; - - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), NULL); - - /* first try its own queue */ - job = camel_imapx_server_ref_job (imapx_server, mailbox, job_type, uid); - if (job) - return job; - - /* then try queue for all the opened servers */ - imapx_store = camel_imapx_server_ref_store (imapx_server); - if (!imapx_store) - return NULL; - - job = camel_imapx_store_ref_job (imapx_store, mailbox, job_type, uid); - - g_object_unref (imapx_store); - - return job; -} - -static void -imapx_expunge_uid_from_summary (CamelIMAPXServer *is, - gchar *uid, - gboolean unsolicited) -{ - CamelFolder *folder; - CamelIMAPXMailbox *mailbox; - CamelMessageInfo *mi; - guint32 messages; - - g_mutex_lock (&is->priv->select_lock); - mailbox = g_weak_ref_get (&is->priv->select_mailbox); - g_mutex_unlock (&is->priv->select_lock); - - g_return_if_fail (mailbox != NULL); - - folder = imapx_server_ref_folder (is, mailbox); - g_return_if_fail (folder != NULL); - - messages = camel_imapx_mailbox_get_messages (mailbox); - - if (unsolicited && messages > 0) - camel_imapx_mailbox_set_messages (mailbox, messages - 1); - - if (is->priv->changes == NULL) - is->priv->changes = camel_folder_change_info_new (); - - mi = camel_folder_summary_peek_loaded (folder->summary, uid); - if (mi) { - camel_folder_summary_remove (folder->summary, mi); - camel_message_info_unref (mi); - } else { - camel_folder_summary_remove_uid (folder->summary, uid); - } - - camel_folder_change_info_remove_uid (is->priv->changes, uid); - - if (imapx_in_idle (is)) { - camel_folder_summary_save_to_db (folder->summary, NULL); - imapx_update_store_summary (folder); - camel_folder_changed (folder, is->priv->changes); - - camel_folder_change_info_clear (is->priv->changes); - } - - g_object_unref (folder); - g_object_unref (mailbox); -} - -/* untagged response handler functions */ - -static gboolean -imapx_untagged_capability (CamelIMAPXServer *is, - GInputStream *input_stream, - GCancellable *cancellable, - GError **error) -{ - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - - if (is->cinfo != NULL) - imapx_free_capability (is->cinfo); - - is->cinfo = imapx_parse_capability ( - CAMEL_IMAPX_INPUT_STREAM (input_stream), cancellable, error); - - if (is->cinfo == NULL) - return FALSE; - - c (is->tagprefix, "got capability flags %08x\n", is->cinfo->capa); - - imapx_server_stash_command_arguments (is); - - return TRUE; -} - -static gboolean -imapx_untagged_expunge (CamelIMAPXServer *is, - GInputStream *input_stream, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXMailbox *mailbox; - CamelIMAPXJob *job = NULL; - guint32 expunge = 0; - - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - - expunge = is->priv->context->id; - job = imapx_match_active_job (is, IMAPX_JOB_EXPUNGE, NULL); - - /* If there is a job running, let it handle the deletion */ - if (job != NULL) - return TRUE; - - job = imapx_match_active_job (is, IMAPX_JOB_COPY_MESSAGE, NULL); - /* Ignore EXPUNGE responses when not running a COPY(MOVE)_MESSAGE job */ - if (!job) { - c (is->tagprefix, "ignoring untagged expunge: %lu\n", is->priv->context->id); - return TRUE; - } - - c (is->tagprefix, "expunged: %lu\n", is->priv->context->id); - - g_mutex_lock (&is->priv->select_lock); - mailbox = g_weak_ref_get (&is->priv->select_mailbox); - g_mutex_unlock (&is->priv->select_lock); - - if (mailbox != NULL) { - CamelFolder *folder; - gchar *uid; - - folder = imapx_server_ref_folder (is, mailbox); - g_return_val_if_fail (folder != NULL, FALSE); - - uid = camel_imapx_dup_uid_from_summary_index ( - folder, expunge - 1); - - if (uid != NULL) - imapx_expunge_uid_from_summary (is, uid, TRUE); - - g_object_unref (folder); - g_object_unref (mailbox); - } - - return TRUE; -} - -static gboolean -imapx_untagged_vanished (CamelIMAPXServer *is, - GInputStream *input_stream, - GCancellable *cancellable, - GError **error) -{ - CamelFolder *folder; - CamelIMAPXMailbox *mailbox; - GArray *uids; - GList *uid_list = NULL; - gboolean unsolicited = TRUE; - guint ii = 0; - guint len = 0; - guchar *token = NULL; - gint tok = 0; - - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); tok = camel_imapx_input_stream_token ( CAMEL_IMAPX_INPUT_STREAM (input_stream), @@ -2014,9 +954,7 @@ imapx_untagged_vanished (CamelIMAPXServe if (uids == NULL) return FALSE; - g_mutex_lock (&is->priv->select_lock); - mailbox = g_weak_ref_get (&is->priv->select_mailbox); - g_mutex_unlock (&is->priv->select_lock); + mailbox = camel_imapx_server_ref_pending_or_selected (is); g_return_val_if_fail (mailbox != NULL, FALSE); @@ -2030,7 +968,7 @@ imapx_untagged_vanished (CamelIMAPXServe if (messages < uids->len) { c ( - is->tagprefix, + is->priv->tagprefix, "Error: mailbox messages (%u) is " "fewer than vanished %u\n", messages, uids->len); @@ -2042,8 +980,9 @@ imapx_untagged_vanished (CamelIMAPXServe camel_imapx_mailbox_set_messages (mailbox, messages); } - if (is->priv->changes == NULL) - is->priv->changes = camel_folder_change_info_new (); + g_return_val_if_fail (is->priv->changes != NULL, FALSE); + + g_mutex_lock (&is->priv->changes_lock); for (ii = 0; ii < uids->len; ii++) { guint32 uid; @@ -2051,23 +990,44 @@ imapx_untagged_vanished (CamelIMAPXServe uid = g_array_index (uids, guint32, ii); - e (is->tagprefix, "vanished: %u\n", uid); + e (is->priv->tagprefix, "vanished: %u\n", uid); str = g_strdup_printf ("%u", uid); uid_list = g_list_prepend (uid_list, str); camel_folder_change_info_remove_uid (is->priv->changes, str); } + g_mutex_unlock (&is->priv->changes_lock); + uid_list = g_list_reverse (uid_list); camel_folder_summary_remove_uids (folder->summary, uid_list); /* If the response is truly unsolicited (e.g. via NOTIFY) * then go ahead and emit the change notification now. */ - if (camel_imapx_command_queue_is_empty (is->queue)) { - camel_folder_summary_save_to_db (folder->summary, NULL); - imapx_update_store_summary (folder); - camel_folder_changed (folder, is->priv->changes); - camel_folder_change_info_clear (is->priv->changes); + COMMAND_LOCK (is); + if (!is->priv->current_command) { + COMMAND_UNLOCK (is); + + g_mutex_lock (&is->priv->changes_lock); + if (is->priv->changes->uid_removed && + is->priv->changes->uid_removed->len >= 100) { + CamelFolderChangeInfo *changes; + + changes = is->priv->changes; + is->priv->changes = camel_folder_change_info_new (); + + g_mutex_unlock (&is->priv->changes_lock); + + camel_folder_summary_save_to_db (folder->summary, NULL); + imapx_update_store_summary (folder); + + camel_folder_changed (folder, changes); + camel_folder_change_info_free (changes); + } else { + g_mutex_unlock (&is->priv->changes_lock); + } + } else { + COMMAND_UNLOCK (is); } g_list_free_full (uid_list, (GDestroyNotify) g_free); @@ -2118,7 +1078,7 @@ imapx_untagged_exists (CamelIMAPXServer g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - mailbox = camel_imapx_server_ref_selected (is); + mailbox = camel_imapx_server_ref_pending_or_selected (is); if (mailbox == NULL) { g_warning ("%s: No mailbox available", G_STRFUNC); @@ -2127,21 +1087,22 @@ imapx_untagged_exists (CamelIMAPXServer exists = (guint32) is->priv->context->id; + c (is->priv->tagprefix, "%s: updating mailbox '%s' messages: %d ~> %d\n", G_STRFUNC, + camel_imapx_mailbox_get_name (mailbox), + camel_imapx_mailbox_get_messages (mailbox), + exists); + camel_imapx_mailbox_set_messages (mailbox, exists); folder = imapx_server_ref_folder (is, mailbox); g_return_val_if_fail (folder != NULL, FALSE); - if (imapx_in_idle (is)) { + if (camel_imapx_server_is_in_idle (is)) { guint count; count = camel_folder_summary_count (folder->summary); - if (count < exists) { - CamelIMAPXIdleStopResult stop_result; - - stop_result = imapx_stop_idle (is, error); - success = (stop_result != IMAPX_IDLE_STOP_ERROR); - } + if (count < exists) + g_signal_emit (is, signals[REFRESH_MAILBOX], 0, mailbox); } g_object_unref (folder); @@ -2165,7 +1126,7 @@ imapx_untagged_flags (CamelIMAPXServer * CAMEL_IMAPX_INPUT_STREAM (input_stream), &flags, NULL, cancellable, error); - c (is->tagprefix, "flags: %08x\n", flags); + c (is->priv->tagprefix, "flags: %08x\n", flags); return success; } @@ -2204,72 +1165,50 @@ imapx_untagged_fetch (CamelIMAPXServer * } if ((finfo->got & (FETCH_BODY | FETCH_UID)) == (FETCH_BODY | FETCH_UID)) { - CamelIMAPXJob *job; - GetMessageData *data; - - job = imapx_match_active_job ( - is, IMAPX_JOB_GET_MESSAGE, finfo->uid); - if (job == NULL) { - g_warn_if_reached (); - return FALSE; - } - - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); + GOutputStream *output_stream; + gconstpointer body_data; + gsize body_size; - /* This must've been a get-message request, - * fill out the body stream, in the right spot. */ + g_return_val_if_fail (is->priv->get_message_stream != NULL, FALSE); - if (job != NULL) { - GOutputStream *output_stream; - gconstpointer body_data; - gsize body_size; + /* Fill out the body stream, in the right spot. */ - if (data->use_multi_fetch) { - data->body_offset = finfo->offset; - g_seekable_seek ( - G_SEEKABLE (data->stream), - finfo->offset, G_SEEK_SET, - NULL, NULL); - } + g_seekable_seek ( + G_SEEKABLE (is->priv->get_message_stream), + finfo->offset, G_SEEK_SET, + NULL, NULL); - output_stream = - g_io_stream_get_output_stream (data->stream); + output_stream = g_io_stream_get_output_stream (is->priv->get_message_stream); - body_data = g_bytes_get_data (finfo->body, &body_size); + body_data = g_bytes_get_data (finfo->body, &body_size); - /* Sometimes the server, like Microsoft Exchange, reports larger message - size than it actually is, which results in no data being read from - the server for that particular offset. */ - if (body_size) { - g_mutex_lock (&is->priv->stream_lock); - if (!g_output_stream_write_all ( - output_stream, body_data, body_size, - NULL, cancellable, error)) { - g_mutex_unlock (&is->priv->stream_lock); - g_prefix_error ( - error, "%s: ", - _("Error writing to cache stream")); - return FALSE; - } + /* Sometimes the server, like Microsoft Exchange, reports larger message + size than it actually is, which results in no data being read from + the server for that particular offset. */ + if (body_size) { + g_mutex_lock (&is->priv->stream_lock); + if (!g_output_stream_write_all ( + output_stream, body_data, body_size, + NULL, cancellable, error)) { g_mutex_unlock (&is->priv->stream_lock); + g_prefix_error ( + error, "%s: ", + _("Error writing to cache stream")); + imapx_free_fetch (finfo); + return FALSE; } + g_mutex_unlock (&is->priv->stream_lock); } } if ((finfo->got & FETCH_FLAGS) && !(finfo->got & FETCH_HEADER)) { - CamelIMAPXJob *job; CamelIMAPXMailbox *select_mailbox; CamelIMAPXMailbox *select_pending; - RefreshInfoData *data = NULL; - job = imapx_match_active_job ( - is, IMAPX_JOB_FETCH_NEW_MESSAGES | - IMAPX_JOB_REFRESH_INFO, NULL); - - if (job != NULL) { - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); + if (is->priv->fetch_changes_mailbox) { + g_return_val_if_fail (is->priv->fetch_changes_mailbox != NULL, FALSE); + g_return_val_if_fail (is->priv->fetch_changes_folder != NULL, FALSE); + g_return_val_if_fail (is->priv->fetch_changes_infos != NULL, FALSE); } g_mutex_lock (&is->priv->select_lock); @@ -2280,27 +1219,49 @@ imapx_untagged_fetch (CamelIMAPXServer * /* This is either a refresh_info job, check to see if it is * and update if so, otherwise it must've been an unsolicited * response, so update the summary to match. */ - if (data && (finfo->got & FETCH_UID) && data->scan_changes) { - struct _refresh_info r; + if ((finfo->got & FETCH_UID) != 0 && is->priv->fetch_changes_folder && is->priv->fetch_changes_infos) { + FetchChangesInfo *nfo; + gint64 monotonic_time; + gint n_messages; + + nfo = g_hash_table_lookup (is->priv->fetch_changes_infos, finfo->uid); + if (!nfo) { + nfo = g_new0 (FetchChangesInfo, 1); + + g_hash_table_insert (is->priv->fetch_changes_infos, (gpointer) camel_pstring_strdup (finfo->uid), nfo); + } - r.uid = finfo->uid; - finfo->uid = NULL; - r.server_flags = finfo->flags; - r.server_user_flags = finfo->user_flags; + nfo->server_flags = finfo->flags; + nfo->server_user_flags = finfo->user_flags; finfo->user_flags = NULL; - r.exists = FALSE; - g_array_append_val (data->infos, r); + monotonic_time = g_get_monotonic_time (); + n_messages = camel_imapx_mailbox_get_messages (is->priv->fetch_changes_mailbox); + + if (n_messages > 0 && is->priv->fetch_changes_last_progress + G_USEC_PER_SEC / 2 < monotonic_time && + is->priv->context && is->priv->context->id <= n_messages) { + COMMAND_LOCK (is); + + if (is->priv->current_command) { + COMMAND_UNLOCK (is); + + is->priv->fetch_changes_last_progress = monotonic_time; + + camel_operation_progress (cancellable, 100 * is->priv->context->id + / camel_imapx_mailbox_get_messages (is->priv->fetch_changes_mailbox)); + } else { + COMMAND_UNLOCK (is); + } + } } else if (select_mailbox != NULL) { CamelFolder *select_folder; CamelMessageInfo *mi = NULL; gboolean changed = FALSE; gchar *uid = NULL; - c (is->tagprefix, "flag changed: %lu\n", is->priv->context->id); + c (is->priv->tagprefix, "flag changed: %lu\n", is->priv->context->id); - select_folder = - imapx_server_ref_folder (is, select_mailbox); + select_folder = imapx_server_ref_folder (is, select_mailbox); g_return_val_if_fail (select_folder != NULL, FALSE); if (finfo->got & FETCH_UID) { @@ -2313,8 +1274,7 @@ imapx_untagged_fetch (CamelIMAPXServer * } if (uid) { - mi = camel_folder_summary_get ( - select_folder->summary, uid); + mi = camel_folder_summary_get (select_folder->summary, uid); if (mi) { /* It's unsolicited _unless_ select_pending (i.e. during * a QRESYNC SELECT */ @@ -2327,29 +1287,30 @@ imapx_untagged_fetch (CamelIMAPXServer * } else { /* This (UID + FLAGS for previously unknown message) might * happen during a SELECT (QRESYNC). We should use it. */ - c (is->tagprefix, "flags changed for unknown uid %s\n.", uid); + c (is->priv->tagprefix, "flags changed for unknown uid %s\n.", uid); } finfo->user_flags = NULL; } if (changed) { - if (is->priv->changes == NULL) - is->priv->changes = - camel_folder_change_info_new (); + g_return_val_if_fail (is->priv->changes != NULL, FALSE); - camel_folder_change_info_change_uid ( - is->priv->changes, uid); + g_mutex_lock (&is->priv->changes_lock); + camel_folder_change_info_change_uid (is->priv->changes, uid); + g_mutex_unlock (&is->priv->changes_lock); } g_free (uid); - if (changed && imapx_in_idle (is)) { - camel_folder_summary_save_to_db ( - select_folder->summary, NULL); + if (changed && camel_imapx_server_is_in_idle (is)) { + camel_folder_summary_save_to_db (select_folder->summary, NULL); imapx_update_store_summary (select_folder); - camel_folder_changed ( - select_folder, is->priv->changes); - camel_folder_change_info_clear ( - is->priv->changes); + + g_mutex_lock (&is->priv->changes_lock); + + camel_folder_changed (select_folder, is->priv->changes); + camel_folder_change_info_clear (is->priv->changes); + + g_mutex_unlock (&is->priv->changes_lock); } if (mi) @@ -2363,145 +1324,123 @@ imapx_untagged_fetch (CamelIMAPXServer * } if ((finfo->got & (FETCH_HEADER | FETCH_UID)) == (FETCH_HEADER | FETCH_UID)) { - CamelIMAPXJob *job; + CamelIMAPXMailbox *mailbox; + CamelFolder *folder; + CamelMimeParser *mp; + CamelMessageInfo *mi; + guint32 messages; + guint32 unseen; + guint32 uidnext; /* This must be a refresh info job as well, but it has * asked for new messages to be added to the index. */ - job = imapx_match_active_job ( - is, IMAPX_JOB_FETCH_NEW_MESSAGES | - IMAPX_JOB_REFRESH_INFO, NULL); - - if (job != NULL) { - CamelIMAPXMailbox *mailbox; - CamelFolder *folder; - CamelMimeParser *mp; - CamelMessageInfo *mi; - guint32 messages; - guint32 unseen; - guint32 uidnext; + if (is->priv->fetch_changes_mailbox) { + g_return_val_if_fail (is->priv->fetch_changes_mailbox != NULL, FALSE); + g_return_val_if_fail (is->priv->fetch_changes_folder != NULL, FALSE); + g_return_val_if_fail (is->priv->fetch_changes_infos != NULL, FALSE); + + folder = g_object_ref (is->priv->fetch_changes_folder); + mailbox = g_object_ref (is->priv->fetch_changes_mailbox); + } else { + mailbox = camel_imapx_server_ref_selected (is); + folder = mailbox ? imapx_server_ref_folder (is, mailbox) : NULL; + } - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_val_if_fail (mailbox != NULL, FALSE); + if (!mailbox || !folder || (!(finfo->got & FETCH_FLAGS) && !is->priv->fetch_changes_infos)) { + g_clear_object (&mailbox); + g_clear_object (&folder); + imapx_free_fetch (finfo); - folder = imapx_server_ref_folder (is, mailbox); - g_return_val_if_fail (folder != NULL, FALSE); + return TRUE; + } + + messages = camel_imapx_mailbox_get_messages (mailbox); + unseen = camel_imapx_mailbox_get_unseen (mailbox); + uidnext = camel_imapx_mailbox_get_uidnext (mailbox); + + /* Do we want to save these headers for later too? Do we care? */ + + mp = camel_mime_parser_new (); + camel_mime_parser_init_with_bytes (mp, finfo->header); + mi = camel_folder_summary_info_new_from_parser (folder->summary, mp); + g_object_unref (mp); + + if (mi != NULL) { + guint32 server_flags; + CamelFlag *server_user_flags; + CamelMessageInfoBase *binfo; + gboolean free_user_flags = FALSE; - messages = camel_imapx_mailbox_get_messages (mailbox); - unseen = camel_imapx_mailbox_get_unseen (mailbox); - uidnext = camel_imapx_mailbox_get_uidnext (mailbox); - - /* Do we want to save these headers for later too? Do we care? */ - - mp = camel_mime_parser_new (); - camel_mime_parser_init_with_bytes (mp, finfo->header); - mi = camel_folder_summary_info_new_from_parser (folder->summary, mp); - g_object_unref (mp); - - if (mi != NULL) { - guint32 server_flags; - CamelFlag *server_user_flags; - CamelMessageInfoBase *binfo; - gboolean free_user_flags = FALSE; - - mi->uid = camel_pstring_strdup (finfo->uid); - - if (!(finfo->got & FETCH_FLAGS)) { - RefreshInfoData *data; - struct _refresh_info *r = NULL; - gint min, max, mid; - gboolean found = FALSE; - - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); - - min = data->last_index; - max = data->index - 1; - - /* array is sorted, so use a binary search */ - do { - gint cmp = 0; - - mid = (min + max) / 2; - r = &g_array_index (data->infos, struct _refresh_info, mid); - cmp = imapx_refresh_info_uid_cmp ( - finfo->uid, - r->uid, - is->priv->context->fetch_order == CAMEL_SORT_ASCENDING); - - if (cmp > 0) - min = mid + 1; - else if (cmp < 0) - max = mid - 1; - else - found = TRUE; + mi->uid = camel_pstring_strdup (finfo->uid); - } while (!found && min <= max); + if (!(finfo->got & FETCH_FLAGS) && is->priv->fetch_changes_infos) { + FetchChangesInfo *nfo; - g_return_val_if_fail (found, FALSE); + nfo = g_hash_table_lookup (is->priv->fetch_changes_infos, finfo->uid); + g_return_val_if_fail (nfo != NULL, FALSE); - server_flags = r->server_flags; - server_user_flags = r->server_user_flags; + server_flags = nfo->server_flags; + server_user_flags = nfo->server_user_flags; + } else { + server_flags = finfo->flags; + server_user_flags = finfo->user_flags; + /* free user_flags ? */ + finfo->user_flags = NULL; + free_user_flags = TRUE; + } + + /* If the message is a really new one -- equal or higher than what + * we know as UIDNEXT for the folder, then it came in since we last + * fetched UIDNEXT and UNREAD count. We'll update UIDNEXT in the + * command completion, but update UNREAD count now according to the + * message SEEN flag */ + if (!(server_flags & CAMEL_MESSAGE_SEEN)) { + guint64 uidl; + + uidl = strtoull (mi->uid, NULL, 10); + + if (uidl >= uidnext) { + c (is->priv->tagprefix, "Updating unseen count for new message %s\n", mi->uid); + camel_imapx_mailbox_set_unseen (mailbox, unseen + 1); } else { - server_flags = finfo->flags; - server_user_flags = finfo->user_flags; - /* free user_flags ? */ - finfo->user_flags = NULL; - free_user_flags = TRUE; + c (is->priv->tagprefix, "Not updating unseen count for new message %s\n", mi->uid); } + } - /* If the message is a really new one -- equal or higher than what - * we know as UIDNEXT for the folder, then it came in since we last - * fetched UIDNEXT and UNREAD count. We'll update UIDNEXT in the - * command completion, but update UNREAD count now according to the - * message SEEN flag */ - if (!(server_flags & CAMEL_MESSAGE_SEEN)) { - guint64 uidl; - - uidl = strtoull (mi->uid, NULL, 10); - - if (uidl >= uidnext) { - c (is->tagprefix, "Updating unseen count for new message %s\n", mi->uid); - camel_imapx_mailbox_set_unseen (mailbox, unseen + 1); - } else { - c (is->tagprefix, "Not updating unseen count for new message %s\n", mi->uid); - } - } + binfo = (CamelMessageInfoBase *) mi; + binfo->size = finfo->size; - binfo = (CamelMessageInfoBase *) mi; - binfo->size = finfo->size; + camel_folder_summary_lock (folder->summary); - camel_folder_summary_lock (folder->summary); + if (!camel_folder_summary_check_uid (folder->summary, mi->uid)) { + imapx_set_message_info_flags_for_new_message (mi, server_flags, server_user_flags, FALSE, NULL, camel_imapx_mailbox_get_permanentflags (mailbox)); + camel_folder_summary_add (folder->summary, mi); - if (!camel_folder_summary_check_uid (folder->summary, mi->uid)) { - RefreshInfoData *data; + g_mutex_lock (&is->priv->changes_lock); - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); + camel_folder_change_info_add_uid (is->priv->changes, mi->uid); + camel_folder_change_info_recent_uid (is->priv->changes, mi->uid); - imapx_set_message_info_flags_for_new_message (mi, server_flags, server_user_flags, FALSE, NULL, camel_imapx_mailbox_get_permanentflags (mailbox)); - camel_folder_summary_add (folder->summary, mi); - camel_folder_change_info_add_uid (data->changes, mi->uid); + g_mutex_unlock (&is->priv->changes_lock); - camel_folder_change_info_recent_uid (data->changes, mi->uid); + if (messages > 0) { + gint cnt = (camel_folder_summary_count (folder->summary) * 100) / messages; - if (messages > 0) { - gint cnt = (camel_folder_summary_count (folder->summary) * 100) / messages; - camel_operation_progress (cancellable, cnt ? cnt : 1); - } - } else { - camel_message_info_unref (mi); + camel_operation_progress (cancellable, cnt ? cnt : 1); } - - camel_folder_summary_unlock (folder->summary); - - if (free_user_flags && server_user_flags) - camel_flag_list_free (&server_user_flags); + } else { + camel_message_info_unref (mi); } - g_object_unref (folder); - g_object_unref (mailbox); + camel_folder_summary_unlock (folder->summary); + + if (free_user_flags && server_user_flags) + camel_flag_list_free (&server_user_flags); } + + g_clear_object (&mailbox); + g_clear_object (&folder); } imapx_free_fetch (finfo); @@ -2670,7 +1609,7 @@ imapx_untagged_recent (CamelIMAPXServer g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - mailbox = camel_imapx_server_ref_selected (is); + mailbox = camel_imapx_server_ref_pending_or_selected (is); if (mailbox == NULL) { g_warning ("%s: No mailbox available", G_STRFUNC); @@ -2679,6 +1618,11 @@ imapx_untagged_recent (CamelIMAPXServer recent = (guint32) is->priv->context->id; + c (is->priv->tagprefix, "%s: updating mailbox '%s' recent: %d ~> %d\n", G_STRFUNC, + camel_imapx_mailbox_get_name (mailbox), + camel_imapx_mailbox_get_recent (mailbox), + recent); + camel_imapx_mailbox_set_recent (mailbox, recent); g_object_unref (mailbox); @@ -2798,7 +1742,9 @@ imapx_untagged_bye (CamelIMAPXServer *is /* XXX It's weird to be setting an error on success, * but it's to indicate the server hung up on us. */ if (success) { - c (is->tagprefix, "BYE: %s\n", token); + g_strstrip ((gchar *) token); + + c (is->priv->tagprefix, "BYE: %s\n", token); g_set_error ( error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT, "IMAP server said BYE: %s", token); @@ -2806,7 +1752,7 @@ imapx_untagged_bye (CamelIMAPXServer *is g_free (token); - is->state = IMAPX_SHUTDOWN; + is->priv->state = IMAPX_SHUTDOWN; return FALSE; } @@ -2819,9 +1765,9 @@ imapx_untagged_preauth (CamelIMAPXServer { g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - c (is->tagprefix, "preauthenticated\n"); - if (is->state < IMAPX_AUTHENTICATED) - is->state = IMAPX_AUTHENTICATED; + c (is->priv->tagprefix, "preauthenticated\n"); + if (is->priv->state < IMAPX_AUTHENTICATED) + is->priv->state = IMAPX_AUTHENTICATED; return TRUE; } @@ -2851,15 +1797,7 @@ imapx_untagged_ok_no_bad (CamelIMAPXServ overwritten with a value from a different mailbox, thus the offline cache will persist, instead of being vanished. */ - g_mutex_lock (&is->priv->select_lock); - - mailbox = g_weak_ref_get (&is->priv->select_pending); - if (!mailbox) - mailbox = g_weak_ref_get (&is->priv->select_mailbox); - if (!mailbox) - mailbox = g_weak_ref_get (&is->priv->select_closing); - - g_mutex_unlock (&is->priv->select_lock); + mailbox = camel_imapx_server_ref_pending_or_selected (is); is->priv->context->sinfo = imapx_parse_status ( CAMEL_IMAPX_INPUT_STREAM (input_stream), @@ -2873,43 +1811,34 @@ imapx_untagged_ok_no_bad (CamelIMAPXServ switch (is->priv->context->sinfo->condition) { case IMAPX_CLOSED: c ( - is->tagprefix, + is->priv->tagprefix, "previously selected mailbox is now closed\n"); { CamelIMAPXMailbox *select_mailbox; - CamelIMAPXMailbox *select_closing; CamelIMAPXMailbox *select_pending; g_mutex_lock (&is->priv->select_lock); - select_mailbox = - g_weak_ref_get (&is->priv->select_mailbox); - select_closing = - g_weak_ref_get (&is->priv->select_closing); - select_pending = - g_weak_ref_get (&is->priv->select_pending); - - if (select_mailbox == NULL) - g_weak_ref_set ( - &is->priv->select_mailbox, - select_pending); + select_mailbox = g_weak_ref_get (&is->priv->select_mailbox); + select_pending = g_weak_ref_get (&is->priv->select_pending); - g_weak_ref_set (&is->priv->select_closing, NULL); + if (select_mailbox == NULL) { + g_weak_ref_set (&is->priv->select_mailbox, select_pending); - g_mutex_unlock (&is->priv->select_lock); + if (select_pending) + is->priv->last_selected_mailbox_change_stamp = camel_imapx_mailbox_get_change_stamp (select_pending); + else + is->priv->last_selected_mailbox_change_stamp = 0; + } - if (select_closing != NULL) - g_signal_emit ( - is, signals[MAILBOX_CLOSED], 0, - select_closing); + g_mutex_unlock (&is->priv->select_lock); g_clear_object (&select_mailbox); - g_clear_object (&select_closing); g_clear_object (&select_pending); } break; case IMAPX_ALERT: - c (is->tagprefix, "ALERT!: %s\n", is->priv->context->sinfo->text); + c (is->priv->tagprefix, "ALERT!: %s\n", is->priv->context->sinfo->text); { const gchar *alert_message; gboolean emit_alert = FALSE; @@ -2938,12 +1867,15 @@ imapx_untagged_ok_no_bad (CamelIMAPXServ service = CAMEL_SERVICE (store); session = camel_service_ref_session (service); - camel_session_user_alert ( - session, service, - CAMEL_SESSION_ALERT_WARNING, - alert_message); + if (session) { + camel_session_user_alert ( + session, service, + CAMEL_SESSION_ALERT_WARNING, + alert_message); + + g_object_unref (session); + } - g_object_unref (session); g_object_unref (store); } @@ -2951,24 +1883,48 @@ imapx_untagged_ok_no_bad (CamelIMAPXServ } break; case IMAPX_PARSE: - c (is->tagprefix, "PARSE: %s\n", is->priv->context->sinfo->text); + c (is->priv->tagprefix, "PARSE: %s\n", is->priv->context->sinfo->text); break; case IMAPX_CAPABILITY: if (is->priv->context->sinfo->u.cinfo) { - struct _capability_info *cinfo = is->cinfo; - is->cinfo = is->priv->context->sinfo->u.cinfo; + struct _capability_info *cinfo; + + g_mutex_lock (&is->priv->stream_lock); + + cinfo = is->priv->cinfo; + is->priv->cinfo = is->priv->context->sinfo->u.cinfo; is->priv->context->sinfo->u.cinfo = NULL; if (cinfo) imapx_free_capability (cinfo); - c (is->tagprefix, "got capability flags %08x\n", is->cinfo ? is->cinfo->capa : 0xFFFFFFFF); + c (is->priv->tagprefix, "got capability flags %08x\n", is->priv->cinfo ? is->priv->cinfo->capa : 0xFFFFFFFF); + + if (is->priv->context->sinfo->text) { + guint32 list_extended = imapx_lookup_capability ("LIST-EXTENDED"); + + is->priv->is_cyrus = is->priv->is_cyrus || camel_strstrcase (is->priv->context->sinfo->text, "cyrus"); + if (is->priv->is_cyrus && is->priv->cinfo && (is->priv->cinfo->capa & list_extended) != 0) { + /* Disable LIST-EXTENDED for cyrus servers */ + c (is->priv->tagprefix, "Disabling LIST-EXTENDED extension for a Cyrus server\n"); + is->priv->cinfo->capa &= ~list_extended; + } + } + imapx_server_stash_command_arguments (is); + + g_mutex_unlock (&is->priv->stream_lock); } break; + case IMAPX_COPYUID: + imapx_free_status (is->priv->copyuid_status); + is->priv->copyuid_status = is->priv->context->sinfo; + is->priv->context->sinfo = NULL; + break; default: break; } imapx_free_status (is->priv->context->sinfo); + is->priv->context->sinfo = NULL; return TRUE; } @@ -3001,7 +1957,7 @@ imapx_untagged (CamelIMAPXServer *is, is->priv->context->lsub = FALSE; is->priv->context->fetch_order = fetch_order; - e (is->tagprefix, "got untagged response\n"); + e (is->priv->tagprefix, "got untagged response\n"); is->priv->context->id = 0; is->priv->context->tok = camel_imapx_input_stream_token ( CAMEL_IMAPX_INPUT_STREAM (input_stream), @@ -3025,12 +1981,12 @@ imapx_untagged (CamelIMAPXServer *is, if (is->priv->context->tok == '\n') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "truncated server response"); goto exit; } - e (is->tagprefix, "Have token '%s' id %lu\n", is->priv->context->token, is->priv->context->id); + e (is->priv->tagprefix, "Have token '%s' id %lu\n", is->priv->context->token, is->priv->context->id); p = is->priv->context->token; while ((c = *p)) *p++ = g_ascii_toupper ((gchar) c); @@ -3042,12 +1998,12 @@ imapx_untagged (CamelIMAPXServer *is, desc = g_hash_table_lookup (is->priv->untagged_handlers, token); if (desc == NULL) { /* unknown response, just ignore it */ - c (is->tagprefix, "unknown token: %s\n", is->priv->context->token); + c (is->priv->tagprefix, "unknown token: %s\n", is->priv->context->token); break; } if (desc->handler == NULL) { /* no handler function, ignore token */ - c (is->tagprefix, "no handler for token: %s\n", is->priv->context->token); + c (is->priv->tagprefix, "no handler for token: %s\n", is->priv->context->token); break; } @@ -3085,6 +2041,61 @@ exit: return success; } +static gssize +imapx_server_write_file_with_progress (GOutputStream *output_stream, + GInputStream *input_stream, + goffset file_size, + GCancellable *cancellable, + GError **error) +{ + gssize n_read; + gsize bytes_copied, n_written; + gchar buffer[8192]; + goffset file_offset; + gboolean res; + + g_return_val_if_fail (G_IS_OUTPUT_STREAM (output_stream), -1); + g_return_val_if_fail (G_IS_INPUT_STREAM (input_stream), -1); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + file_offset = 0; + bytes_copied = 0; + res = TRUE; + do { + n_read = g_input_stream_read (input_stream, buffer, sizeof (buffer), cancellable, error); + if (n_read == -1) { + res = FALSE; + break; + } + + if (n_read == 0) + break; + + if (!g_output_stream_write_all (output_stream, buffer, n_read, &n_written, cancellable, error) || n_written == -1) { + res = FALSE; + break; + } + + file_offset += n_read; + + if (file_size > 0) { + gdouble divd = (gdouble) file_offset / (gdouble) file_size; + camel_operation_progress (cancellable, (gint) (100 * divd)); + } + + bytes_copied += n_written; + if (bytes_copied > G_MAXSSIZE) + bytes_copied = G_MAXSSIZE; + } while (res); + + if (res) + return bytes_copied; + + return -1; +} + /* handle any continuation requests * either data continuations, or auth continuation */ static gboolean @@ -3095,7 +2106,7 @@ imapx_continuation (CamelIMAPXServer *is GCancellable *cancellable, GError **error) { - CamelIMAPXCommand *ic, *newliteral = NULL; + CamelIMAPXCommand *ic, *newic = NULL; CamelIMAPXCommandPart *cp; GList *link; gssize n_bytes_written; @@ -3105,7 +2116,7 @@ imapx_continuation (CamelIMAPXServer *is * can write while we have it ... so we dont need any * ohter lock here. All other writes go through * queue-lock */ - if (imapx_in_idle (is)) { + if (camel_imapx_server_is_in_idle (is)) { success = camel_imapx_input_stream_skip ( CAMEL_IMAPX_INPUT_STREAM (input_stream), cancellable, error); @@ -3113,57 +2124,38 @@ imapx_continuation (CamelIMAPXServer *is if (!success) return FALSE; - c (is->tagprefix, "Got continuation response for IDLE \n"); - g_rec_mutex_lock (&is->priv->idle_lock); - /* We might have actually sent the DONE already! */ - if (is->priv->idle_state == IMAPX_IDLE_ISSUED) { - is->priv->idle_state = IMAPX_IDLE_STARTED; - } else if (is->priv->idle_state == IMAPX_IDLE_CANCEL) { - /* IDLE got cancelled after we sent the command, while - * we were waiting for this continuation. Send DONE - * immediately. */ - if (!imapx_command_idle_stop (is, error)) { - g_rec_mutex_unlock (&is->priv->idle_lock); - return FALSE; - } - is->priv->idle_state = IMAPX_IDLE_WAIT_DONE; - } else if (is->priv->idle_state == IMAPX_IDLE_WAIT_DONE) { - /* Do nothing, just wait */ - } else { - c ( - is->tagprefix, "idle starts in wrong state %d\n", - is->priv->idle_state); - } - g_rec_mutex_unlock (&is->priv->idle_lock); + c (is->priv->tagprefix, "Got continuation response for IDLE \n"); - QUEUE_LOCK (is); - is->literal = NULL; - imapx_command_start_next (is); - QUEUE_UNLOCK (is); + g_mutex_lock (&is->priv->idle_lock); + is->priv->idle_state = IMAPX_IDLE_STATE_RUNNING; + g_cond_broadcast (&is->priv->idle_cond); + g_mutex_unlock (&is->priv->idle_lock); return TRUE; } - ic = is->literal; + ic = is->priv->continuation_command; if (!litplus) { if (ic == NULL) { - c (is->tagprefix, "got continuation response with no outstanding continuation requests?\n"); + c (is->priv->tagprefix, "got continuation response with no outstanding continuation requests?\n"); return camel_imapx_input_stream_skip ( CAMEL_IMAPX_INPUT_STREAM (input_stream), cancellable, error); } - c (is->tagprefix, "got continuation response for data\n"); + c (is->priv->tagprefix, "got continuation response for data\n"); } else { - c (is->tagprefix, "sending LITERAL+ continuation\n"); + c (is->priv->tagprefix, "sending LITERAL+ continuation\n"); + g_return_val_if_fail (ic != NULL, FALSE); } - link = ic->current_part; + /* coverity[deadcode] */ + link = ic ? ic->current_part : NULL; g_return_val_if_fail (link != NULL, FALSE); cp = (CamelIMAPXCommandPart *) link->data; switch (cp->type & CAMEL_IMAPX_COMMAND_MASK) { case CAMEL_IMAPX_COMMAND_DATAWRAPPER: - c (is->tagprefix, "writing data wrapper to literal\n"); + c (is->priv->tagprefix, "writing data wrapper to literal\n"); n_bytes_written = camel_data_wrapper_write_to_output_stream_sync ( CAMEL_DATA_WRAPPER (cp->ob), @@ -3188,7 +2180,7 @@ imapx_continuation (CamelIMAPXServer *is g_free (token); if (resp == NULL) return FALSE; - c (is->tagprefix, "got auth continuation, feeding token '%s' back to auth mech\n", resp); + c (is->priv->tagprefix, "got auth continuation, feeding token '%s' back to auth mech\n", resp); g_mutex_lock (&is->priv->stream_lock); n_bytes_written = g_output_stream_write_all ( @@ -3202,15 +2194,17 @@ imapx_continuation (CamelIMAPXServer *is /* we want to keep getting called until we get a status reponse from the server * ignore what sasl tells us */ - newliteral = ic; + newic = ic; /* We already ate the end of the input stream line */ goto noskip; break; } case CAMEL_IMAPX_COMMAND_FILE: { GFile *file; + GFileInfo *file_info; GFileInputStream *file_input_stream; + goffset file_size = 0; - c (is->tagprefix, "writing file '%s' to literal\n", (gchar *) cp->ob); + c (is->priv->tagprefix, "writing file '%s' to literal\n", (gchar *) cp->ob); file = g_file_new_for_path (cp->ob); file_input_stream = g_file_read (file, cancellable, error); @@ -3219,14 +2213,22 @@ imapx_continuation (CamelIMAPXServer *is if (file_input_stream == NULL) return FALSE; + file_info = g_file_input_stream_query_info (file_input_stream, + G_FILE_ATTRIBUTE_STANDARD_SIZE, cancellable, NULL); + if (file_info) { + file_size = g_file_info_get_size (file_info); + g_object_unref (file_info); + } + g_mutex_lock (&is->priv->stream_lock); - n_bytes_written = g_output_stream_splice ( - output_stream, - G_INPUT_STREAM (file_input_stream), - G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE, - cancellable, error); + + n_bytes_written = imapx_server_write_file_with_progress ( + output_stream, G_INPUT_STREAM (file_input_stream), + file_size, cancellable, error); + g_mutex_unlock (&is->priv->stream_lock); + g_input_stream_close (G_INPUT_STREAM (file_input_stream), cancellable, NULL); g_object_unref (file_input_stream); if (n_bytes_written < 0) @@ -3244,9 +2246,8 @@ imapx_continuation (CamelIMAPXServer *is break; default: /* should we just ignore? */ - is->literal = NULL; g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "continuation response for non-continuation request"); return FALSE; } @@ -3266,7 +2267,7 @@ noskip: ic->current_part = link; cp = (CamelIMAPXCommandPart *) link->data; - c (is->tagprefix, "next part of command \"%c%05u: %s\"\n", is->tagprefix, ic->tag, cp->data); + c (is->priv->tagprefix, "next part of command \"%c%05u: %s\"\n", is->priv->tagprefix, ic->tag, cp->data); g_mutex_lock (&is->priv->stream_lock); n_bytes_written = g_output_stream_write_all ( @@ -3277,12 +2278,12 @@ noskip: return FALSE; if (cp->type & (CAMEL_IMAPX_COMMAND_CONTINUATION | CAMEL_IMAPX_COMMAND_LITERAL_PLUS)) { - newliteral = ic; + newic = ic; } else { - g_assert (g_list_next (link) == NULL); + g_warn_if_fail (g_list_next (link) == NULL); } } else { - c (is->tagprefix, "%p: queueing continuation\n", ic); + c (is->priv->tagprefix, "%p: queueing continuation\n", ic); } g_mutex_lock (&is->priv->stream_lock); @@ -3292,12 +2293,7 @@ noskip: if (n_bytes_written < 0) return FALSE; - QUEUE_LOCK (is); - is->literal = newliteral; - - if (!litplus) - imapx_command_start_next (is); - QUEUE_UNLOCK (is); + is->priv->continuation_command = newic; return TRUE; } @@ -3318,80 +2314,74 @@ imapx_completion (CamelIMAPXServer *is, /* Given "A0001 ...", 'A' = tag prefix, '0001' = tag. */ - if (token[0] != is->tagprefix) { + if (token[0] != is->priv->tagprefix) { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "Server sent unexpected response: %s", token); return FALSE; } tag = strtoul ((gchar *) token + 1, NULL, 10); - QUEUE_LOCK (is); + COMMAND_LOCK (is); - if (is->literal != NULL && is->literal->tag == tag) - ic = camel_imapx_command_ref (is->literal); + if (is->priv->current_command != NULL && is->priv->current_command->tag == tag) + ic = camel_imapx_command_ref (is->priv->current_command); else - ic = camel_imapx_command_queue_ref_by_tag (is->active, tag); + ic = NULL; - QUEUE_UNLOCK (is); + COMMAND_UNLOCK (is); if (ic == NULL) { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "got response tag unexpectedly: %s", token); return FALSE; } - c (is->tagprefix, "Got completion response for command %05u '%s'\n", ic->tag, ic->name); + c (is->priv->tagprefix, "Got completion response for command %05u '%s'\n", ic->tag, camel_imapx_job_get_kind_name (ic->job_kind)); + + g_mutex_lock (&is->priv->changes_lock); if (camel_folder_change_info_changed (is->priv->changes)) { - CamelFolder *folder; + CamelFolder *folder = NULL; CamelIMAPXMailbox *mailbox; + CamelFolderChangeInfo *changes; - g_mutex_lock (&is->priv->select_lock); - mailbox = g_weak_ref_get (&is->priv->select_mailbox); - g_mutex_unlock (&is->priv->select_lock); + changes = is->priv->changes; + is->priv->changes = camel_folder_change_info_new (); - g_return_val_if_fail (mailbox != NULL, FALSE); + g_mutex_unlock (&is->priv->changes_lock); - folder = imapx_server_ref_folder (is, mailbox); - g_return_val_if_fail (folder != NULL, FALSE); + mailbox = camel_imapx_server_ref_selected (is); - camel_folder_summary_save_to_db (folder->summary, NULL); + g_warn_if_fail (mailbox != NULL); - imapx_update_store_summary (folder); - camel_folder_changed (folder, is->priv->changes); - camel_folder_change_info_clear (is->priv->changes); + if (mailbox) { + folder = imapx_server_ref_folder (is, mailbox); + g_return_val_if_fail (folder != NULL, FALSE); - g_object_unref (folder); - g_object_unref (mailbox); - } + camel_folder_summary_save_to_db (folder->summary, NULL); - QUEUE_LOCK (is); + imapx_update_store_summary (folder); + camel_folder_changed (folder, changes); + } - /* Move the command from the active queue to the done queue. - * We're holding our own reference to the command so there's - * no risk of accidentally finalizing it here. */ - camel_imapx_command_queue_remove (is->active, ic); - imapx_server_command_removed (is, ic); - camel_imapx_command_queue_push_tail (is->done, ic); + camel_folder_change_info_free (changes); - if (is->literal == ic) - is->literal = NULL; + g_clear_object (&folder); + g_clear_object (&mailbox); + } else { + g_mutex_unlock (&is->priv->changes_lock); + } if (g_list_next (ic->current_part) != NULL) { - QUEUE_UNLOCK (is); g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, - "command still has unsent parts? %s", ic->name); + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, + "command still has unsent parts? %s", camel_imapx_job_get_kind_name (ic->job_kind)); goto exit; } - camel_imapx_command_queue_remove (is->done, ic); - - QUEUE_UNLOCK (is); - mailbox = camel_imapx_server_ref_selected (is); ic->status = imapx_parse_status ( @@ -3403,16 +2393,22 @@ imapx_completion (CamelIMAPXServer *is, if (ic->status == NULL) goto exit; - if (ic->complete != NULL) - ic->complete (is, ic); + if (ic->status->condition == IMAPX_CAPABILITY) { + guint32 list_extended = imapx_lookup_capability ("LIST-EXTENDED"); + + is->priv->is_cyrus = is->priv->is_cyrus || (ic->status->text && camel_strstrcase (ic->status->text, "cyrus")); + if (is->priv->is_cyrus && ic->status->u.cinfo && (ic->status->u.cinfo->capa & list_extended) != 0) { + /* Disable LIST-EXTENDED for cyrus servers */ + c (is->priv->tagprefix, "Disabling LIST-EXTENDED extension for a Cyrus server\n"); + ic->status->u.cinfo->capa &= ~list_extended; + } + } success = TRUE; exit: - QUEUE_LOCK (is); - imapx_command_start_next (is); - QUEUE_UNLOCK (is); + ic->completed = TRUE; camel_imapx_command_unref (ic); return success; @@ -3421,23 +2417,19 @@ exit: static gboolean imapx_step (CamelIMAPXServer *is, GInputStream *input_stream, + GOutputStream *output_stream, GCancellable *cancellable, GError **error) { - GOutputStream *output_stream; guint len; guchar *token; gint tok; gboolean success = FALSE; - // poll ? wait for other stuff? loop? tok = camel_imapx_input_stream_token ( CAMEL_IMAPX_INPUT_STREAM (input_stream), &token, &len, cancellable, error); - output_stream = camel_imapx_server_ref_output_stream (is); - g_return_val_if_fail (output_stream != NULL, FALSE); - switch (tok) { case IMAPX_TOK_ERROR: /* GError is already set. */ @@ -3458,6171 +2450,4070 @@ imapx_step (CamelIMAPXServer *is, break; default: g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "unexpected server response:"); break; } - g_clear_object (&output_stream); - return success; } -/* Used to run 1 command synchronously, - * use for capa, login, and namespaces only. */ -static gboolean -imapx_command_run (CamelIMAPXServer *is, - CamelIMAPXCommand *ic, - GCancellable *cancellable, - GError **error) +static void +imapx_server_set_streams (CamelIMAPXServer *is, + GInputStream *input_stream, + GOutputStream *output_stream) { - GInputStream *input_stream; - gboolean success = TRUE; - - input_stream = camel_imapx_server_ref_input_stream (is); - g_return_val_if_fail (input_stream != NULL, FALSE); - - camel_imapx_command_close (ic); - - QUEUE_LOCK (is); - imapx_command_start (is, ic); - QUEUE_UNLOCK (is); - - while (success && ic->status == NULL) - success = imapx_step (is, input_stream, cancellable, error); - - if (is->literal == ic) - is->literal = NULL; - - QUEUE_LOCK (is); - camel_imapx_command_queue_remove (is->active, ic); - imapx_server_command_removed (is, ic); - QUEUE_UNLOCK (is); - - g_object_unref (input_stream); + GConverter *logger; - return success; -} + if (input_stream != NULL) { + GInputStream *temp_stream; -static void -imapx_command_complete (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - camel_imapx_command_done (ic); - camel_imapx_command_unref (ic); -} + /* The logger produces debugging output. */ + logger = camel_imapx_logger_new (is->priv->tagprefix); + input_stream = g_converter_input_stream_new ( + input_stream, logger); + g_clear_object (&logger); -static void -imapx_command_cancelled (GCancellable *cancellable, - CamelIMAPXCommand *ic) -{ - /* Unblock imapx_command_run_sync() immediately. - * - * If camel_imapx_command_done() is called sometime later, - * the GCond will broadcast but no one will be listening. */ + /* Buffer the input stream for parsing. */ + temp_stream = camel_imapx_input_stream_new (input_stream); + camel_binding_bind_property ( + temp_stream, "close-base-stream", + input_stream, "close-base-stream", + G_BINDING_SYNC_CREATE); + g_object_unref (input_stream); + input_stream = temp_stream; + } - camel_imapx_command_done (ic); -} + if (output_stream != NULL) { + /* The logger produces debugging output. */ + logger = camel_imapx_logger_new (is->priv->tagprefix); + output_stream = g_converter_output_stream_new ( + output_stream, logger); + g_clear_object (&logger); + } -/* The caller should free the command as well */ -static gboolean -imapx_command_run_sync (CamelIMAPXServer *is, - CamelIMAPXCommand *ic, - GCancellable *cancellable, - GError **error) -{ - guint cancel_id = 0; - gboolean success = TRUE; + g_mutex_lock (&is->priv->stream_lock); - /* FIXME The only caller of this function currently does not set - * a "complete" callback function, so we can get away with - * referencing the command here and dropping the reference - * in imapx_command_complete(). The queueing/dequeueing - * of these things is too complex for my little mind, so - * we may have to revisit the reference counting if this - * function gets another caller. */ - - g_warn_if_fail (ic->complete == NULL); - ic->complete = imapx_command_complete; - - if (G_IS_CANCELLABLE (cancellable)) - cancel_id = g_cancellable_connect ( - cancellable, - G_CALLBACK (imapx_command_cancelled), - camel_imapx_command_ref (ic), - (GDestroyNotify) camel_imapx_command_unref); - - /* Unref'ed in imapx_command_complete(). */ - camel_imapx_command_ref (ic); + /* Don't close the base streams so STARTTLS works correctly. */ - imapx_command_queue (is, ic); + if (G_IS_FILTER_INPUT_STREAM (is->priv->input_stream)) { + g_filter_input_stream_set_close_base_stream ( + G_FILTER_INPUT_STREAM (is->priv->input_stream), + FALSE); + } - camel_imapx_command_wait (ic); + if (G_IS_FILTER_OUTPUT_STREAM (is->priv->output_stream)) { + g_filter_output_stream_set_close_base_stream ( + G_FILTER_OUTPUT_STREAM (is->priv->output_stream), + FALSE); + } - if (cancel_id > 0) - g_cancellable_disconnect (cancellable, cancel_id); + g_clear_object (&is->priv->input_stream); + is->priv->input_stream = input_stream; - if (camel_imapx_command_set_error_if_failed (ic, error)) - return FALSE; + g_clear_object (&is->priv->output_stream); + is->priv->output_stream = output_stream; - return success; + g_mutex_unlock (&is->priv->stream_lock); } -static gboolean -imapx_ensure_mailbox_permanentflags (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - GCancellable *cancellable, - GError **error) +#ifdef G_OS_UNIX +static void +imapx_server_child_process_setup (gpointer user_data) { - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); +#ifdef TIOCNOTTY + gint fd; +#endif - if (camel_imapx_mailbox_get_permanentflags (mailbox) != ~0) - return TRUE; + setsid (); - /* This will also invoke SELECT command, which updates PERMANENTFLAGS - for the mailbox. There might be possible to use EXAMINE for it, - but some servers do not return the same set of flags as with SELECT. - It's a little hack on top of the IMAPx implementation. */ - return camel_imapx_server_noop (is, mailbox, cancellable, error); +#ifdef TIOCNOTTY + /* Detach from the controlling tty if we have one. Otherwise, + * SSH might do something stupid like trying to use it instead + * of running $SSH_ASKPASS. */ + if ((fd = open ("/dev/tty", O_RDONLY)) != -1) { + ioctl (fd, TIOCNOTTY, NULL); + close (fd); + } +#endif /* TIOCNOTTY */ } +#endif /* G_OS_UNIX */ -/* ********************************************************************** */ -// IDLE support - -/*TODO handle negative cases sanely */ static gboolean -imapx_command_idle_stop (CamelIMAPXServer *is, - GError **error) -{ - GOutputStream *output_stream; - GCancellable *cancellable; - gboolean success; +connect_to_server_process (CamelIMAPXServer *is, + const gchar *cmd, + GError **error) +{ + GSubprocessLauncher *launcher; + GSubprocess *subprocess = NULL; + CamelNetworkSettings *network_settings; + CamelProvider *provider; + CamelSettings *settings; + CamelIMAPXStore *store; + CamelURL url; + gchar **argv = NULL; + gchar *buf; + gchar *cmd_copy; + gchar *full_cmd; + const gchar *password; + gchar *host; + gchar *user; + guint16 port; - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + memset (&url, 0, sizeof (CamelURL)); - output_stream = camel_imapx_server_ref_output_stream (is); - g_return_val_if_fail (output_stream != NULL, FALSE); + launcher = g_subprocess_launcher_new ( + G_SUBPROCESS_FLAGS_STDIN_PIPE | + G_SUBPROCESS_FLAGS_STDOUT_PIPE | + G_SUBPROCESS_FLAGS_STDERR_SILENCE); - cancellable = g_weak_ref_get (&is->priv->parser_cancellable); +#ifdef G_OS_UNIX + g_subprocess_launcher_set_child_setup ( + launcher, imapx_server_child_process_setup, + NULL, (GDestroyNotify) NULL); +#endif - g_mutex_lock (&is->priv->stream_lock); - success = g_output_stream_write_all ( - output_stream, "DONE\r\n", 6, NULL, cancellable, error); - g_mutex_unlock (&is->priv->stream_lock); + store = camel_imapx_server_ref_store (is); - if (!success) { - g_prefix_error (error, "Unable to issue DONE: "); - c (is->tagprefix, "Failed to issue DONE to terminate IDLE\n"); - is->state = IMAPX_SHUTDOWN; - g_main_loop_quit (is->priv->parser_main_loop); - } + password = camel_service_get_password (CAMEL_SERVICE (store)); + provider = camel_service_get_provider (CAMEL_SERVICE (store)); + settings = camel_service_ref_settings (CAMEL_SERVICE (store)); - g_clear_object (&cancellable); - g_clear_object (&output_stream); + network_settings = CAMEL_NETWORK_SETTINGS (settings); + host = camel_network_settings_dup_host (network_settings); + port = camel_network_settings_get_port (network_settings); + user = camel_network_settings_dup_user (network_settings); - return success; -} + /* Put full details in the environment, in case the connection + * program needs them */ + camel_url_set_protocol (&url, provider->protocol); + camel_url_set_host (&url, host); + camel_url_set_port (&url, port); + camel_url_set_user (&url, user); + buf = camel_url_to_string (&url, 0); -static void -imapx_command_idle_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - GError *local_error = NULL; + g_subprocess_launcher_setenv (launcher, "URL", buf, TRUE); + g_subprocess_launcher_setenv (launcher, "URLHOST", host, TRUE); - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); + if (port > 0) { + gchar *port_string; - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error performing IDLE")); - camel_imapx_job_take_error (job, local_error); + port_string = g_strdup_printf ("%u", port); + g_subprocess_launcher_setenv ( + launcher, "URLPORT", port_string, TRUE); + g_free (port_string); } - g_rec_mutex_lock (&is->priv->idle_lock); - is->priv->idle_state = IMAPX_IDLE_OFF; - g_rec_mutex_unlock (&is->priv->idle_lock); + if (user != NULL) { + g_subprocess_launcher_setenv ( + launcher, "URLPORT", user, TRUE); + } - imapx_unregister_job (is, job); -} + if (password != NULL) { + g_subprocess_launcher_setenv ( + launcher, "URLPASSWD", password, TRUE); + } -static gboolean -imapx_job_idle_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXCommand *ic; - CamelIMAPXCommandPart *cp; - CamelIMAPXMailbox *mailbox; - gboolean success = TRUE; + g_free (buf); - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_val_if_fail (mailbox != NULL, FALSE); + g_object_unref (settings); + g_object_unref (store); - ic = camel_imapx_command_new ( - is, "IDLE", mailbox, "IDLE"); - camel_imapx_command_set_job (ic, job); - ic->pri = job->pri; - ic->complete = imapx_command_idle_done; + /* Now do %h, %u, etc. substitution in cmd */ + buf = cmd_copy = g_strdup (cmd); - camel_imapx_command_close (ic); - cp = g_queue_peek_head (&ic->parts); - cp->type |= CAMEL_IMAPX_COMMAND_CONTINUATION; + full_cmd = g_strdup (""); - QUEUE_LOCK (is); - g_rec_mutex_lock (&is->priv->idle_lock); - /* Don't issue it if the idle was cancelled already */ - if (is->priv->idle_state == IMAPX_IDLE_PENDING) { - is->priv->idle_state = IMAPX_IDLE_ISSUED; + for (;;) { + gchar *pc; + gchar *tmp; + const gchar *var; + gint len; - if (camel_imapx_command_queue_is_empty (is->active)) { - imapx_command_start (is, ic); - } else { - c (is->tagprefix, "finally cancelling IDLE, other command was quicker\n"); - is->priv->idle_state = IMAPX_IDLE_OFF; - imapx_unregister_job (is, job); + pc = strchr (buf, '%'); + ignore: + if (!pc) { + tmp = g_strdup_printf ("%s%s", full_cmd, buf); + g_free (full_cmd); + full_cmd = tmp; + break; } - } else { - imapx_unregister_job (is, job); - } - g_rec_mutex_unlock (&is->priv->idle_lock); - QUEUE_UNLOCK (is); - camel_imapx_command_unref (ic); + len = pc - buf; - g_object_unref (mailbox); + var = NULL; - return success; -} + switch (pc[1]) { + case 'h': + var = host; + break; + case 'u': + var = user; + break; + } + if (!var) { + /* If there wasn't a valid %-code, with an actual + * variable to insert, pretend we didn't see the % */ + pc = strchr (pc + 1, '%'); + goto ignore; + } + tmp = g_strdup_printf ("%s%.*s%s", full_cmd, len, buf, var); + g_free (full_cmd); + full_cmd = tmp; + buf = pc + 2; + } -static gboolean -camel_imapx_server_idle (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXJob *job; - gint previous_connection_timeout; - gboolean success; + g_free (cmd_copy); - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_IDLE; - job->start = imapx_job_idle_start; + g_free (host); + g_free (user); - camel_imapx_job_set_mailbox (job, mailbox); + if (g_shell_parse_argv (full_cmd, NULL, &argv, error)) { + subprocess = g_subprocess_launcher_spawnv ( + launcher, (const gchar * const *) argv, error); + g_strfreev (argv); + } - previous_connection_timeout = imapx_server_set_connection_timeout (is->priv->connection, 0); + g_free (full_cmd); + g_object_unref (launcher); - success = imapx_submit_job (is, job, error); + if (subprocess != NULL) { + GInputStream *input_stream; + GOutputStream *output_stream; - if (previous_connection_timeout >= 0) - imapx_server_set_connection_timeout (is->priv->connection, previous_connection_timeout); + g_mutex_lock (&is->priv->stream_lock); + g_warn_if_fail (is->priv->subprocess == NULL); + is->priv->subprocess = g_object_ref (subprocess); + g_mutex_unlock (&is->priv->stream_lock); - camel_imapx_job_unref (job); + input_stream = g_subprocess_get_stdout_pipe (subprocess); + output_stream = g_subprocess_get_stdin_pipe (subprocess); - return success; -} + imapx_server_set_streams (is, input_stream, output_stream); -static gboolean -imapx_job_fetch_new_messages_matches (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox, - const gchar *uid) -{ - return camel_imapx_job_has_mailbox (job, mailbox); + g_object_unref (subprocess); + } + + return TRUE; } static gboolean -imapx_server_fetch_new_messages (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - gboolean async, - gboolean update_unseen, - GCancellable *cancellable, - GError **error) +imapx_connect_to_server (CamelIMAPXServer *is, + GCancellable *cancellable, + GError **error) { - CamelIMAPXJob *job; - RefreshInfoData *data; - gboolean success; + CamelNetworkSettings *network_settings; + CamelNetworkSecurityMethod method; + CamelIMAPXStore *store; + CamelSettings *settings; + GIOStream *connection = NULL; + GIOStream *tls_stream; + GSocket *socket; + guint len; + guchar *token; + gint tok; + CamelIMAPXCommand *ic; + gchar *shell_command = NULL; + gboolean use_shell_command; + gboolean success = TRUE; + gchar *host; - data = g_slice_new0 (RefreshInfoData); - data->changes = camel_folder_change_info_new (); - data->update_unseen = update_unseen; + store = camel_imapx_server_ref_store (is); - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_FETCH_NEW_MESSAGES; - job->start = imapx_job_fetch_new_messages_start; - job->matches = imapx_job_fetch_new_messages_matches; - job->noreply = async; + settings = camel_service_ref_settings (CAMEL_SERVICE (store)); - camel_imapx_job_set_mailbox (job, mailbox); + network_settings = CAMEL_NETWORK_SETTINGS (settings); + host = camel_network_settings_dup_host (network_settings); + method = camel_network_settings_get_security_method (network_settings); - camel_imapx_job_set_data ( - job, data, (GDestroyNotify) refresh_info_data_free); + use_shell_command = camel_imapx_settings_get_use_shell_command ( + CAMEL_IMAPX_SETTINGS (settings)); - success = imapx_submit_job (is, job, error); + if (use_shell_command) + shell_command = camel_imapx_settings_dup_shell_command ( + CAMEL_IMAPX_SETTINGS (settings)); - camel_imapx_job_unref (job); + g_object_unref (settings); - return success; -} + if (shell_command != NULL) { + success = connect_to_server_process (is, shell_command, error); -static gboolean -imapx_call_idle (gpointer data) -{ - CamelFolder *folder; - CamelIMAPXServer *is; - CamelIMAPXMailbox *mailbox; - GCancellable *cancellable; - GError *local_error = NULL; + g_free (shell_command); - is = g_weak_ref_get (data); + if (success) + goto connected; + else + goto exit; + } - if (is == NULL) - goto exit; + connection = camel_network_service_connect_sync ( + CAMEL_NETWORK_SERVICE (store), cancellable, error); - /* XXX Rename to 'pending_lock'? */ - g_rec_mutex_lock (&is->priv->idle_lock); - g_source_unref (is->priv->idle_pending); - is->priv->idle_pending = NULL; + if (connection != NULL) { + GInputStream *input_stream; + GOutputStream *output_stream; + GError *local_error = NULL; - if (is->priv->idle_state != IMAPX_IDLE_PENDING) { - g_rec_mutex_unlock (&is->priv->idle_lock); - goto exit; - } + /* Disable the Nagle algorithm with TCP_NODELAY, since IMAP + * commands should be issued immediately even we've not yet + * received a response to a previous command. */ + socket = g_socket_connection_get_socket ( + G_SOCKET_CONNECTION (connection)); + g_socket_set_option ( + socket, IPPROTO_TCP, TCP_NODELAY, 1, &local_error); + if (local_error != NULL) { + /* Failure to set the socket option is non-fatal. */ + g_warning ("%s: %s", G_STRFUNC, local_error->message); + g_clear_error (&local_error); + } - g_rec_mutex_unlock (&is->priv->idle_lock); + g_mutex_lock (&is->priv->stream_lock); + g_warn_if_fail (is->priv->connection == NULL); + is->priv->connection = g_object_ref (connection); + g_mutex_unlock (&is->priv->stream_lock); - g_mutex_lock (&is->priv->select_lock); - mailbox = g_weak_ref_get (&is->priv->select_mailbox); - g_mutex_unlock (&is->priv->select_lock); + input_stream = g_io_stream_get_input_stream (connection); + output_stream = g_io_stream_get_output_stream (connection); - if (mailbox == NULL) - goto exit; + imapx_server_set_streams (is, input_stream, output_stream); - folder = imapx_server_ref_folder (is, mailbox); - if (folder == NULL) + /* Hang on to the connection reference in case we need to + * issue STARTTLS below. */ + } else { + success = FALSE; goto exit; + } - cancellable = g_weak_ref_get (&is->priv->parser_cancellable); +connected: + while (1) { + GInputStream *input_stream; - /* We block here until the IDLE command completes. */ - camel_imapx_server_idle (is, mailbox, cancellable, &local_error); - - if (local_error == NULL) { - gboolean have_new_messages; - gboolean fetch_new_messages; + input_stream = camel_imapx_server_ref_input_stream (is); - have_new_messages = - camel_imapx_mailbox_get_messages (mailbox) > - camel_folder_summary_count (folder->summary); + token = NULL; + tok = camel_imapx_input_stream_token ( + CAMEL_IMAPX_INPUT_STREAM (input_stream), + &token, &len, cancellable, error); - fetch_new_messages = - have_new_messages && - imapx_is_command_queue_empty (is); + if (tok < 0) { + success = FALSE; - if (fetch_new_messages) - imapx_server_fetch_new_messages ( - is, mailbox, TRUE, TRUE, - cancellable, &local_error); - } + } else if (tok == '*') { + success = imapx_untagged ( + is, input_stream, cancellable, error); - /* XXX Need a better way to propagate IDLE errors. */ - if (local_error != NULL) { - if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && - is->state != IMAPX_SHUTDOWN) - g_warning ("%s: %s", G_STRFUNC, local_error->message); - g_clear_error (&local_error); - } + if (success) { + g_object_unref (input_stream); + break; + } - g_clear_object (&folder); - g_clear_object (&cancellable); + } else { + camel_imapx_input_stream_ungettoken ( + CAMEL_IMAPX_INPUT_STREAM (input_stream), + tok, token, len); -exit: - g_clear_object (&is); + success = camel_imapx_input_stream_text ( + CAMEL_IMAPX_INPUT_STREAM (input_stream), + &token, cancellable, error); - return G_SOURCE_REMOVE; -} + g_free (token); + } -static gpointer -imapx_idle_thread (gpointer data) -{ - CamelIMAPXServer *is = (CamelIMAPXServer *) data; - GSource *pending; + g_object_unref (input_stream); - g_main_context_push_thread_default (is->priv->idle_main_context); + if (!success) + goto exit; + } - /* Schedule the first IDLE command after a brief "dwell" - * delay so any other pending commands get priority. - * - * XXX Don't fully understand why this is necessary, but - * for now just adapting old code and hoping to avoid - * regressions. - */ + g_mutex_lock (&is->priv->stream_lock); - g_rec_mutex_lock (&is->priv->idle_lock); + if (!is->priv->cinfo) { + g_mutex_unlock (&is->priv->stream_lock); - g_warn_if_fail (is->priv->idle_pending == NULL); - pending = g_timeout_source_new_seconds (IMAPX_IDLE_DWELL_TIME); - g_source_set_name (pending, "imapx_call_idle"); - g_source_set_callback ( - pending, imapx_call_idle, - imapx_weak_ref_new (is), - (GDestroyNotify) imapx_weak_ref_free); - g_source_attach (pending, is->priv->idle_main_context); - is->priv->idle_pending = g_source_ref (pending); - g_source_unref (pending); + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_CAPABILITY, "CAPABILITY"); - g_rec_mutex_unlock (&is->priv->idle_lock); + success = camel_imapx_server_process_command_sync (is, ic, _("Failed to get capabilities"), cancellable, error); - g_main_loop_run (is->priv->idle_main_loop); + camel_imapx_command_unref (ic); - g_main_context_pop_thread_default (is->priv->idle_main_context); + if (!success) + goto exit; + } else { + g_mutex_unlock (&is->priv->stream_lock); + } - g_object_unref (is); + if (method == CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT) { - return NULL; -} + g_mutex_lock (&is->priv->stream_lock); -static CamelIMAPXIdleStopResult -imapx_stop_idle (CamelIMAPXServer *is, - GError **error) -{ - CamelIMAPXIdleStopResult result = IMAPX_IDLE_STOP_NOOP; - time_t now; + if (CAMEL_IMAPX_LACK_CAPABILITY (is->priv->cinfo, STARTTLS)) { + g_mutex_unlock (&is->priv->stream_lock); + g_set_error ( + error, CAMEL_ERROR, + CAMEL_ERROR_GENERIC, + _("Failed to connect to IMAP server %s in secure mode: %s"), + host, _("STARTTLS not supported")); + success = FALSE; + goto exit; + } else { + g_mutex_unlock (&is->priv->stream_lock); + } - time (&now); + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_STARTTLS, "STARTTLS"); - g_rec_mutex_lock (&is->priv->idle_lock); + success = camel_imapx_server_process_command_sync (is, ic, _("Failed to issue STARTTLS"), cancellable, error); - switch (is->priv->idle_state) { - case IMAPX_IDLE_ISSUED: - is->priv->idle_state = IMAPX_IDLE_CANCEL; - result = IMAPX_IDLE_STOP_SUCCESS; - break; + if (success) { + g_mutex_lock (&is->priv->stream_lock); - case IMAPX_IDLE_CANCEL: - result = IMAPX_IDLE_STOP_SUCCESS; - break; + /* See if we got new capabilities + * in the STARTTLS response. */ + imapx_free_capability (is->priv->cinfo); + is->priv->cinfo = NULL; + if (ic->status->condition == IMAPX_CAPABILITY) { + is->priv->cinfo = ic->status->u.cinfo; + ic->status->u.cinfo = NULL; + c (is->priv->tagprefix, "got capability flags %08x\n", is->priv->cinfo ? is->priv->cinfo->capa : 0xFFFFFFFF); + imapx_server_stash_command_arguments (is); + } - case IMAPX_IDLE_WAIT_DONE: - result = IMAPX_IDLE_STOP_WAIT_DONE; - break; + g_mutex_unlock (&is->priv->stream_lock); + } - case IMAPX_IDLE_STARTED: - if (imapx_command_idle_stop (is, error)) { - result = IMAPX_IDLE_STOP_WAIT_DONE; - is->priv->idle_state = IMAPX_IDLE_WAIT_DONE; - } else { - result = IMAPX_IDLE_STOP_ERROR; - is->priv->idle_state = IMAPX_IDLE_OFF; - goto exit; - } - break; + camel_imapx_command_unref (ic); - case IMAPX_IDLE_PENDING: - is->priv->idle_state = IMAPX_IDLE_OFF; - break; + if (!success) + goto exit; - case IMAPX_IDLE_OFF: - break; - } + tls_stream = camel_network_service_starttls ( + CAMEL_NETWORK_SERVICE (store), connection, error); -exit: - g_rec_mutex_unlock (&is->priv->idle_lock); + if (tls_stream != NULL) { + GInputStream *input_stream; + GOutputStream *output_stream; - return result; -} + g_mutex_lock (&is->priv->stream_lock); + g_object_unref (is->priv->connection); + is->priv->connection = g_object_ref (tls_stream); + g_mutex_unlock (&is->priv->stream_lock); -static void -imapx_start_idle (CamelIMAPXServer *is) -{ - if (camel_application_is_exiting) - return; + input_stream = + g_io_stream_get_input_stream (tls_stream); + output_stream = + g_io_stream_get_output_stream (tls_stream); - g_rec_mutex_lock (&is->priv->idle_lock); + imapx_server_set_streams ( + is, input_stream, output_stream); - if (is->priv->idle_state != IMAPX_IDLE_OFF) { - g_warn_if_fail (is->priv->idle_state == IMAPX_IDLE_OFF); - g_rec_mutex_unlock (&is->priv->idle_lock); - return; - } + g_object_unref (tls_stream); + } else { + g_prefix_error ( + error, + _("Failed to connect to IMAP server %s in secure mode: "), + host); + success = FALSE; + goto exit; + } - is->priv->idle_state = IMAPX_IDLE_PENDING; + /* Get new capabilities if they weren't already given */ + g_mutex_lock (&is->priv->stream_lock); + if (is->priv->cinfo == NULL) { + g_mutex_unlock (&is->priv->stream_lock); + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_CAPABILITY, "CAPABILITY"); + success = camel_imapx_server_process_command_sync (is, ic, _("Failed to get capabilities"), cancellable, error); + camel_imapx_command_unref (ic); - if (is->priv->idle_thread == NULL) { - is->priv->idle_thread = g_thread_new ( - NULL, imapx_idle_thread, g_object_ref (is)); - - } else if (is->priv->idle_pending == NULL) { - GSource *pending; - - pending = g_idle_source_new (); - g_source_set_name (pending, "imapx_call_idle"); - g_source_set_callback ( - pending, imapx_call_idle, - imapx_weak_ref_new (is), - (GDestroyNotify) imapx_weak_ref_free); - g_source_attach (pending, is->priv->idle_main_context); - is->priv->idle_pending = g_source_ref (pending); - g_source_unref (pending); + if (!success) + goto exit; + } else { + g_mutex_unlock (&is->priv->stream_lock); + } } - g_rec_mutex_unlock (&is->priv->idle_lock); -} +exit: + if (!success) { + g_mutex_lock (&is->priv->stream_lock); -static gboolean -imapx_in_idle (CamelIMAPXServer *is) -{ - gboolean in_idle = FALSE; + g_clear_object (&is->priv->input_stream); + g_clear_object (&is->priv->output_stream); + g_clear_object (&is->priv->connection); + g_clear_object (&is->priv->subprocess); + + if (is->priv->cinfo != NULL) { + imapx_free_capability (is->priv->cinfo); + is->priv->cinfo = NULL; + } - g_rec_mutex_lock (&is->priv->idle_lock); + g_mutex_unlock (&is->priv->stream_lock); + } - if (is->priv->idle_thread != NULL) - in_idle = (is->priv->idle_state > IMAPX_IDLE_OFF); + g_free (host); - g_rec_mutex_unlock (&is->priv->idle_lock); + g_clear_object (&connection); + g_clear_object (&store); - return in_idle; + return success; } -static gboolean -imapx_use_idle (CamelIMAPXServer *is) +gboolean +camel_imapx_server_is_connected (CamelIMAPXServer *imapx_server) { - gboolean use_idle = FALSE; - - /* No need for IDLE if the server supports NOTIFY. */ - if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, NOTIFY)) - return FALSE; - - if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, IDLE)) { - CamelIMAPXSettings *settings; - - settings = camel_imapx_server_ref_settings (is); - use_idle = camel_imapx_settings_get_use_idle (settings); - g_object_unref (settings); - } + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), FALSE); - return use_idle; + return imapx_server->priv->state >= IMAPX_CONNECTED; } -// end IDLE -/* ********************************************************************** */ -static void -imapx_command_select_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) +CamelAuthenticationResult +camel_imapx_server_authenticate_sync (CamelIMAPXServer *is, + const gchar *mechanism, + GCancellable *cancellable, + GError **error) { - CamelIMAPXJob *job; - CamelIMAPXMailbox *select_closing; - CamelIMAPXMailbox *select_pending; - GError *local_error = NULL; - - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); - - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - CamelIMAPXCommandQueue *failed; - GQueue trash = G_QUEUE_INIT; - GList *list, *link; - gboolean noperm_error; - - c (is->tagprefix, "Select failed: %s\n", local_error ? local_error->message : "Unknown error"); + CamelNetworkSettings *network_settings; + CamelIMAPXStore *store; + CamelService *service; + CamelSettings *settings; + CamelAuthenticationResult result; + CamelIMAPXCommand *ic; + CamelSasl *sasl = NULL; + gchar *host; + gchar *user; - g_mutex_lock (&is->priv->select_lock); - select_closing = g_weak_ref_get (&is->priv->select_closing); - select_pending = g_weak_ref_get (&is->priv->select_pending); - g_weak_ref_set (&is->priv->select_mailbox, NULL); - g_weak_ref_set (&is->priv->select_closing, NULL); - g_weak_ref_set (&is->priv->select_pending, NULL); - is->state = IMAPX_INITIALISED; - g_mutex_unlock (&is->priv->select_lock); + g_return_val_if_fail ( + CAMEL_IS_IMAPX_SERVER (is), + CAMEL_AUTHENTICATION_ERROR); - failed = camel_imapx_command_queue_new (); + store = camel_imapx_server_ref_store (is); - QUEUE_LOCK (is); + service = CAMEL_SERVICE (store); + settings = camel_service_ref_settings (service); - noperm_error = select_pending != NULL && ic->status && ic->status->result == IMAPX_NO && - (ic->status->condition == IMAPX_NOPERM || ic->status->condition == IMAPX_UNKNOWN); + network_settings = CAMEL_NETWORK_SETTINGS (settings); + host = camel_network_settings_dup_host (network_settings); + user = camel_network_settings_dup_user (network_settings); - if (select_pending != NULL) { - GList *head = camel_imapx_command_queue_peek_head_link (is->queue); - - for (link = head; link != NULL; link = g_list_next (link)) { - CamelIMAPXCommand *cw = link->data; - CamelIMAPXMailbox *cw_mailbox; - - cw_mailbox = camel_imapx_command_ref_mailbox (cw); - - if (cw_mailbox == select_pending) { - c ( - is->tagprefix, - "Cancelling command '%s'(%p) " - "for mailbox '%s'\n", - cw->name, cw, - camel_imapx_mailbox_get_name (cw_mailbox)); - g_queue_push_tail (&trash, link); - } + g_object_unref (settings); - g_clear_object (&cw_mailbox); - } - } + if (mechanism != NULL) { + g_mutex_lock (&is->priv->stream_lock); - if (noperm_error) { - /* This avoids another SELECT try on this mailbox; - the mailbox can be write-only in this case. */ - if (camel_imapx_mailbox_get_permanentflags (select_pending) == ~0) - camel_imapx_mailbox_set_permanentflags (select_pending, 0); + if (is->priv->cinfo && !g_hash_table_lookup (is->priv->cinfo->auth_types, mechanism) && ( + !g_str_equal (mechanism, "Google") || !g_hash_table_lookup (is->priv->cinfo->auth_types, "XOAUTH2"))) { + g_mutex_unlock (&is->priv->stream_lock); + g_set_error ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE, + _("IMAP server %s does not support %s " + "authentication"), host, mechanism); + result = CAMEL_AUTHENTICATION_ERROR; + goto exit; + } else { + g_mutex_unlock (&is->priv->stream_lock); } - while ((link = g_queue_pop_head (&trash)) != NULL) { - CamelIMAPXCommand *cw = link->data; - camel_imapx_command_ref (cw); - camel_imapx_command_queue_delete_link (is->queue, link); - imapx_server_command_removed (is, cw); - camel_imapx_command_queue_push_tail (failed, cw); - camel_imapx_command_unref (cw); + sasl = camel_sasl_new ("imap", mechanism, service); + if (sasl == NULL) { + g_set_error ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE, + _("No support for %s authentication"), + mechanism); + result = CAMEL_AUTHENTICATION_ERROR; + goto exit; } + } - QUEUE_UNLOCK (is); - - list = camel_imapx_command_queue_peek_head_link (failed); - - for (link = list; link != NULL; link = g_list_next (link)) { - CamelIMAPXCommand *cw = link->data; - CamelIMAPXJob *failed_job; - - failed_job = camel_imapx_command_get_job (cw); - - if (!CAMEL_IS_IMAPX_JOB (failed_job)) { - g_warn_if_reached (); - continue; - } + if (sasl != NULL) { + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_AUTHENTICATE, "AUTHENTICATE %A", sasl); + } else { + const gchar *password; - if (!noperm_error) - camel_imapx_job_cancel (failed_job); + password = camel_service_get_password (service); - if (ic->status) - cw->status = imapx_copy_status (ic->status); + if (user == NULL) { + g_set_error_literal ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE, + _("Cannot authenticate without a username")); + result = CAMEL_AUTHENTICATION_ERROR; + goto exit; + } - cw->complete (is, cw); + if (password == NULL) { + g_set_error_literal ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE, + _("Authentication password not available")); + result = CAMEL_AUTHENTICATION_ERROR; + goto exit; } - camel_imapx_command_queue_free (failed); + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LOGIN, "LOGIN %s %s", user, password); + } - camel_imapx_job_take_error (job, local_error); - imapx_unregister_job (is, job); + if (!camel_imapx_server_process_command_sync (is, ic, _("Failed to authenticate"), cancellable, error) && ( + !ic->status || ic->status->result != IMAPX_NO)) + result = CAMEL_AUTHENTICATION_ERROR; + else if (ic->status->result == IMAPX_OK) + result = CAMEL_AUTHENTICATION_ACCEPTED; + else if (ic->status->result == IMAPX_NO) { + g_clear_error (error); - } else { - CamelFolder *folder; - CamelIMAPXSummary *imapx_summary; - guint32 uidnext; - - c (is->tagprefix, "Select ok!\n"); - - g_mutex_lock (&is->priv->select_lock); - select_closing = g_weak_ref_get (&is->priv->select_closing); - select_pending = g_weak_ref_get (&is->priv->select_pending); - g_weak_ref_set (&is->priv->select_mailbox, select_pending); - g_weak_ref_set (&is->priv->select_closing, NULL); - g_weak_ref_set (&is->priv->select_pending, NULL); - is->state = IMAPX_SELECTED; - g_mutex_unlock (&is->priv->select_lock); + if (camel_imapx_store_is_connecting_concurrent_connection (store)) { + /* At least one connection succeeded, probably max connection limit + set on the server had been reached, thus use special error code + for it, to instruct the connection manager to decrease the limit + and use already created connection. */ + g_set_error_literal ( + error, CAMEL_IMAPX_SERVER_ERROR, + CAMEL_IMAPX_SERVER_ERROR_CONCURRENT_CONNECT_FAILED, + ic->status->text ? ic->status->text : _("Unknown error")); + result = CAMEL_AUTHENTICATION_ERROR; + } else if (sasl) { + CamelSaslClass *sasl_class; - /* We should have a strong reference - * on the newly-selected CamelFolder. */ - folder = imapx_server_ref_folder (is, select_pending); - g_return_if_fail (folder != NULL); - - uidnext = camel_imapx_mailbox_get_uidnext (select_pending); - imapx_summary = CAMEL_IMAPX_SUMMARY (folder->summary); - - if (imapx_summary->uidnext < uidnext) { - /* We don't want to fetch new messages if the command we selected this - * folder for is *already* fetching all messages (i.e. scan_changes). - * Bug #667725. */ - CamelIMAPXJob *job = imapx_server_ref_job ( - is, select_pending, - IMAPX_JOB_REFRESH_INFO, NULL); - if (job) { - camel_imapx_job_unref (job); - c ( - is->tagprefix, - "Will not fetch_new_messages when already refreshing information\n"); + sasl_class = CAMEL_SASL_GET_CLASS (sasl); + if (sasl_class && sasl_class->auth_type && !sasl_class->auth_type->need_password) { + g_set_error_literal ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE, + ic->status->text ? ic->status->text : _("Unknown error")); + result = CAMEL_AUTHENTICATION_ERROR; } else { - imapx_server_fetch_new_messages (is, select_pending, TRUE, TRUE, NULL, NULL); + result = CAMEL_AUTHENTICATION_REJECTED; } + } else { + result = CAMEL_AUTHENTICATION_REJECTED; } + } else { + g_set_error_literal ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE, + ic->status->text ? ic->status->text : _("Unknown error")); + result = CAMEL_AUTHENTICATION_ERROR; + } -#if 0 /* see comment for disabled bits in imapx_job_refresh_info_start() */ - /* This should trigger a new messages scan */ - if (is->exists != folder->summary->root_view->total_count) - g_warning ( - "exists is %d our summary is %d and summary exists is %d\n", is->exists, - folder->summary->root_view->total_count, - ((CamelIMAPXSummary *) folder->summary)->exists); -#endif + /* Forget old capabilities after login. */ + if (result == CAMEL_AUTHENTICATION_ACCEPTED) { + g_mutex_lock (&is->priv->stream_lock); - g_clear_object (&folder); + if (is->priv->cinfo) { + imapx_free_capability (is->priv->cinfo); + is->priv->cinfo = NULL; + } + + if (ic->status->condition == IMAPX_CAPABILITY) { + is->priv->cinfo = ic->status->u.cinfo; + ic->status->u.cinfo = NULL; + c (is->priv->tagprefix, "got capability flags %08x\n", is->priv->cinfo ? is->priv->cinfo->capa : 0xFFFFFFFF); + imapx_server_stash_command_arguments (is); + } + + g_mutex_unlock (&is->priv->stream_lock); } - if (select_closing != NULL) - g_signal_emit (is, signals[MAILBOX_CLOSED], 0, select_closing); + camel_imapx_command_unref (ic); + + if (sasl != NULL) + g_object_unref (sasl); + +exit: + g_free (host); + g_free (user); + + g_object_unref (store); - g_clear_object (&select_closing); - g_clear_object (&select_pending); + return result; } -/* Should have a queue lock. TODO Change the way select is written */ -static void -imapx_maybe_select (CamelIMAPXServer *is, - CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox) +static gboolean +imapx_reconnect (CamelIMAPXServer *is, + GCancellable *cancellable, + GError **error) { CamelIMAPXCommand *ic; - CamelIMAPXMailbox *select_mailbox; - CamelIMAPXMailbox *select_pending; - gboolean nothing_to_do = FALSE; - - /* Select is complicated by the fact we may have commands - * active on the server for a different selection. - * - * So this waits for any commands to complete, selects the - * new mailbox, and halts the queuing of any new commands. - * It is assumed whomever called us is about to issue a - * high-priority command anyway. */ + CamelService *service; + CamelSession *session; + CamelIMAPXStore *store; + CamelSettings *settings; + gchar *mechanism; + gboolean use_qresync; + gboolean use_idle; + gboolean success = FALSE; - g_mutex_lock (&is->priv->select_lock); + store = camel_imapx_server_ref_store (is); - select_mailbox = g_weak_ref_get (&is->priv->select_mailbox); - select_pending = g_weak_ref_get (&is->priv->select_pending); + service = CAMEL_SERVICE (store); + session = camel_service_ref_session (service); + if (!session) { + g_set_error_literal ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_UNAVAILABLE, + _("You must be working online to complete this operation")); + g_object_unref (store); + return FALSE; + } - if (select_pending != NULL) { - nothing_to_do = TRUE; - } else if (select_mailbox == mailbox) { - nothing_to_do = TRUE; - } else if (!camel_imapx_command_queue_is_empty (is->active)) { - nothing_to_do = TRUE; - } else { - g_weak_ref_set (&is->priv->select_pending, mailbox); + settings = camel_service_ref_settings (service); - if (select_mailbox != NULL) { - g_weak_ref_set (&is->priv->select_mailbox, NULL); - } else { - /* If no mailbox was selected, we won't get a - * [CLOSED] status so just point select_mailbox - * at the newly-selected mailbox immediately. */ - g_weak_ref_set (&is->priv->select_mailbox, mailbox); - } + mechanism = camel_network_settings_dup_auth_mechanism ( + CAMEL_NETWORK_SETTINGS (settings)); - g_weak_ref_set (&is->priv->select_closing, select_mailbox); + use_qresync = camel_imapx_settings_get_use_qresync (CAMEL_IMAPX_SETTINGS (settings)); + use_idle = camel_imapx_settings_get_use_idle (CAMEL_IMAPX_SETTINGS (settings)); - /* Hrm, what about reconnecting? */ - is->state = IMAPX_INITIALISED; - } + g_object_unref (settings); - g_clear_object (&select_mailbox); - g_clear_object (&select_pending); + if (!imapx_connect_to_server (is, cancellable, error)) + goto exception; - g_mutex_unlock (&is->priv->select_lock); + if (is->priv->state == IMAPX_AUTHENTICATED) + goto preauthed; - if (nothing_to_do) - return; + if (!camel_session_authenticate_sync ( + session, service, mechanism, cancellable, error)) + goto exception; - g_signal_emit (is, signals[MAILBOX_SELECT], 0, mailbox); + /* After login we re-capa unless the server already told us. */ + g_mutex_lock (&is->priv->stream_lock); + if (is->priv->cinfo == NULL) { + GError *local_error = NULL; - ic = camel_imapx_command_new ( - is, "SELECT", NULL, "SELECT %M", mailbox); + g_mutex_unlock (&is->priv->stream_lock); - if (is->use_qresync) { - CamelFolder *folder; + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_CAPABILITY, "CAPABILITY"); + camel_imapx_server_process_command_sync (is, ic, _("Failed to get capabilities"), cancellable, &local_error); + camel_imapx_command_unref (ic); - folder = imapx_server_ref_folder (is, mailbox); - camel_imapx_command_add_qresync_parameter (ic, folder); - g_clear_object (&folder); + if (local_error != NULL) { + g_propagate_error (error, local_error); + goto exception; + } + } else { + g_mutex_unlock (&is->priv->stream_lock); } - ic->complete = imapx_command_select_done; - camel_imapx_command_set_job (ic, job); - - imapx_command_start (is, ic); + is->priv->state = IMAPX_AUTHENTICATED; - camel_imapx_command_unref (ic); -} +preauthed: + /* Fetch namespaces (if supported). */ + g_mutex_lock (&is->priv->stream_lock); + if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, NAMESPACE)) { + GError *local_error = NULL; -static void -imapx_server_set_streams (CamelIMAPXServer *is, - GInputStream *input_stream, - GOutputStream *output_stream) -{ - GConverter *logger; + g_mutex_unlock (&is->priv->stream_lock); - if (input_stream != NULL) { - GInputStream *temp_stream; + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NAMESPACE, "NAMESPACE"); + camel_imapx_server_process_command_sync (is, ic, _("Failed to issue NAMESPACE"), cancellable, &local_error); + camel_imapx_command_unref (ic); - /* The logger produces debugging output. */ - logger = camel_imapx_logger_new (is->tagprefix); - input_stream = g_converter_input_stream_new ( - input_stream, logger); - g_clear_object (&logger); + if (local_error != NULL) { + g_propagate_error (error, local_error); + goto exception; + } - /* Buffer the input stream for parsing. */ - temp_stream = camel_imapx_input_stream_new (input_stream); - g_object_bind_property ( - temp_stream, "close-base-stream", - input_stream, "close-base-stream", - G_BINDING_SYNC_CREATE); - g_object_unref (input_stream); - input_stream = temp_stream; + g_mutex_lock (&is->priv->stream_lock); } - if (output_stream != NULL) { - /* The logger produces debugging output. */ - logger = camel_imapx_logger_new (is->tagprefix); - output_stream = g_converter_output_stream_new ( - output_stream, logger); - g_clear_object (&logger); - } + /* Enable quick mailbox resynchronization (if supported). */ + if (use_qresync && CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, QRESYNC)) { + GError *local_error = NULL; - g_mutex_lock (&is->priv->stream_lock); + g_mutex_unlock (&is->priv->stream_lock); - /* Don't close the base streams so STARTTLS works correctly. */ + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_ENABLE, "ENABLE CONDSTORE QRESYNC"); + camel_imapx_server_process_command_sync (is, ic, _("Failed to enable QResync"), cancellable, &local_error); + camel_imapx_command_unref (ic); - if (G_IS_FILTER_INPUT_STREAM (is->priv->input_stream)) { - g_filter_input_stream_set_close_base_stream ( - G_FILTER_INPUT_STREAM (is->priv->input_stream), - FALSE); - } + if (local_error != NULL) { + g_propagate_error (error, local_error); + goto exception; + } - if (G_IS_FILTER_OUTPUT_STREAM (is->priv->output_stream)) { - g_filter_output_stream_set_close_base_stream ( - G_FILTER_OUTPUT_STREAM (is->priv->output_stream), - FALSE); - } + g_mutex_lock (&is->priv->stream_lock); - g_clear_object (&is->priv->input_stream); - is->priv->input_stream = input_stream; + is->priv->use_qresync = TRUE; + } else { + is->priv->use_qresync = FALSE; + } - g_clear_object (&is->priv->output_stream); - is->priv->output_stream = output_stream; + /* Set NOTIFY options after enabling QRESYNC (if supported). */ + if (use_idle && CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, NOTIFY)) { + GError *local_error = NULL; - g_mutex_unlock (&is->priv->stream_lock); -} + g_mutex_unlock (&is->priv->stream_lock); -#if GLIB_CHECK_VERSION(2,39,0) -#ifdef G_OS_UNIX -static void -imapx_server_child_process_setup (gpointer user_data) -{ -#ifdef TIOCNOTTY - gint fd; -#endif /* TIOCNOTTY */ + /* XXX The list of FETCH attributes is negotiable. */ + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NOTIFY, "NOTIFY SET " + "(selected " + "(MessageNew (UID RFC822.SIZE RFC822.HEADER FLAGS)" + " MessageExpunge" + " FlagChange)) " + "(personal " + "(MessageNew" + " MessageExpunge" + " MailboxName" + " SubscriptionChange))"); + camel_imapx_server_process_command_sync (is, ic, _("Failed to issue NOTIFY"), cancellable, &local_error); + camel_imapx_command_unref (ic); - setsid (); + if (local_error != NULL) { + g_propagate_error (error, local_error); + goto exception; + } -#ifdef TIOCNOTTY - /* Detach from the controlling tty if we have one. Otherwise, - * SSH might do something stupid like trying to use it instead - * of running $SSH_ASKPASS. */ - if ((fd = open ("/dev/tty", O_RDONLY)) != -1) { - ioctl (fd, TIOCNOTTY, NULL); - close (fd); + g_mutex_lock (&is->priv->stream_lock); } -#endif /* TIOCNOTTY */ -} -#endif /* G_OS_UNIX */ -#endif -static gboolean -connect_to_server_process (CamelIMAPXServer *is, - const gchar *cmd, - GError **error) -{ -#if GLIB_CHECK_VERSION(2,39,0) - GSubprocessLauncher *launcher; - GSubprocess *subprocess = NULL; - CamelNetworkSettings *network_settings; - CamelProvider *provider; - CamelSettings *settings; - CamelIMAPXStore *store; - CamelURL url; - gchar **argv = NULL; - gchar *buf; - gchar *cmd_copy; - gchar *full_cmd; - const gchar *password; - gchar *host; - gchar *user; - guint16 port; + g_mutex_unlock (&is->priv->stream_lock); - memset (&url, 0, sizeof (CamelURL)); + is->priv->state = IMAPX_INITIALISED; - launcher = g_subprocess_launcher_new ( - G_SUBPROCESS_FLAGS_STDIN_PIPE | - G_SUBPROCESS_FLAGS_STDOUT_PIPE | - G_SUBPROCESS_FLAGS_STDERR_SILENCE); + success = TRUE; -#ifdef G_OS_UNIX - g_subprocess_launcher_set_child_setup ( - launcher, imapx_server_child_process_setup, - NULL, (GDestroyNotify) NULL); -#endif + goto exit; - store = camel_imapx_server_ref_store (is); +exception: + imapx_disconnect (is); - password = camel_service_get_password (CAMEL_SERVICE (store)); - provider = camel_service_get_provider (CAMEL_SERVICE (store)); - settings = camel_service_ref_settings (CAMEL_SERVICE (store)); +exit: + g_free (mechanism); - network_settings = CAMEL_NETWORK_SETTINGS (settings); - host = camel_network_settings_dup_host (network_settings); - port = camel_network_settings_get_port (network_settings); - user = camel_network_settings_dup_user (network_settings); + g_object_unref (session); + g_object_unref (store); - /* Put full details in the environment, in case the connection - * program needs them */ - camel_url_set_protocol (&url, provider->protocol); - camel_url_set_host (&url, host); - camel_url_set_port (&url, port); - camel_url_set_user (&url, user); - buf = camel_url_to_string (&url, 0); - - g_subprocess_launcher_setenv (launcher, "URL", buf, TRUE); - g_subprocess_launcher_setenv (launcher, "URLHOST", host, TRUE); - - if (port > 0) { - gchar *port_string; - - port_string = g_strdup_printf ("%u", port); - g_subprocess_launcher_setenv ( - launcher, "URLPORT", port_string, TRUE); - g_free (port_string); - } - - if (user != NULL) { - g_subprocess_launcher_setenv ( - launcher, "URLPORT", user, TRUE); - } - - if (password != NULL) { - g_subprocess_launcher_setenv ( - launcher, "URLPASSWD", password, TRUE); - } - - g_free (buf); - - g_object_unref (settings); - g_object_unref (store); - - /* Now do %h, %u, etc. substitution in cmd */ - buf = cmd_copy = g_strdup (cmd); - - full_cmd = g_strdup (""); - - for (;;) { - gchar *pc; - gchar *tmp; - const gchar *var; - gint len; - - pc = strchr (buf, '%'); - ignore: - if (!pc) { - tmp = g_strdup_printf ("%s%s", full_cmd, buf); - g_free (full_cmd); - full_cmd = tmp; - break; - } - - len = pc - buf; - - var = NULL; - - switch (pc[1]) { - case 'h': - var = host; - break; - case 'u': - var = user; - break; - } - if (!var) { - /* If there wasn't a valid %-code, with an actual - * variable to insert, pretend we didn't see the % */ - pc = strchr (pc + 1, '%'); - goto ignore; - } - tmp = g_strdup_printf ("%s%.*s%s", full_cmd, len, buf, var); - g_free (full_cmd); - full_cmd = tmp; - buf = pc + 2; - } - - g_free (cmd_copy); - - g_free (host); - g_free (user); - - if (g_shell_parse_argv (full_cmd, NULL, &argv, error)) { - subprocess = g_subprocess_launcher_spawnv ( - launcher, (const gchar * const *) argv, error); - g_strfreev (argv); - } - - g_free (full_cmd); - g_object_unref (launcher); - - if (subprocess != NULL) { - GInputStream *input_stream; - GOutputStream *output_stream; - - g_mutex_lock (&is->priv->stream_lock); - g_warn_if_fail (is->priv->subprocess == NULL); - is->priv->subprocess = g_object_ref (subprocess); - g_mutex_unlock (&is->priv->stream_lock); - - input_stream = g_subprocess_get_stdout_pipe (subprocess); - output_stream = g_subprocess_get_stdin_pipe (subprocess); - - imapx_server_set_streams (is, input_stream, output_stream); - - g_object_unref (subprocess); - } - - return TRUE; - -#else /* GLIB_CHECK_VERSION(2,39,0) */ - - g_set_error_literal ( - error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "GLib 2.39 or later is required to connect " - "to an IMAP server through a shell command"); - - return FALSE; -#endif -} - -gboolean -imapx_connect_to_server (CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) -{ - CamelNetworkSettings *network_settings; - CamelNetworkSecurityMethod method; - CamelIMAPXStore *store; - CamelSettings *settings; - GIOStream *connection = NULL; - GIOStream *tls_stream; - GSocket *socket; - guint len; - guchar *token; - gint tok; - CamelIMAPXCommand *ic; - gchar *shell_command = NULL; - gboolean use_shell_command; - gboolean success = TRUE; - gchar *host; - GError *local_error = NULL; - - store = camel_imapx_server_ref_store (is); - - settings = camel_service_ref_settings (CAMEL_SERVICE (store)); - - network_settings = CAMEL_NETWORK_SETTINGS (settings); - host = camel_network_settings_dup_host (network_settings); - method = camel_network_settings_get_security_method (network_settings); - - use_shell_command = camel_imapx_settings_get_use_shell_command ( - CAMEL_IMAPX_SETTINGS (settings)); - - if (use_shell_command) - shell_command = camel_imapx_settings_dup_shell_command ( - CAMEL_IMAPX_SETTINGS (settings)); - - g_object_unref (settings); - - if (shell_command != NULL) { - success = connect_to_server_process (is, shell_command, error); - - g_free (shell_command); - - if (success) - goto connected; - else - goto exit; - } - - connection = camel_network_service_connect_sync ( - CAMEL_NETWORK_SERVICE (store), cancellable, error); - - if (connection != NULL) { - GInputStream *input_stream; - GOutputStream *output_stream; - - /* Disable the Nagle algorithm with TCP_NODELAY, since IMAP - * commands should be issued immediately even we've not yet - * received a response to a previous command. */ - socket = g_socket_connection_get_socket ( - G_SOCKET_CONNECTION (connection)); - g_socket_set_option ( - socket, IPPROTO_TCP, TCP_NODELAY, 1, &local_error); - if (local_error != NULL) { - /* Failure to set the socket option is non-fatal. */ - g_warning ("%s: %s", G_STRFUNC, local_error->message); - g_clear_error (&local_error); - } - - g_mutex_lock (&is->priv->stream_lock); - g_warn_if_fail (is->priv->connection == NULL); - is->priv->connection = g_object_ref (connection); - g_mutex_unlock (&is->priv->stream_lock); - - input_stream = g_io_stream_get_input_stream (connection); - output_stream = g_io_stream_get_output_stream (connection); - - imapx_server_set_streams (is, input_stream, output_stream); - - /* Hang on to the connection reference in case we need to - * issue STARTTLS below. */ - } else { - success = FALSE; - goto exit; - } - -connected: - while (1) { - GInputStream *input_stream; - - input_stream = camel_imapx_server_ref_input_stream (is); - - token = NULL; - tok = camel_imapx_input_stream_token ( - CAMEL_IMAPX_INPUT_STREAM (input_stream), - &token, &len, cancellable, error); - - if (tok < 0) { - success = FALSE; - - } else if (tok == '*') { - success = imapx_untagged ( - is, input_stream, cancellable, error); - - if (success) { - g_object_unref (input_stream); - break; - } - - } else { - camel_imapx_input_stream_ungettoken ( - CAMEL_IMAPX_INPUT_STREAM (input_stream), - tok, token, len); - - success = camel_imapx_input_stream_text ( - CAMEL_IMAPX_INPUT_STREAM (input_stream), - &token, cancellable, error); - - g_free (token); - } - - g_object_unref (input_stream); - - if (!success) - goto exit; - } - - if (!is->cinfo) { - ic = camel_imapx_command_new ( - is, "CAPABILITY", NULL, "CAPABILITY"); - - success = imapx_command_run (is, ic, cancellable, error); - - /* Server reported error. */ - if (success && ic->status->result != IMAPX_OK) { - g_set_error ( - error, CAMEL_ERROR, - CAMEL_ERROR_GENERIC, - "%s", ic->status->text); - success = FALSE; - } - - camel_imapx_command_unref (ic); - - if (!success) - goto exit; - } - - if (method == CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT) { - - if (CAMEL_IMAPX_LACK_CAPABILITY (is->cinfo, STARTTLS)) { - g_set_error ( - &local_error, CAMEL_ERROR, - CAMEL_ERROR_GENERIC, - _("Failed to connect to IMAP server %s in secure mode: %s"), - host, _("STARTTLS not supported")); - goto exit; - } - - ic = camel_imapx_command_new ( - is, "STARTTLS", NULL, "STARTTLS"); - - success = imapx_command_run (is, ic, cancellable, error); - - /* Server reported error. */ - if (success && ic->status->result != IMAPX_OK) { - g_set_error ( - error, CAMEL_ERROR, - CAMEL_ERROR_GENERIC, - "%s", ic->status->text); - success = FALSE; - } - - if (success) { - /* See if we got new capabilities - * in the STARTTLS response. */ - imapx_free_capability (is->cinfo); - is->cinfo = NULL; - if (ic->status->condition == IMAPX_CAPABILITY) { - is->cinfo = ic->status->u.cinfo; - ic->status->u.cinfo = NULL; - c (is->tagprefix, "got capability flags %08x\n", is->cinfo ? is->cinfo->capa : 0xFFFFFFFF); - imapx_server_stash_command_arguments (is); - } - } - - camel_imapx_command_unref (ic); - - if (!success) - goto exit; - - tls_stream = camel_network_service_starttls ( - CAMEL_NETWORK_SERVICE (store), connection, error); - - if (tls_stream != NULL) { - GInputStream *input_stream; - GOutputStream *output_stream; - - g_mutex_lock (&is->priv->stream_lock); - g_object_unref (is->priv->connection); - is->priv->connection = g_object_ref (tls_stream); - g_mutex_unlock (&is->priv->stream_lock); - - input_stream = - g_io_stream_get_input_stream (tls_stream); - output_stream = - g_io_stream_get_output_stream (tls_stream); - - imapx_server_set_streams ( - is, input_stream, output_stream); - - g_object_unref (tls_stream); - } else { - g_prefix_error ( - error, - _("Failed to connect to IMAP server %s in secure mode: "), - host); - success = FALSE; - goto exit; - } - - /* Get new capabilities if they weren't already given */ - if (is->cinfo == NULL) { - ic = camel_imapx_command_new ( - is, "CAPABILITY", NULL, "CAPABILITY"); - success = imapx_command_run (is, ic, cancellable, error); - camel_imapx_command_unref (ic); - - if (!success) - goto exit; - } - } - -exit: - if (!success) { - g_mutex_lock (&is->priv->stream_lock); - - g_clear_object (&is->priv->input_stream); - g_clear_object (&is->priv->output_stream); - g_clear_object (&is->priv->connection); -#if GLIB_CHECK_VERSION(2,39,0) - g_clear_object (&is->priv->subprocess); -#endif - - if (is->cinfo != NULL) { - imapx_free_capability (is->cinfo); - is->cinfo = NULL; - } - - g_mutex_unlock (&is->priv->stream_lock); - } - - g_free (host); - - g_clear_object (&connection); - g_clear_object (&store); - - return success; -} - -gboolean -camel_imapx_server_is_connected (CamelIMAPXServer *imapx_server) -{ - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), FALSE); - - return imapx_server->state >= IMAPX_CONNECTED; -} - -CamelAuthenticationResult -camel_imapx_server_authenticate (CamelIMAPXServer *is, - const gchar *mechanism, - GCancellable *cancellable, - GError **error) -{ - CamelNetworkSettings *network_settings; - CamelIMAPXStore *store; - CamelService *service; - CamelSettings *settings; - CamelAuthenticationResult result; - CamelIMAPXCommand *ic; - CamelSasl *sasl = NULL; - gchar *host; - gchar *user; - - g_return_val_if_fail ( - CAMEL_IS_IMAPX_SERVER (is), - CAMEL_AUTHENTICATION_ERROR); - - store = camel_imapx_server_ref_store (is); - - service = CAMEL_SERVICE (store); - settings = camel_service_ref_settings (service); - - network_settings = CAMEL_NETWORK_SETTINGS (settings); - host = camel_network_settings_dup_host (network_settings); - user = camel_network_settings_dup_user (network_settings); - - g_object_unref (settings); - - if (mechanism != NULL) { - if (is->cinfo && !g_hash_table_lookup (is->cinfo->auth_types, mechanism)) { - g_set_error ( - error, CAMEL_SERVICE_ERROR, - CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE, - _("IMAP server %s does not support %s " - "authentication"), host, mechanism); - result = CAMEL_AUTHENTICATION_ERROR; - goto exit; - } - - sasl = camel_sasl_new ("imap", mechanism, service); - if (sasl == NULL) { - g_set_error ( - error, CAMEL_SERVICE_ERROR, - CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE, - _("No support for %s authentication"), - mechanism); - result = CAMEL_AUTHENTICATION_ERROR; - goto exit; - } - } - - if (sasl != NULL) { - ic = camel_imapx_command_new ( - is, "AUTHENTICATE", NULL, "AUTHENTICATE %A", sasl); - } else { - const gchar *password; - - password = camel_service_get_password (service); - - if (user == NULL) { - g_set_error_literal ( - error, CAMEL_SERVICE_ERROR, - CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE, - _("Cannot authenticate without a username")); - result = CAMEL_AUTHENTICATION_ERROR; - goto exit; - } - - if (password == NULL) { - g_set_error_literal ( - error, CAMEL_SERVICE_ERROR, - CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE, - _("Authentication password not available")); - result = CAMEL_AUTHENTICATION_ERROR; - goto exit; - } - - ic = camel_imapx_command_new ( - is, "LOGIN", NULL, "LOGIN %s %s", user, password); - } - - if (!imapx_command_run (is, ic, cancellable, error)) - result = CAMEL_AUTHENTICATION_ERROR; - else if (ic->status->result == IMAPX_OK) - result = CAMEL_AUTHENTICATION_ACCEPTED; - else if (ic->status->result == IMAPX_NO) { - if (camel_imapx_store_is_connecting_concurrent_connection (store)) { - /* At least one connection succeeded, probably max connection limit - set on the server had been reached, thus use special error code - for it, to instruct the connection manager to decrease the limit - and use already created connection. */ - g_set_error_literal ( - error, CAMEL_IMAPX_SERVER_ERROR, - CAMEL_IMAPX_SERVER_ERROR_CONCURRENT_CONNECT_FAILED, - ic->status->text ? ic->status->text : _("Unknown error")); - result = CAMEL_AUTHENTICATION_ERROR; - } else { - result = CAMEL_AUTHENTICATION_REJECTED; - } - } else { - g_set_error_literal ( - error, CAMEL_SERVICE_ERROR, - CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE, - ic->status->text ? ic->status->text : _("Unknown error")); - result = CAMEL_AUTHENTICATION_ERROR; - } - - /* Forget old capabilities after login. */ - if (result == CAMEL_AUTHENTICATION_ACCEPTED) { - if (is->cinfo) { - imapx_free_capability (is->cinfo); - is->cinfo = NULL; - } - - if (ic->status->condition == IMAPX_CAPABILITY) { - is->cinfo = ic->status->u.cinfo; - ic->status->u.cinfo = NULL; - c (is->tagprefix, "got capability flags %08x\n", is->cinfo ? is->cinfo->capa : 0xFFFFFFFF); - imapx_server_stash_command_arguments (is); - } - } - - camel_imapx_command_unref (ic); - - if (sasl != NULL) - g_object_unref (sasl); - -exit: - g_free (host); - g_free (user); - - g_object_unref (store); - - return result; -} - -static gboolean -imapx_reconnect (CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXCommand *ic; - CamelService *service; - CamelSession *session; - CamelIMAPXStore *store; - CamelSettings *settings; - gchar *mechanism; - gboolean use_qresync; - gboolean success = FALSE; - - store = camel_imapx_server_ref_store (is); - - service = CAMEL_SERVICE (store); - session = camel_service_ref_session (service); - - settings = camel_service_ref_settings (service); - - mechanism = camel_network_settings_dup_auth_mechanism ( - CAMEL_NETWORK_SETTINGS (settings)); - - use_qresync = camel_imapx_settings_get_use_qresync ( - CAMEL_IMAPX_SETTINGS (settings)); - - g_object_unref (settings); - - if (!imapx_connect_to_server (is, cancellable, error)) - goto exception; - - if (is->state == IMAPX_AUTHENTICATED) - goto preauthed; - - if (!camel_session_authenticate_sync ( - session, service, mechanism, cancellable, error)) - goto exception; - - /* After login we re-capa unless the server already told us. */ - if (is->cinfo == NULL) { - GError *local_error = NULL; - - ic = camel_imapx_command_new ( - is, "CAPABILITY", NULL, "CAPABILITY"); - imapx_command_run (is, ic, cancellable, &local_error); - camel_imapx_command_unref (ic); - - if (local_error != NULL) { - g_propagate_error (error, local_error); - goto exception; - } - } - - is->state = IMAPX_AUTHENTICATED; - -preauthed: - /* Fetch namespaces (if supported). */ - if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, NAMESPACE)) { - GError *local_error = NULL; - - ic = camel_imapx_command_new ( - is, "NAMESPACE", NULL, "NAMESPACE"); - imapx_command_run (is, ic, cancellable, &local_error); - camel_imapx_command_unref (ic); - - if (local_error != NULL) { - g_propagate_error (error, local_error); - goto exception; - } - } - - /* Enable quick mailbox resynchronization (if supported). */ - if (use_qresync && CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, QRESYNC)) { - GError *local_error = NULL; - - ic = camel_imapx_command_new ( - is, "ENABLE", NULL, "ENABLE CONDSTORE QRESYNC"); - imapx_command_run (is, ic, cancellable, &local_error); - camel_imapx_command_unref (ic); - - if (local_error != NULL) { - g_propagate_error (error, local_error); - goto exception; - } - - is->use_qresync = TRUE; - } else { - is->use_qresync = FALSE; - } - - /* Set NOTIFY options after enabling QRESYNC (if supported). */ - if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, NOTIFY)) { - GError *local_error = NULL; - - /* XXX The list of FETCH attributes is negotiable. */ - ic = camel_imapx_command_new ( - is, "NOTIFY", NULL, "NOTIFY SET " - "(selected " - "(MessageNew (UID RFC822.SIZE RFC822.HEADER FLAGS)" - " MessageExpunge" - " FlagChange)) " - "(personal " - "(MessageNew" - " MessageExpunge" - " MailboxName" - " SubscriptionChange))"); - imapx_command_run (is, ic, cancellable, &local_error); - camel_imapx_command_unref (ic); - - if (local_error != NULL) { - g_propagate_error (error, local_error); - goto exception; - } - } - - is->state = IMAPX_INITIALISED; - - success = TRUE; - - goto exit; - -exception: - imapx_disconnect (is); - - if (is->cinfo) { - imapx_free_capability (is->cinfo); - is->cinfo = NULL; - } - -exit: - g_free (mechanism); - - g_object_unref (session); - g_object_unref (store); - - return success; -} - -/* ********************************************************************** */ - -static void -imapx_command_fetch_message_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - GetMessageData *data; - CamelIMAPXMailbox *mailbox; - GCancellable *cancellable; - GError *local_error = NULL; - - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); - - /* This is only for pushing status messages. */ - cancellable = camel_imapx_job_get_cancellable (job); - - data = camel_imapx_job_get_data (job); - g_return_if_fail (data != NULL); - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_if_fail (mailbox != NULL); - - /* We either have more to fetch (partial mode?), we are complete, - * or we failed. Failure is handled in the fetch code, so - * we just return the job, or keep it alive with more requests */ - - g_atomic_int_add (&job->commands, -1); - - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error fetching message")); - - } else if (data->use_multi_fetch) { - gsize really_fetched = g_seekable_tell (G_SEEKABLE (data->stream)); - /* Don't automatically stop when we reach the reported message - * size -- some crappy servers (like Microsoft Exchange) have - * a tendency to lie about it. Keep going (one request at a - * time) until the data actually stop coming. */ - if (data->fetch_offset < data->size || - data->fetch_offset == really_fetched) { - CamelIMAPXCommand *new_ic; - - camel_operation_progress ( - cancellable, - (data->fetch_offset *100) / data->size); - - new_ic = camel_imapx_command_new ( - is, "FETCH", mailbox, - "UID FETCH %t (BODY.PEEK[]", - data->uid); - camel_imapx_command_add (new_ic, "<%u.%u>", data->fetch_offset, MULTI_SIZE); - camel_imapx_command_add (new_ic, ")"); - new_ic->complete = imapx_command_fetch_message_done; - camel_imapx_command_set_job (new_ic, job); - new_ic->pri = job->pri - 1; - data->fetch_offset += MULTI_SIZE; - g_atomic_int_add (&job->commands, 1); - - imapx_command_queue (is, new_ic); - - camel_imapx_command_unref (new_ic); - - goto exit; - } - } - - /* If we have more messages to fetch, skip the rest. */ - if (g_atomic_int_get (&job->commands) > 0) { - /* Make sure no command will starve in a queue */ - QUEUE_LOCK (is); - imapx_command_start_next (is); - QUEUE_UNLOCK (is); - - goto exit; - } - - /* No more messages to fetch, let's wrap things up. */ - - if (local_error == NULL) { - g_io_stream_close (data->stream, cancellable, &local_error); - g_prefix_error ( - &local_error, "%s: ", - _("Failed to close the tmp stream")); - } - - if (local_error == NULL && - g_cancellable_set_error_if_cancelled (cancellable, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error fetching message")); - } - - if (local_error == NULL) { - gchar *cur_filename; - gchar *tmp_filename; - gchar *dirname; - - cur_filename = camel_data_cache_get_filename ( - data->message_cache, "cur", data->uid); - - tmp_filename = camel_data_cache_get_filename ( - data->message_cache, "tmp", data->uid); - - dirname = g_path_get_dirname (cur_filename); - g_mkdir_with_parents (dirname, 0700); - g_free (dirname); - - if (g_rename (tmp_filename, cur_filename) == 0) { - /* Exchange the "tmp" stream for the "cur" stream. */ - g_clear_object (&data->stream); - data->stream = camel_data_cache_get ( - data->message_cache, "cur", - data->uid, &local_error); - } else { - g_set_error ( - &local_error, G_FILE_ERROR, - g_file_error_from_errno (errno), - "%s: %s", - _("Failed to copy the tmp file"), - g_strerror (errno)); - } - - g_free (cur_filename); - g_free (tmp_filename); - } - - /* Delete the 'tmp' file only if the operation succeeded. It's because - cancelled operations end before they are properly finished (IMAP-protocol speaking), - thus if any other GET_MESSAGE operation was waiting for this job, then it - realized that the message was not downloaded and opened its own "tmp" file, but - of the same name, thus this remove would drop file which could be used - by a different GET_MESSAGE job. */ - if (!local_error && !g_cancellable_is_cancelled (cancellable)) - camel_data_cache_remove (data->message_cache, "tmp", data->uid, NULL); - - /* Avoid possible use-after-free when the imapx_unregister_job() can - also free the 'job' structure. */ - camel_imapx_job_ref (job); - - imapx_unregister_job (is, job); - - if (local_error != NULL) { - CamelIMAPXJob *pending_job; - - /* Give a chance to other threads. */ - g_thread_yield (); - - pending_job = imapx_server_ref_job (is, mailbox, IMAPX_JOB_GET_MESSAGE, data->uid); - if (pending_job != NULL) { - GIOStream *cache_stream; - - /* Wait for the job to finish. */ - camel_imapx_job_wait (pending_job, NULL); - camel_imapx_job_unref (pending_job); - - /* Disregard errors here. If we failed to retrieve the - * message from cache (implying the job we were waiting - * on failed or got cancelled), we'll just re-fetch it. */ - cache_stream = camel_data_cache_get (data->message_cache, "cur", data->uid, NULL); - if (cache_stream != NULL) { - g_clear_error (&local_error); - - g_clear_object (&data->stream); - data->stream = cache_stream; - } - } - - if (local_error) { - camel_imapx_job_take_error (job, local_error); - local_error = NULL; - } - } - - camel_imapx_job_unref (job); - -exit: - if (local_error != NULL) - camel_imapx_job_take_error (job, local_error); - - g_object_unref (mailbox); -} - -static gboolean -imapx_job_get_message_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXCommand *ic; - CamelIMAPXMailbox *mailbox; - GetMessageData *data; - gint i; - gboolean success = TRUE; - - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_val_if_fail (mailbox != NULL, FALSE); - - if (data->use_multi_fetch) { - for (i = 0; i < 3 && data->fetch_offset < data->size; i++) { - ic = camel_imapx_command_new ( - is, "FETCH", mailbox, - "UID FETCH %t (BODY.PEEK[]", - data->uid); - camel_imapx_command_add (ic, "<%u.%u>", data->fetch_offset, MULTI_SIZE); - camel_imapx_command_add (ic, ")"); - ic->complete = imapx_command_fetch_message_done; - camel_imapx_command_set_job (ic, job); - ic->pri = job->pri; - data->fetch_offset += MULTI_SIZE; - g_atomic_int_add (&job->commands, 1); - - imapx_command_queue (is, ic); - - camel_imapx_command_unref (ic); - } - } else { - ic = camel_imapx_command_new ( - is, "FETCH", mailbox, - "UID FETCH %t (BODY.PEEK[])", - data->uid); - ic->complete = imapx_command_fetch_message_done; - camel_imapx_command_set_job (ic, job); - ic->pri = job->pri; - g_atomic_int_add (&job->commands, 1); - - imapx_command_queue (is, ic); - - camel_imapx_command_unref (ic); - } - - g_object_unref (mailbox); - - return success; -} - -static gboolean -imapx_job_get_message_matches (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox, - const gchar *uid) -{ - GetMessageData *data; - - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); - - if (!camel_imapx_job_has_mailbox (job, mailbox)) - return FALSE; - - if (g_strcmp0 (uid, data->uid) != 0) - return FALSE; - - return TRUE; -} - -/* ********************************************************************** */ - -static void -imapx_command_copy_messages_step_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - CamelFolder *folder; - CamelIMAPXMailbox *mailbox; - CopyMessagesData *data; - GPtrArray *uids; - gint i; - GError *local_error = NULL; - - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); - - data = camel_imapx_job_get_data (job); - g_return_if_fail (data != NULL); - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_if_fail (mailbox != NULL); - - folder = imapx_server_ref_folder (is, mailbox); - g_return_if_fail (folder != NULL); - - uids = data->uids; - i = data->index; - - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - if (data->use_move_command) - g_prefix_error ( - &local_error, "%s: ", - _("Error moving messages")); - else - g_prefix_error ( - &local_error, "%s: ", - _("Error copying messages")); - camel_imapx_job_take_error (job, local_error); - goto exit; - } - - if (ic->status && ic->status->u.copyuid.uids && ic->status->u.copyuid.copied_uids && - ic->status->u.copyuid.uids->len == ic->status->u.copyuid.copied_uids->len) { - CamelFolder *destination; - - destination = imapx_server_ref_folder (is, data->destination); - if (destination) { - CamelMessageInfo *source_info, *destination_info; - CamelFolderChangeInfo *changes; - gint ii; - - changes = camel_folder_change_info_new (); - - for (ii = 0; ii < ic->status->u.copyuid.uids->len; ii++) { - gchar *uid; - gboolean is_new = FALSE; - - uid = g_strdup_printf ("%d", g_array_index (ic->status->u.copyuid.uids, guint32, ii)); - source_info = camel_folder_summary_get (folder->summary, uid); - g_free (uid); - - if (!source_info) - continue; - - uid = g_strdup_printf ("%d", g_array_index (ic->status->u.copyuid.copied_uids, guint32, ii)); - destination_info = camel_folder_summary_get (folder->summary, uid); - - if (!destination_info) { - is_new = TRUE; - destination_info = camel_message_info_clone (source_info); - destination_info->summary = destination->summary; - camel_pstring_free (destination_info->uid); - destination_info->uid = camel_pstring_strdup (uid); - } - - g_free (uid); - - imapx_set_message_info_flags_for_new_message ( - destination_info, - ((CamelMessageInfoBase *) source_info)->flags, - ((CamelMessageInfoBase *) source_info)->user_flags, - TRUE, - ((CamelMessageInfoBase *) source_info)->user_tags, - camel_imapx_mailbox_get_permanentflags (data->destination)); - if (is_new) - camel_folder_summary_add (destination->summary, destination_info); - camel_folder_change_info_add_uid (changes, destination_info->uid); - - camel_message_info_unref (source_info); - if (!is_new) - camel_message_info_unref (destination_info); - } - - if (camel_folder_change_info_changed (changes)) { - camel_folder_summary_touch (destination->summary); - camel_folder_summary_save_to_db (destination->summary, NULL); - camel_folder_changed (destination, changes); - } - - camel_folder_change_info_free (changes); - g_object_unref (destination); - } - } - - if (data->delete_originals) { - gint j; - - for (j = data->last_index; j < i; j++) - camel_folder_delete_message (folder, uids->pdata[j]); - } - - if (i < uids->len) { - imapx_command_copy_messages_step_start ( - is, job, i, &local_error); - - if (local_error != NULL) - camel_imapx_job_take_error (job, local_error); - } - -exit: - g_object_unref (folder); - g_object_unref (mailbox); - - imapx_unregister_job (is, job); -} - -static gboolean -imapx_command_copy_messages_step_start (CamelIMAPXServer *is, - CamelIMAPXJob *job, - gint index, - GError **error) -{ - CamelIMAPXMailbox *mailbox; - CamelIMAPXCommand *ic; - CopyMessagesData *data; - GPtrArray *uids; - gint i = index; - gboolean success = TRUE; - - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_val_if_fail (mailbox != NULL, FALSE); - - uids = data->uids; - - if (data->use_move_command) - ic = camel_imapx_command_new (is, "MOVE", mailbox, "UID MOVE "); - else - ic = camel_imapx_command_new (is, "COPY", mailbox, "UID COPY "); - ic->complete = imapx_command_copy_messages_step_done; - camel_imapx_command_set_job (ic, job); - ic->pri = job->pri; - data->last_index = i; - - g_object_unref (mailbox); - - for (; i < uids->len; i++) { - gint res; - const gchar *uid = (gchar *) g_ptr_array_index (uids, i); - - res = imapx_uidset_add (&data->uidset, ic, uid); - if (res == 1) { - camel_imapx_command_add (ic, " %M", data->destination); - data->index = i + 1; - imapx_command_queue (is, ic); - goto exit; - } - } - - data->index = i; - if (imapx_uidset_done (&data->uidset, ic)) { - camel_imapx_command_add (ic, " %M", data->destination); - imapx_command_queue (is, ic); - goto exit; - } - -exit: - camel_imapx_command_unref (ic); - - return success; -} - -static gboolean -imapx_job_copy_messages_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXMailbox *mailbox; - CopyMessagesData *data; - gboolean success; - - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_val_if_fail (mailbox != NULL, FALSE); - - success = imapx_server_sync_changes ( - is, mailbox, job->type, job->pri, cancellable, error); - if (!success) - imapx_unregister_job (is, job); - - /* XXX Should we still do this even if a failure occurred? */ - g_ptr_array_sort (data->uids, (GCompareFunc) imapx_uids_array_cmp); - imapx_uidset_init (&data->uidset, 0, MAX_COMMAND_LEN); - - g_object_unref (mailbox); - - return imapx_command_copy_messages_step_start (is, job, 0, error); -} - -static gboolean -imapx_job_copy_messages_matches (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox, - const gchar *uid) -{ - return camel_imapx_job_has_mailbox (job, mailbox); -} - -/* ********************************************************************** */ - -static void -imapx_command_append_message_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - CamelIMAPXFolder *ifolder; - CamelIMAPXMailbox *mailbox; - CamelFolder *folder; - CamelMessageInfo *mi; - AppendMessageData *data; - gchar *cur, *old_uid; - guint32 uidvalidity; - GError *local_error = NULL; - - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); - - data = camel_imapx_job_get_data (job); - g_return_if_fail (data != NULL); - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_if_fail (mailbox != NULL); - - folder = imapx_server_ref_folder (is, mailbox); - g_return_if_fail (folder != NULL); - - uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox); - - ifolder = CAMEL_IMAPX_FOLDER (folder); - - /* Append done. If we the server supports UIDPLUS we will get - * an APPENDUID response with the new uid. This lets us move the - * message we have directly to the cache and also create a correctly - * numbered MessageInfo, without losing any information. Otherwise - * we have to wait for the server to let us know it was appended. */ - - mi = camel_message_info_clone (data->info); - old_uid = g_strdup (data->info->uid); - - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error appending message")); - camel_imapx_job_take_error (job, local_error); - - } else if (ic->status && ic->status->condition == IMAPX_APPENDUID) { - c (is->tagprefix, "Got appenduid %d %d\n", (gint) ic->status->u.appenduid.uidvalidity, (gint) ic->status->u.appenduid.uid); - if (ic->status->u.appenduid.uidvalidity == uidvalidity) { - CamelFolderChangeInfo *changes; - - data->appended_uid = g_strdup_printf ("%u", (guint) ic->status->u.appenduid.uid); - mi->uid = camel_pstring_add (data->appended_uid, FALSE); - - cur = camel_data_cache_get_filename (ifolder->cache, "cur", mi->uid); - if (g_rename (data->path, cur) == -1 && errno != ENOENT) { - g_warning ("%s: Failed to rename '%s' to '%s': %s", G_STRFUNC, data->path, cur, g_strerror (errno)); - } - - imapx_set_message_info_flags_for_new_message ( - mi, - ((CamelMessageInfoBase *) data->info)->flags, - ((CamelMessageInfoBase *) data->info)->user_flags, - TRUE, - ((CamelMessageInfoBase *) data->info)->user_tags, - camel_imapx_mailbox_get_permanentflags (mailbox)); - camel_folder_summary_add (folder->summary, mi); - changes = camel_folder_change_info_new (); - camel_folder_change_info_add_uid (changes, mi->uid); - camel_folder_changed (folder, changes); - camel_folder_change_info_free (changes); - - g_free (cur); - } else { - c (is->tagprefix, "but uidvalidity changed \n"); - } - } - - camel_data_cache_remove (ifolder->cache, "new", old_uid, NULL); - g_free (old_uid); - - g_object_unref (folder); - g_object_unref (mailbox); - - imapx_unregister_job (is, job); -} - -static const gchar * -get_month_str (gint month) -{ - static const gchar tm_months[][4] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - - if (month < 1 || month > 12) - return NULL; - - return tm_months[month - 1]; -} - -static gboolean -imapx_job_append_message_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXMailbox *mailbox; - CamelIMAPXCommand *ic; - AppendMessageData *data; - - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_val_if_fail (mailbox != NULL, FALSE); - - if (data->date_time > 0) { - gchar *date_time; - struct tm stm; - - gmtime_r (&data->date_time, &stm); - - /* Store always in UTC */ - date_time = g_strdup_printf ( - "\"%02d-%s-%04d %02d:%02d:%02d +0000\"", - stm.tm_mday, - get_month_str (stm.tm_mon + 1), - stm.tm_year + 1900, - stm.tm_hour, - stm.tm_min, - stm.tm_sec); - - ic = camel_imapx_command_new ( - is, "APPEND", NULL, - "APPEND %M %F %t %P", mailbox, - ((CamelMessageInfoBase *) data->info)->flags, - ((CamelMessageInfoBase *) data->info)->user_flags, - date_time, - data->path); - - g_free (date_time); - } else { - ic = camel_imapx_command_new ( - is, "APPEND", NULL, - "APPEND %M %F %P", mailbox, - ((CamelMessageInfoBase *) data->info)->flags, - ((CamelMessageInfoBase *) data->info)->user_flags, - data->path); - } - - ic->complete = imapx_command_append_message_done; - camel_imapx_command_set_job (ic, job); - ic->pri = job->pri; - g_atomic_int_add (&job->commands, 1); - - imapx_command_queue (is, ic); - - camel_imapx_command_unref (ic); - - g_object_unref (mailbox); - - return TRUE; -} - -/* ********************************************************************** */ - -static gint -imapx_refresh_info_uid_cmp (gconstpointer ap, - gconstpointer bp, - gboolean ascending) -{ - guint av, bv; - - av = g_ascii_strtoull ((const gchar *) ap, NULL, 10); - bv = g_ascii_strtoull ((const gchar *) bp, NULL, 10); - - if (av < bv) - return ascending ? -1 : 1; - else if (av > bv) - return ascending ? 1 : -1; - else - return 0; -} - -static gint -imapx_uids_array_cmp (gconstpointer ap, - gconstpointer bp) -{ - const gchar **a = (const gchar **) ap; - const gchar **b = (const gchar **) bp; - - return imapx_refresh_info_uid_cmp (*a, *b, TRUE); -} - -static gint -imapx_refresh_info_cmp (gconstpointer ap, - gconstpointer bp) -{ - const struct _refresh_info *a = ap; - const struct _refresh_info *b = bp; - - return imapx_refresh_info_uid_cmp (a->uid, b->uid, TRUE); -} - -static gint -imapx_refresh_info_cmp_descending (gconstpointer ap, - gconstpointer bp) -{ - const struct _refresh_info *a = ap; - const struct _refresh_info *b = bp; - - return imapx_refresh_info_uid_cmp (a->uid, b->uid, FALSE); - -} - -/* skips over non-server uids (pending appends) */ -static guint -imapx_index_next (GPtrArray *uids, - CamelFolderSummary *s, - guint index) -{ - - while (index < uids->len) { - CamelMessageInfo *info; - - index++; - if (index >= uids->len) - break; - - info = camel_folder_summary_get (s, g_ptr_array_index (uids, index)); - if (!info) - continue; - - if (info && (strchr (camel_message_info_uid (info), '-') != NULL)) { - camel_message_info_unref (info); - e ('?', "Ignoring offline uid '%s'\n", camel_message_info_uid (info)); - } else { - camel_message_info_unref (info); - break; - } - } - - return index; -} - -static void -imapx_command_step_fetch_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXMailbox *mailbox; - CamelIMAPXSummary *isum; - CamelIMAPXJob *job; - CamelFolder *folder; - RefreshInfoData *data; - gint i; - GError *local_error = NULL; - - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); - - data = camel_imapx_job_get_data (job); - g_return_if_fail (data != NULL); - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_if_fail (mailbox != NULL); - - folder = imapx_server_ref_folder (is, mailbox); - g_return_if_fail (folder != NULL); - - data->scan_changes = FALSE; - - isum = CAMEL_IMAPX_SUMMARY (folder->summary); - - i = data->index; - - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error fetching message headers")); - camel_imapx_job_take_error (job, local_error); - goto exit; - } - - if (camel_folder_change_info_changed (data->changes)) { - imapx_update_store_summary (folder); - camel_folder_summary_save_to_db (folder->summary, NULL); - camel_folder_changed (folder, data->changes); - } - - camel_folder_change_info_clear (data->changes); - - if (i < data->infos->len) { - ic = camel_imapx_command_new ( - is, "FETCH", mailbox, "UID FETCH "); - ic->complete = imapx_command_step_fetch_done; - camel_imapx_command_set_job (ic, job); - ic->pri = job->pri - 1; - - data->last_index = i; - - for (; i < data->infos->len; i++) { - gint res; - struct _refresh_info *r = &g_array_index (data->infos, struct _refresh_info, i); - - if (!r->exists) { - res = imapx_uidset_add (&data->uidset, ic, r->uid); - if (res == 1) { - camel_imapx_command_add (ic, " (RFC822.SIZE RFC822.HEADER)"); - data->index = i + 1; - - imapx_command_queue (is, ic); - - camel_imapx_command_unref (ic); - - g_object_unref (folder); - g_object_unref (mailbox); - - return; - } - } - } - - data->index = data->infos->len; - if (imapx_uidset_done (&data->uidset, ic)) { - camel_imapx_command_add (ic, " (RFC822.SIZE RFC822.HEADER)"); - - imapx_command_queue (is, ic); - - camel_imapx_command_unref (ic); - - g_object_unref (folder); - g_object_unref (mailbox); - - return; - } - - /* XXX What fate for our newly-created but unsubmitted - * CamelIMAPXCommand if we get here? I guess just - * discard it and move on? Also warn so I know if - * we're actually taking this branch for real. */ - camel_imapx_command_unref (ic); - g_warn_if_reached (); - } - - if (camel_folder_summary_count (folder->summary)) { - gchar *uid; - guint32 uidl; - guint32 uidnext; - - uid = camel_imapx_dup_uid_from_summary_index ( - folder, - camel_folder_summary_count (folder->summary) - 1); - if (uid) { - uidl = (guint32) strtoull (uid, NULL, 10); - g_free (uid); - - uidl++; - - uidnext = camel_imapx_mailbox_get_uidnext (mailbox); - - if (uidl > uidnext) { - c ( - is->tagprefix, - "Updating uidnext for '%s' to %ul\n", - camel_imapx_mailbox_get_name (mailbox), - uidl); - camel_imapx_mailbox_set_uidnext (mailbox, uidl); - } - } - } - - isum->uidnext = camel_imapx_mailbox_get_uidnext (mailbox); - -exit: - refresh_info_data_infos_free (data); - - g_object_unref (folder); - g_object_unref (mailbox); - - imapx_unregister_job (is, job); + return success; } -static gint -imapx_uid_cmp (gconstpointer ap, - gconstpointer bp, - gpointer data) -{ - const gchar *a = ap, *b = bp; - gchar *ae, *be; - gulong av, bv; - - av = strtoul (a, &ae, 10); - bv = strtoul (b, &be, 10); - - if (av < bv) - return -1; - else if (av > bv) - return 1; - - if (*ae == '-') - ae++; - if (*be == '-') - be++; +/* ********************************************************************** */ - return strcmp (ae, be); -} +/* FIXME: this is basically a copy of the same in camel-imapx-utils.c */ +static struct { + const gchar *name; + guint32 flag; +} flags_table[] = { + { "\\ANSWERED", CAMEL_MESSAGE_ANSWERED }, + { "\\DELETED", CAMEL_MESSAGE_DELETED }, + { "\\DRAFT", CAMEL_MESSAGE_DRAFT }, + { "\\FLAGGED", CAMEL_MESSAGE_FLAGGED }, + { "\\SEEN", CAMEL_MESSAGE_SEEN }, + { "\\RECENT", CAMEL_IMAPX_MESSAGE_RECENT }, + { "JUNK", CAMEL_MESSAGE_JUNK }, + { "NOTJUNK", CAMEL_MESSAGE_NOTJUNK } +}; static void -imapx_job_scan_changes_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - CamelIMAPXMailbox *mailbox; - CamelIMAPXSettings *settings; - CamelFolder *folder; - RefreshInfoData *data; - GCancellable *cancellable; - guint uidset_size; - guint32 unseen; - GError *local_error = NULL; - - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); - - /* This is only for pushing status messages. */ - cancellable = camel_imapx_job_get_cancellable (job); - - data = camel_imapx_job_get_data (job); - g_return_if_fail (data != NULL); - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_if_fail (mailbox != NULL); - - folder = imapx_server_ref_folder (is, mailbox); - g_return_if_fail (folder != NULL); - - data->scan_changes = FALSE; - - settings = camel_imapx_server_ref_settings (is); - uidset_size = camel_imapx_settings_get_batch_fetch_count (settings); - g_object_unref (settings); - - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error retrieving message")); - camel_imapx_job_take_error (job, local_error); - - } else { - GCompareDataFunc uid_cmp = imapx_uid_cmp; - CamelMessageInfo *s_minfo = NULL; - CamelIMAPXMessageInfo *info; - CamelFolderSummary *s = folder->summary; - GList *removed = NULL, *l; - gboolean fetch_new = FALSE; - gint i; - guint j = 0; - GPtrArray *uids; - - /* Actually we wanted to do this after the SELECT but before the - * FETCH command was issued. But this should suffice. */ - ((CamelIMAPXSummary *) s)->uidnext = - camel_imapx_mailbox_get_uidnext (mailbox); - ((CamelIMAPXSummary *) s)->modseq = - camel_imapx_mailbox_get_highestmodseq (mailbox); - - /* Here we do the typical sort/iterate/merge loop. - * If the server flags dont match what we had, we modify our - * flags to pick up what the server now has - but we merge - * not overwrite */ - - /* FIXME: We also have to check the offline directory for - * anything missing in our summary, and also queue up jobs - * for all outstanding messages to be uploaded */ - - camel_folder_summary_lock (s); - - /* obtain a copy to be thread safe */ - uids = camel_folder_summary_get_array (s); - - qsort (data->infos->data, data->infos->len, sizeof (struct _refresh_info), imapx_refresh_info_cmp); - g_ptr_array_sort (uids, (GCompareFunc) imapx_uids_array_cmp); - - if (uids->len) - s_minfo = camel_folder_summary_get (s, g_ptr_array_index (uids, 0)); - - for (i = 0; i < data->infos->len; i++) { - struct _refresh_info *r = &g_array_index (data->infos, struct _refresh_info, i); - - while (s_minfo && uid_cmp (camel_message_info_uid (s_minfo), r->uid, s) < 0) { - const gchar *uid = camel_message_info_uid (s_minfo); - - camel_folder_change_info_remove_uid (data->changes, uid); - removed = g_list_prepend (removed, (gpointer ) g_strdup (uid)); - camel_message_info_unref (s_minfo); - s_minfo = NULL; - - j = imapx_index_next (uids, s, j); - if (j < uids->len) - s_minfo = camel_folder_summary_get (s, g_ptr_array_index (uids, j)); - } - - info = NULL; - if (s_minfo && uid_cmp (s_minfo->uid, r->uid, s) == 0) { - info = (CamelIMAPXMessageInfo *) s_minfo; - - if (imapx_update_message_info_flags ( - (CamelMessageInfo *) info, - r->server_flags, - r->server_user_flags, - camel_imapx_mailbox_get_permanentflags (mailbox), - folder, FALSE)) - camel_folder_change_info_change_uid ( - data->changes, - camel_message_info_uid (s_minfo)); - r->exists = TRUE; - } else - fetch_new = TRUE; - - if (s_minfo) { - camel_message_info_unref (s_minfo); - s_minfo = NULL; - } - - if (j >= uids->len) - break; - - j = imapx_index_next (uids, s, j); - if (j < uids->len) - s_minfo = camel_folder_summary_get (s, g_ptr_array_index (uids, j)); - } - - if (s_minfo) - camel_message_info_unref (s_minfo); - - while (j < uids->len) { - s_minfo = camel_folder_summary_get (s, g_ptr_array_index (uids, j)); - - if (!s_minfo) { - j++; - continue; - } - - e (is->tagprefix, "Message %s vanished\n", s_minfo->uid); - removed = g_list_prepend (removed, (gpointer) g_strdup (s_minfo->uid)); - camel_message_info_unref (s_minfo); - j++; - } - - for (l = removed; l != NULL; l = g_list_next (l)) { - gchar *uid = (gchar *) l->data; - - camel_folder_change_info_remove_uid (data->changes, uid); - } - - if (removed != NULL) { - camel_folder_summary_remove_uids (s, removed); - camel_folder_summary_touch (s); - - g_list_free_full (removed, (GDestroyNotify) g_free); - } - - camel_folder_summary_save_to_db (s, NULL); - imapx_update_store_summary (folder); - - camel_folder_summary_unlock (s); - - if (camel_folder_change_info_changed (data->changes)) - camel_folder_changed (folder, data->changes); - camel_folder_change_info_clear (data->changes); - - camel_folder_summary_free_array (uids); - - /* If we have any new messages, download their headers, but only a few (100?) at a time */ - if (fetch_new) { - job->pop_operation_msg = TRUE; - - camel_operation_push_message ( - cancellable, - _("Fetching summary information for new messages in '%s'"), - camel_folder_get_display_name (folder)); - - imapx_uidset_init (&data->uidset, uidset_size, 0); - /* These are new messages which arrived since we last knew the unseen count; - * update it as they arrive. */ - data->update_unseen = TRUE; - - g_object_unref (folder); - g_object_unref (mailbox); - - return imapx_command_step_fetch_done (is, ic); - } - } - - refresh_info_data_infos_free (data); - - /* There's no sane way to get the server-side unseen count - * on the select mailbox, so just work it out from the flags. */ - unseen = camel_folder_summary_get_unread_count (folder->summary); - camel_imapx_mailbox_set_unseen (mailbox, unseen); - - g_object_unref (folder); - g_object_unref (mailbox); - - imapx_unregister_job (is, job); -} - -static gboolean -imapx_job_scan_changes_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) +imapx_server_set_store (CamelIMAPXServer *server, + CamelIMAPXStore *store) { - CamelFolder *folder; - CamelIMAPXCommand *ic; - CamelIMAPXMailbox *mailbox; - RefreshInfoData *data; - - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_val_if_fail (mailbox != NULL, FALSE); - - folder = imapx_server_ref_folder (is, mailbox); - g_return_val_if_fail (folder != NULL, FALSE); - - job->pop_operation_msg = TRUE; - - camel_operation_push_message ( - cancellable, - _("Scanning for changed messages in '%s'"), - camel_folder_get_display_name (folder)); - - ic = camel_imapx_command_new ( - is, "FETCH", mailbox, - "UID FETCH 1:* (UID FLAGS)"); - camel_imapx_command_set_job (ic, job); - ic->complete = imapx_job_scan_changes_done; - - data->scan_changes = TRUE; - ic->pri = job->pri; - refresh_info_data_infos_free (data); - data->infos = g_array_new (0, 0, sizeof (struct _refresh_info)); - - imapx_command_queue (is, ic); - - camel_imapx_command_unref (ic); - - g_object_unref (folder); - g_object_unref (mailbox); + g_return_if_fail (CAMEL_IS_IMAPX_STORE (store)); - return TRUE; + g_weak_ref_set (&server->priv->store, store); } static void -imapx_command_fetch_new_messages_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) +imapx_server_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) { - CamelIMAPXJob *job; - CamelIMAPXSummary *isum; - CamelIMAPXMailbox *mailbox; - CamelFolder *folder; - RefreshInfoData *data; - GError *local_error = NULL; - - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); - - data = camel_imapx_job_get_data (job); - g_return_if_fail (data != NULL); - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_if_fail (mailbox != NULL); - - folder = imapx_server_ref_folder (is, mailbox); - g_return_if_fail (folder != NULL); - - isum = CAMEL_IMAPX_SUMMARY (folder->summary); - - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error fetching new messages")); - camel_imapx_job_take_error (job, local_error); - goto exit; - } - - if (camel_folder_change_info_changed (data->changes)) { - camel_folder_summary_save_to_db (folder->summary, NULL); - imapx_update_store_summary (folder); - camel_folder_changed (folder, data->changes); - camel_folder_change_info_clear (data->changes); - } - - if (camel_folder_summary_count (folder->summary)) { - gchar *uid; - guint32 uidl; - guint32 uidnext; - - uid = camel_imapx_dup_uid_from_summary_index ( - folder, - camel_folder_summary_count (folder->summary) - 1); - if (uid) { - uidl = (guint32) strtoull (uid, NULL, 10); - g_free (uid); - - uidl++; - - uidnext = camel_imapx_mailbox_get_uidnext (mailbox); - - if (uidl > uidnext) { - c ( - is->tagprefix, - "Updating uidnext for '%s' to %ul\n", - camel_imapx_mailbox_get_name (mailbox), - uidl); - camel_imapx_mailbox_set_uidnext (mailbox, uidl); - } - } + switch (property_id) { + case PROP_STORE: + imapx_server_set_store ( + CAMEL_IMAPX_SERVER (object), + g_value_get_object (value)); + return; } - isum->uidnext = camel_imapx_mailbox_get_uidnext (mailbox); - -exit: - g_object_unref (folder); - g_object_unref (mailbox); - - imapx_unregister_job (is, job); + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void -imapx_command_fetch_new_uids_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - RefreshInfoData *data; - - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); - - data = camel_imapx_job_get_data (job); - g_return_if_fail (data != NULL); - - data->scan_changes = FALSE; - - qsort ( - data->infos->data, - data->infos->len, - sizeof (struct _refresh_info), - imapx_refresh_info_cmp_descending); - - imapx_command_step_fetch_done (is, ic); -} - -static gboolean -imapx_job_fetch_new_messages_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) +imapx_server_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { - CamelIMAPXCommand *ic; - CamelFolder *folder; - CamelIMAPXMailbox *mailbox; - CamelIMAPXSettings *settings; - CamelSortType fetch_order; - RefreshInfoData *data; - guint32 total, diff; - guint32 messages; - guint uidset_size; - gchar *uid = NULL; - - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_val_if_fail (mailbox != NULL, FALSE); - - folder = imapx_server_ref_folder (is, mailbox); - g_return_val_if_fail (folder != NULL, FALSE); - - settings = camel_imapx_server_ref_settings (is); - fetch_order = camel_imapx_settings_get_fetch_order (settings); - uidset_size = camel_imapx_settings_get_batch_fetch_count (settings); - g_object_unref (settings); - - messages = camel_imapx_mailbox_get_messages (mailbox); + switch (property_id) { + case PROP_STORE: + g_value_take_object ( + value, + camel_imapx_server_ref_store ( + CAMEL_IMAPX_SERVER (object))); + return; + } - total = camel_folder_summary_count (folder->summary); - diff = messages - total; + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} - if (total > 0) { - guint64 uidl; - uid = camel_imapx_dup_uid_from_summary_index (folder, total - 1); - if (uid) { - uidl = strtoull (uid, NULL, 10); - g_free (uid); - uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uidl + 1); - } else { - uid = g_strdup ("1"); - } - } else - uid = g_strdup ("1"); +static void +imapx_server_dispose (GObject *object) +{ + CamelIMAPXServer *server = CAMEL_IMAPX_SERVER (object); - job->pop_operation_msg = TRUE; + g_cancellable_cancel (server->priv->cancellable); - camel_operation_push_message ( - cancellable, - _("Fetching summary information for new messages in '%s'"), - camel_folder_get_display_name (folder)); + imapx_disconnect (server); - if (diff > uidset_size || fetch_order == CAMEL_SORT_DESCENDING) { - ic = camel_imapx_command_new ( - is, "FETCH", mailbox, - "UID FETCH %s:* (UID FLAGS)", uid); - imapx_uidset_init (&data->uidset, uidset_size, 0); - refresh_info_data_infos_free (data); - data->infos = g_array_new (0, 0, sizeof (struct _refresh_info)); - ic->pri = job->pri; + g_weak_ref_set (&server->priv->store, NULL); - data->scan_changes = TRUE; + g_clear_object (&server->priv->subprocess); - if (fetch_order == CAMEL_SORT_DESCENDING) - ic->complete = imapx_command_fetch_new_uids_done; - else - ic->complete = imapx_command_step_fetch_done; - } else { - ic = camel_imapx_command_new ( - is, "FETCH", mailbox, - "UID FETCH %s:* (RFC822.SIZE RFC822.HEADER FLAGS)", uid); - ic->pri = job->pri; - ic->complete = imapx_command_fetch_new_messages_done; + g_mutex_lock (&server->priv->idle_lock); + g_clear_object (&server->priv->idle_cancellable); + g_clear_object (&server->priv->idle_mailbox); + if (server->priv->idle_pending) { + g_source_destroy (server->priv->idle_pending); + g_source_unref (server->priv->idle_pending); + server->priv->idle_pending = NULL; } + g_mutex_unlock (&server->priv->idle_lock); - camel_imapx_command_set_job (ic, job); - - imapx_command_queue (is, ic); - - camel_imapx_command_unref (ic); - - g_free (uid); - - g_object_unref (folder); - g_object_unref (mailbox); + g_clear_object (&server->priv->subprocess); - return TRUE; + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (camel_imapx_server_parent_class)->dispose (object); } -static gboolean -imapx_job_refresh_info_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) +static void +imapx_server_finalize (GObject *object) { - CamelIMAPXSummary *isum; - CamelFolder *folder; - CamelIMAPXMailbox *mailbox; - const gchar *full_name; - gboolean need_rescan = FALSE; - gboolean is_selected = FALSE; - gboolean can_qresync = FALSE; - gboolean success; - guint32 messages; - guint32 unseen; - guint32 uidnext; - guint32 uidvalidity; - guint64 highestmodseq; - guint32 total; - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_val_if_fail (mailbox != NULL, FALSE); - - folder = imapx_server_ref_folder (is, mailbox); - g_return_val_if_fail (folder != NULL, FALSE); + CamelIMAPXServer *is = CAMEL_IMAPX_SERVER (object); - isum = CAMEL_IMAPX_SUMMARY (folder->summary); + g_mutex_clear (&is->priv->stream_lock); + g_mutex_clear (&is->priv->select_lock); + g_mutex_clear (&is->priv->changes_lock); - full_name = camel_folder_get_full_name (folder); + camel_folder_change_info_free (is->priv->changes); + imapx_free_status (is->priv->copyuid_status); - /* Sync changes first, else unread count will not - * match. Need to think about better ways for this */ - success = imapx_server_sync_changes ( - is, mailbox, job->type, job->pri, cancellable, error); - if (!success) - goto done; + g_free (is->priv->context); + g_hash_table_destroy (is->priv->untagged_handlers); - messages = camel_imapx_mailbox_get_messages (mailbox); - unseen = camel_imapx_mailbox_get_unseen (mailbox); - uidnext = camel_imapx_mailbox_get_uidnext (mailbox); - uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox); - highestmodseq = camel_imapx_mailbox_get_highestmodseq (mailbox); + if (is->priv->inactivity_timeout != NULL) + g_source_unref (is->priv->inactivity_timeout); + g_mutex_clear (&is->priv->inactivity_timeout_lock); -#if 0 /* There are issues with this still; continue with the buggy - * behaviour where we issue STATUS on the current folder, for now. */ - if (is->priv->select_folder == folder) - is_selected = TRUE; -#endif - total = camel_folder_summary_count (folder->summary); + g_free (is->priv->status_data_items); + g_free (is->priv->list_return_opts); - if (uidvalidity > 0 && uidvalidity != isum->validity) - need_rescan = TRUE; + if (is->priv->search_results != NULL) + g_array_unref (is->priv->search_results); + g_mutex_clear (&is->priv->search_results_lock); - /* We don't have valid unread count or modseq for currently-selected server - * (unless we want to re-SELECT it). We fake unread count when fetching - * message flags, but don't depend on modseq for the selected folder */ - if (total != messages || - isum->uidnext != uidnext || - camel_folder_summary_get_unread_count (folder->summary) != unseen || - (!is_selected && isum->modseq != highestmodseq)) - need_rescan = TRUE; - - /* This is probably the first check of this folder after startup; - * use STATUS to check whether the cached summary is valid, rather - * than blindly updating. Only for servers which support CONDSTORE - * though. */ - if (isum->modseq > 0 && highestmodseq == 0) - need_rescan = FALSE; + g_hash_table_destroy (is->priv->known_alerts); + g_mutex_clear (&is->priv->known_alerts_lock); - /* If we don't think there's anything to do, poke it to check */ - if (!need_rescan) { - CamelIMAPXCommand *ic; + g_mutex_clear (&is->priv->idle_lock); + g_cond_clear (&is->priv->idle_cond); - #if 0 /* see comment for disabled bits above */ - if (is_selected) { - /* We may not issue STATUS on the current folder. Use SELECT or NOOP instead. */ - if (0 /* server needs SELECT not just NOOP */) { - if (imapx_in_idle (is)) - if (!imapx_stop_idle (is, error)) - goto done; - /* This doesn't work -- this is an immediate command, not queued */ - imapx_maybe_select (is, folder) - } else { - /* Or maybe just NOOP, unless we're in IDLE in which case do nothing */ - if (!imapx_in_idle (is)) { - if (!camel_imapx_server_noop (is, folder, cancellable, error)) - goto done; - } - } - } else - #endif - { - ic = camel_imapx_command_new ( - is, "STATUS", NULL, "STATUS %M (%t)", - mailbox, is->priv->status_data_items); + g_rec_mutex_clear (&is->priv->command_lock); - camel_imapx_command_set_job (ic, job); - ic->pri = job->pri; + g_weak_ref_clear (&is->priv->store); + g_weak_ref_clear (&is->priv->select_mailbox); + g_weak_ref_clear (&is->priv->select_pending); + g_clear_object (&is->priv->cancellable); - success = imapx_command_run_sync ( - is, ic, cancellable, error); + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (camel_imapx_server_parent_class)->finalize (object); +} - camel_imapx_command_unref (ic); +static void +imapx_server_constructed (GObject *object) +{ + CamelIMAPXServer *server; - if (!success) { - g_prefix_error ( - error, "%s: ", - _("Error refreshing folder")); - goto done; - } - } + /* Chain up to parent's method. */ + G_OBJECT_CLASS (camel_imapx_server_parent_class)->constructed (object); - /* Recalulate need_rescan */ + server = CAMEL_IMAPX_SERVER (object); + server->priv->tagprefix = 'Z'; +} - messages = camel_imapx_mailbox_get_messages (mailbox); - unseen = camel_imapx_mailbox_get_unseen (mailbox); - uidnext = camel_imapx_mailbox_get_uidnext (mailbox); - highestmodseq = camel_imapx_mailbox_get_highestmodseq (mailbox); +static void +camel_imapx_server_class_init (CamelIMAPXServerClass *class) +{ + GObjectClass *object_class; - if (total != messages || - isum->uidnext != uidnext || - camel_folder_summary_get_unread_count (folder->summary) != unseen || - (!is_selected && isum->modseq != highestmodseq)) - need_rescan = TRUE; - } + g_type_class_add_private (class, sizeof (CamelIMAPXServerPrivate)); - messages = camel_imapx_mailbox_get_messages (mailbox); - unseen = camel_imapx_mailbox_get_unseen (mailbox); - uidnext = camel_imapx_mailbox_get_uidnext (mailbox); - uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox); - highestmodseq = camel_imapx_mailbox_get_highestmodseq (mailbox); + object_class = G_OBJECT_CLASS (class); + object_class->set_property = imapx_server_set_property; + object_class->get_property = imapx_server_get_property; + object_class->finalize = imapx_server_finalize; + object_class->dispose = imapx_server_dispose; + object_class->constructed = imapx_server_constructed; - if (is->use_qresync && isum->modseq > 0 && uidvalidity > 0) - can_qresync = TRUE; + g_object_class_install_property ( + object_class, + PROP_STORE, + g_param_spec_object ( + "store", + "Store", + "IMAPX store for this server", + CAMEL_TYPE_IMAPX_STORE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); - e ( - is->tagprefix, - "folder %s is %sselected, " - "total %u / %u, unread %u / %u, modseq %" - G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT - ", uidnext %u / %u: will %srescan\n", - full_name, - is_selected ? "" : "not ", - total, - messages, - camel_folder_summary_get_unread_count (folder->summary), - unseen, - isum->modseq, - highestmodseq, - isum->uidnext, - uidnext, - need_rescan ? "" : "not "); - - /* Fetch new messages first, so that they appear to the user ASAP */ - if (messages > total || uidnext > isum->uidnext) { - if (!total) - need_rescan = FALSE; + signals[REFRESH_MAILBOX] = g_signal_new ( + "refresh-mailbox", + G_OBJECT_CLASS_TYPE (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CamelIMAPXServerClass, refresh_mailbox), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + CAMEL_TYPE_IMAPX_MAILBOX); +} - success = imapx_server_fetch_new_messages ( - is, mailbox, FALSE, FALSE, cancellable, error); - if (!success) - goto done; +static void +camel_imapx_server_init (CamelIMAPXServer *is) +{ + is->priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is); - /* If QRESYNC-capable we'll have got all flags changes in SELECT */ - if (can_qresync) - goto qresync_done; - } + is->priv->untagged_handlers = create_initial_untagged_handler_table (); - if (!need_rescan) - goto done; + g_mutex_init (&is->priv->stream_lock); + g_mutex_init (&is->priv->inactivity_timeout_lock); + g_mutex_init (&is->priv->select_lock); + g_mutex_init (&is->priv->changes_lock); + g_mutex_init (&is->priv->search_results_lock); + g_mutex_init (&is->priv->known_alerts_lock); - if (can_qresync) { - /* Actually we only want to select it; no need for the NOOP */ - success = camel_imapx_server_noop ( - is, mailbox, cancellable, error); - if (!success) - goto done; - qresync_done: - messages = camel_imapx_mailbox_get_messages (mailbox); - unseen = camel_imapx_mailbox_get_unseen (mailbox); - highestmodseq = camel_imapx_mailbox_get_highestmodseq (mailbox); + g_weak_ref_init (&is->priv->store, NULL); + g_weak_ref_init (&is->priv->select_mailbox, NULL); + g_weak_ref_init (&is->priv->select_pending, NULL); - isum->modseq = highestmodseq; - total = camel_folder_summary_count (folder->summary); - if (total != messages || - camel_folder_summary_get_unread_count (folder->summary) != unseen || - (isum->modseq != highestmodseq)) { - c ( - is->tagprefix, - "Eep, after QRESYNC we're out of sync. " - "total %u / %u, unread %u / %u, modseq %" - G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT "\n", - total, messages, - camel_folder_summary_get_unread_count (folder->summary), - unseen, - isum->modseq, - highestmodseq); - } else { - c ( - is->tagprefix, - "OK, after QRESYNC we're still in sync. " - "total %u / %u, unread %u / %u, modseq %" - G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT "\n", - total, messages, - camel_folder_summary_get_unread_count (folder->summary), - unseen, - isum->modseq, - highestmodseq); - goto done; - } - } + is->priv->cancellable = g_cancellable_new (); - g_object_unref (folder); - g_object_unref (mailbox); + is->priv->state = IMAPX_DISCONNECTED; + is->priv->is_cyrus = FALSE; + is->priv->copyuid_status = NULL; - return imapx_job_scan_changes_start (job, is, cancellable, error); + is->priv->changes = camel_folder_change_info_new (); -done: - g_object_unref (folder); - g_object_unref (mailbox); + is->priv->known_alerts = g_hash_table_new_full ( + (GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) NULL); - imapx_unregister_job (is, job); + /* Initialize IDLE members. */ + g_mutex_init (&is->priv->idle_lock); + g_cond_init (&is->priv->idle_cond); + is->priv->idle_state = IMAPX_IDLE_STATE_OFF; + is->priv->idle_stamp = 0; - return success; + g_rec_mutex_init (&is->priv->command_lock); } -static gboolean -imapx_job_refresh_info_matches (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox, - const gchar *uid) +CamelIMAPXServer * +camel_imapx_server_new (CamelIMAPXStore *store) { - return camel_imapx_job_has_mailbox (job, mailbox); -} + g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), NULL); -/* ********************************************************************** */ + return g_object_new ( + CAMEL_TYPE_IMAPX_SERVER, + "store", store, NULL); +} -static void -imapx_command_expunge_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) +CamelIMAPXStore * +camel_imapx_server_ref_store (CamelIMAPXServer *server) { - CamelIMAPXJob *job; - CamelIMAPXMailbox *mailbox; - CamelFolder *folder; - GError *local_error = NULL; - - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_if_fail (mailbox != NULL); - - folder = imapx_server_ref_folder (is, mailbox); - g_return_if_fail (folder != NULL); - - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error expunging message")); - camel_imapx_job_take_error (job, local_error); - - } else { - GPtrArray *uids; - CamelStore *parent_store; - const gchar *full_name; - - full_name = camel_folder_get_full_name (folder); - parent_store = camel_folder_get_parent_store (folder); - - camel_folder_summary_lock (folder->summary); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), NULL); - camel_folder_summary_save_to_db (folder->summary, NULL); - uids = camel_db_get_folder_deleted_uids (parent_store->cdb_r, full_name, NULL); + return g_weak_ref_get (&server->priv->store); +} - if (uids && uids->len) { - CamelFolderChangeInfo *changes; - GList *removed = NULL; - gint i; +CamelIMAPXSettings * +camel_imapx_server_ref_settings (CamelIMAPXServer *server) +{ + CamelIMAPXStore *store; + CamelSettings *settings; - changes = camel_folder_change_info_new (); - for (i = 0; i < uids->len; i++) { - gchar *uid = uids->pdata[i]; - CamelMessageInfo *mi; + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), NULL); - mi = camel_folder_summary_peek_loaded (folder->summary, uid); - if (mi) { - camel_folder_summary_remove (folder->summary, mi); - camel_message_info_unref (mi); - } else { - camel_folder_summary_remove_uid (folder->summary, uid); - } + store = camel_imapx_server_ref_store (server); + settings = camel_service_ref_settings (CAMEL_SERVICE (store)); + g_object_unref (store); - camel_folder_change_info_remove_uid (changes, uids->pdata[i]); - removed = g_list_prepend (removed, (gpointer) uids->pdata[i]); - } + return CAMEL_IMAPX_SETTINGS (settings); +} - camel_folder_summary_save_to_db (folder->summary, NULL); - camel_folder_changed (folder, changes); - camel_folder_change_info_free (changes); +/** + * camel_imapx_server_ref_input_stream: + * @is: a #CamelIMAPXServer + * + * Returns the #GInputStream for @is, which is owned by either a + * #GTcpConnection or a #GSubprocess. If the #CamelIMAPXServer is not + * yet connected or has lost its connection, the function returns %NULL. + * + * The returned #GInputStream is referenced for thread-safety and must + * be unreferenced with g_object_unref() when finished with it. + * + * Returns: a #GInputStream, or %NULL + * + * Since: 3.12 + **/ +GInputStream * +camel_imapx_server_ref_input_stream (CamelIMAPXServer *is) +{ + GInputStream *input_stream = NULL; - g_list_free (removed); - g_ptr_array_foreach (uids, (GFunc) camel_pstring_free, NULL); - } + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); - if (uids) - g_ptr_array_free (uids, TRUE); + g_mutex_lock (&is->priv->stream_lock); - camel_folder_summary_unlock (folder->summary); - } + if (is->priv->input_stream != NULL) + input_stream = g_object_ref (is->priv->input_stream); - g_object_unref (folder); - g_object_unref (mailbox); + g_mutex_unlock (&is->priv->stream_lock); - imapx_unregister_job (is, job); + return input_stream; } -static gboolean -imapx_job_expunge_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) +/** + * camel_imapx_server_ref_output_stream: + * @is: a #CamelIMAPXServer + * + * Returns the #GOutputStream for @is, which is owned by either a + * #GTcpConnection or a #GSubprocess. If the #CamelIMAPXServer is not + * yet connected or has lost its connection, the function returns %NULL. + * + * The returned #GOutputStream is referenced for thread-safety and must + * be unreferenced with g_object_unref() when finished with it. + * + * Returns: a #GOutputStream, or %NULL + * + * Since: 3.12 + **/ +GOutputStream * +camel_imapx_server_ref_output_stream (CamelIMAPXServer *is) { - CamelIMAPXCommand *ic; - CamelIMAPXMailbox *mailbox; - gboolean success; - - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_val_if_fail (mailbox != NULL, FALSE); - - success = imapx_server_sync_changes ( - is, mailbox, job->type, job->pri, cancellable, error); + GOutputStream *output_stream = NULL; - if (success) { - /* TODO handle UIDPLUS capability */ - ic = camel_imapx_command_new ( - is, "EXPUNGE", mailbox, "EXPUNGE"); - camel_imapx_command_set_job (ic, job); - ic->pri = job->pri; - ic->complete = imapx_command_expunge_done; + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); - imapx_command_queue (is, ic); + g_mutex_lock (&is->priv->stream_lock); - camel_imapx_command_unref (ic); - } + if (is->priv->output_stream != NULL) + output_stream = g_object_ref (is->priv->output_stream); - g_object_unref (mailbox); + g_mutex_unlock (&is->priv->stream_lock); - return success; + return output_stream; } -static gboolean -imapx_job_expunge_matches (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox, - const gchar *uid) +/** + * camel_imapx_server_ref_selected: + * @is: a #CamelIMAPXServer + * + * Returns the #CamelIMAPXMailbox representing the currently selected + * mailbox (or mailbox being selected if a SELECT + * command is in progress) on the IMAP server, or %NULL if no mailbox + * is currently selected or being selected on the server. + * + * The returned #CamelIMAPXMailbox is reference for thread-safety and + * should be unreferenced with g_object_unref() when finished with it. + * + * Returns: a #CamelIMAPXMailbox, or %NULL + * + * Since: 3.12 + **/ +CamelIMAPXMailbox * +camel_imapx_server_ref_selected (CamelIMAPXServer *is) { - return camel_imapx_job_has_mailbox (job, mailbox); -} + CamelIMAPXMailbox *mailbox; -/* ********************************************************************** */ + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); -static void -imapx_command_list_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - GError *local_error = NULL; + g_mutex_lock (&is->priv->select_lock); - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); + mailbox = g_weak_ref_get (&is->priv->select_mailbox); + if (mailbox == NULL) + mailbox = g_weak_ref_get (&is->priv->select_pending); - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error fetching folders")); - camel_imapx_job_take_error (job, local_error); - } + g_mutex_unlock (&is->priv->select_lock); - e (is->tagprefix, "==== list or lsub completed ==== \n"); - imapx_unregister_job (is, job); + return mailbox; } -static void -imapx_command_list_lsub (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) +/* Some untagged responses updated pending SELECT mailbox, not the currently + selected or closing one, thus use this function instead. */ +CamelIMAPXMailbox * +camel_imapx_server_ref_pending_or_selected (CamelIMAPXServer *is) { - CamelIMAPXJob *job; - ListData *data; - GError *local_error = NULL; - - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); + CamelIMAPXMailbox *mailbox; - data = camel_imapx_job_get_data (job); - g_return_if_fail (data != NULL); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error fetching folders")); - camel_imapx_job_take_error (job, local_error); - imapx_unregister_job (is, job); + g_mutex_lock (&is->priv->select_lock); - } else { - ic = camel_imapx_command_new ( - is, "LIST", NULL, - "LSUB \"\" %s", - data->pattern); - - ic->pri = job->pri; - camel_imapx_command_set_job (ic, job); - ic->complete = imapx_command_list_done; + mailbox = g_weak_ref_get (&is->priv->select_pending); + if (mailbox == NULL) + mailbox = g_weak_ref_get (&is->priv->select_mailbox); - imapx_command_queue (is, ic); + g_mutex_unlock (&is->priv->select_lock); - camel_imapx_command_unref (ic); - } + return mailbox; } -static gboolean -imapx_job_list_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) +gboolean +camel_imapx_server_mailbox_selected (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox) { - CamelIMAPXCommand *ic; - ListData *data; - - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); - - if (is->priv->list_return_opts != NULL) { - ic = camel_imapx_command_new ( - is, "LIST", NULL, - "LIST \"\" %s RETURN (%t)", - data->pattern, - is->priv->list_return_opts); - ic->complete = imapx_command_list_done; - } else { - ic = camel_imapx_command_new ( - is, "LIST", NULL, - "LIST \"\" %s", - data->pattern); - ic->complete = imapx_command_list_lsub; - } - - ic->pri = job->pri; - camel_imapx_command_set_job (ic, job); - - imapx_command_queue (is, ic); + CamelIMAPXMailbox *selected_mailbox; + gboolean res; - camel_imapx_command_unref (ic); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - return TRUE; -} + g_mutex_lock (&is->priv->select_lock); + selected_mailbox = g_weak_ref_get (&is->priv->select_mailbox); + res = selected_mailbox == mailbox; + g_clear_object (&selected_mailbox); + g_mutex_unlock (&is->priv->select_lock); -static gboolean -imapx_job_list_matches (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox, - const gchar *uid) -{ - return TRUE; /* matches everything */ + return res; } -/* ********************************************************************** */ - -static void -imapx_command_create_mailbox_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) +gboolean +camel_imapx_server_ensure_selected_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) { - CamelIMAPXJob *job; - GError *local_error = NULL; + CamelIMAPXCommand *ic; + CamelIMAPXMailbox *selected_mailbox; + gboolean success; - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error creating folder")); - camel_imapx_job_take_error (job, local_error); - } + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; - imapx_unregister_job (is, job); -} + g_mutex_lock (&is->priv->select_lock); + selected_mailbox = g_weak_ref_get (&is->priv->select_mailbox); + if (selected_mailbox == mailbox) { + gboolean request_noop; + gint change_stamp; -static gboolean -imapx_job_create_mailbox_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXCommand *ic; - MailboxData *data; + change_stamp = selected_mailbox ? camel_imapx_mailbox_get_change_stamp (selected_mailbox) : 0; + request_noop = selected_mailbox && is->priv->last_selected_mailbox_change_stamp != change_stamp; - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); + if (request_noop) + is->priv->last_selected_mailbox_change_stamp = change_stamp; - ic = camel_imapx_command_new ( - is, "CREATE", NULL, "CREATE %m", - data->mailbox_name); - ic->pri = job->pri; - camel_imapx_command_set_job (ic, job); - ic->complete = imapx_command_create_mailbox_done; + g_mutex_unlock (&is->priv->select_lock); + g_clear_object (&selected_mailbox); - imapx_command_queue (is, ic); + if (request_noop) { + c (is->priv->tagprefix, "%s: Selected mailbox '%s' changed, do NOOP instead\n", G_STRFUNC, camel_imapx_mailbox_get_name (mailbox)); - camel_imapx_command_unref (ic); + return camel_imapx_server_noop_sync (is, mailbox, cancellable, error); + } - return TRUE; -} + return TRUE; + } -/* ********************************************************************** */ + g_clear_object (&selected_mailbox); -static void -imapx_command_delete_mailbox_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - MailboxData *data; - GError *local_error = NULL; + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_SELECT, "SELECT %M", mailbox); - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); + if (is->priv->use_qresync) { + CamelFolder *folder; - data = camel_imapx_job_get_data (job); - g_return_if_fail (data != NULL); + folder = imapx_server_ref_folder (is, mailbox); + camel_imapx_command_add_qresync_parameter (ic, folder); + g_clear_object (&folder); + } - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error deleting folder")); - camel_imapx_job_take_error (job, local_error); + g_weak_ref_set (&is->priv->select_pending, mailbox); + g_mutex_unlock (&is->priv->select_lock); - } else { - CamelIMAPXStore *imapx_store; + success = camel_imapx_server_process_command_sync (is, ic, _("Failed to select mailbox"), cancellable, error); - /* Perform the same processing as imapx_untagged_list() - * would if the server notified us of a deleted mailbox. */ + camel_imapx_command_unref (ic); - imapx_store = camel_imapx_server_ref_store (is); + g_mutex_lock (&is->priv->select_lock); - camel_imapx_mailbox_deleted (data->mailbox); - camel_imapx_store_emit_mailbox_updated (imapx_store, data->mailbox); + g_weak_ref_set (&is->priv->select_pending, NULL); - g_clear_object (&imapx_store); + if (success) { + is->priv->state = IMAPX_SELECTED; + is->priv->last_selected_mailbox_change_stamp = camel_imapx_mailbox_get_change_stamp (mailbox); + g_weak_ref_set (&is->priv->select_mailbox, mailbox); + } else { + is->priv->state = IMAPX_INITIALISED; + is->priv->last_selected_mailbox_change_stamp = 0; + g_weak_ref_set (&is->priv->select_mailbox, NULL); } - imapx_unregister_job (is, job); + g_mutex_unlock (&is->priv->select_lock); + + return success; } -static gboolean -imapx_job_delete_mailbox_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) +gboolean +camel_imapx_server_process_command_sync (CamelIMAPXServer *is, + CamelIMAPXCommand *ic, + const gchar *error_prefix, + GCancellable *cancellable, + GError **error) { - CamelIMAPXStore *imapx_store; - CamelIMAPXCommand *ic; - MailboxData *data; - CamelIMAPXMailbox *inbox; - - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); - - imapx_store = camel_imapx_server_ref_store (is); - /* Keep going, even if this returns NULL. */ - inbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX"); - g_clear_object (&imapx_store); + CamelIMAPXCommandPart *cp; + GInputStream *input_stream = NULL; + GOutputStream *output_stream = NULL; + gboolean cp_literal_plus; + GList *head; + gchar *string; + gboolean success = FALSE; + GError *local_error = NULL; - /* Make sure the to-be-deleted folder is not - * selected by selecting INBOX for this operation. */ - ic = camel_imapx_command_new ( - is, "DELETE", inbox, - "DELETE %M", data->mailbox); - ic->pri = job->pri; - camel_imapx_command_set_job (ic, job); - ic->complete = imapx_command_delete_mailbox_done; + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic), FALSE); - imapx_command_queue (is, ic); + camel_imapx_command_close (ic); + if (ic->status) { + imapx_free_status (ic->status); + ic->status = NULL; + } + ic->completed = FALSE; - camel_imapx_command_unref (ic); + head = g_queue_peek_head_link (&ic->parts); + g_return_val_if_fail (head != NULL, FALSE); + cp = (CamelIMAPXCommandPart *) head->data; + ic->current_part = head; - g_clear_object (&inbox); + if (g_cancellable_set_error_if_cancelled (cancellable, &local_error)) { + if (error_prefix && local_error) + g_prefix_error (&local_error, "%s: ", error_prefix); - return TRUE; -} + if (local_error) + g_propagate_error (error, local_error); -/* ********************************************************************** */ + return FALSE; + } -static void -imapx_command_rename_mailbox_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - MailboxData *data; - GError *local_error = NULL; + cp_literal_plus = ((cp->type & CAMEL_IMAPX_COMMAND_LITERAL_PLUS) != 0); - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); + COMMAND_LOCK (is); - data = camel_imapx_job_get_data (job); - g_return_if_fail (data != NULL); + if (is->priv->current_command != NULL) { + g_warning ("%s: [%c] %p: Starting command %p (%s) while still processing %p (%s)", G_STRFUNC, + is->priv->tagprefix, is, ic, camel_imapx_job_get_kind_name (ic->job_kind), + is->priv->current_command, camel_imapx_job_get_kind_name (is->priv->current_command->job_kind)); + } - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error renaming folder")); - camel_imapx_job_take_error (job, local_error); + if (g_cancellable_set_error_if_cancelled (cancellable, &local_error)) { + c (is->priv->tagprefix, "%s: command %p (%s) cancelled\n", G_STRFUNC, ic, camel_imapx_job_get_kind_name (ic->job_kind)); - } else { - CamelIMAPXStore *imapx_store; + COMMAND_UNLOCK (is); - /* Perform the same processing as imapx_untagged_list() - * would if the server notified us of a renamed mailbox. */ + if (error_prefix && local_error) + g_prefix_error (&local_error, "%s: ", error_prefix); - imapx_store = camel_imapx_server_ref_store (is); - camel_imapx_store_handle_mailbox_rename (imapx_store, data->mailbox, data->mailbox_name); + if (local_error) + g_propagate_error (error, local_error); - g_clear_object (&imapx_store); + return FALSE; } - imapx_unregister_job (is, job); -} + c (is->priv->tagprefix, "%s: %p (%s) ~> %p (%s)\n", G_STRFUNC, is->priv->current_command, + is->priv->current_command ? camel_imapx_job_get_kind_name (is->priv->current_command->job_kind) : "", + ic, camel_imapx_job_get_kind_name (ic->job_kind)); -static gboolean -imapx_job_rename_mailbox_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXCommand *ic; - CamelIMAPXStore *imapx_store; - CamelIMAPXMailbox *inbox; - MailboxData *data; + is->priv->current_command = ic; + is->priv->continuation_command = ic; - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); + COMMAND_UNLOCK (is); - imapx_store = camel_imapx_server_ref_store (is); - inbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX"); - g_clear_object (&imapx_store); - g_return_val_if_fail (inbox != NULL, FALSE); + input_stream = camel_imapx_server_ref_input_stream (is); + output_stream = camel_imapx_server_ref_output_stream (is); - camel_imapx_job_set_mailbox (job, inbox); + if (output_stream == NULL) { + local_error = g_error_new_literal ( + CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT, + _("Cannot issue command, no stream available")); + goto exit; + } + + c ( + is->priv->tagprefix, + "Starting command (%s) %c%05u %s\r\n", + is->priv->current_command ? " literal" : "", + is->priv->tagprefix, + ic->tag, + cp->data && g_str_has_prefix (cp->data, "LOGIN") ? + "LOGIN..." : cp->data); - ic = camel_imapx_command_new ( - is, "RENAME", inbox, "RENAME %M %m", - data->mailbox, data->mailbox_name); - ic->pri = job->pri; - camel_imapx_command_set_job (ic, job); - ic->complete = imapx_command_rename_mailbox_done; + if (ic->job_kind == CAMEL_IMAPX_JOB_DONE) + string = g_strdup_printf ("%s\r\n", cp->data); + else + string = g_strdup_printf ("%c%05u %s\r\n", is->priv->tagprefix, ic->tag, cp->data); + g_mutex_lock (&is->priv->stream_lock); + success = g_output_stream_write_all ( + output_stream, string, strlen (string), + NULL, cancellable, &local_error); + g_mutex_unlock (&is->priv->stream_lock); + g_free (string); - imapx_command_queue (is, ic); + if (local_error != NULL || !success) + goto exit; - camel_imapx_command_unref (ic); + while (is->priv->continuation_command == ic && cp_literal_plus) { + /* Sent LITERAL+ continuation immediately */ + imapx_continuation ( + is, input_stream, output_stream, + TRUE, cancellable, &local_error); + if (local_error != NULL) + goto exit; + } - g_object_unref (inbox); + while (success && !ic->completed) + success = imapx_step (is, input_stream, output_stream, cancellable, &local_error); - return TRUE; -} + imapx_server_reset_inactivity_timer (is); -/* ********************************************************************** */ + exit: -static void -imapx_command_subscribe_mailbox_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - MailboxData *data; - GError *local_error = NULL; + COMMAND_LOCK (is); - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); + if (is->priv->current_command == ic) { + c (is->priv->tagprefix, "%s: %p ~> %p; success:%d local-error:%s result:%s status-text:'%s'\n", G_STRFUNC, + is->priv->current_command, NULL, success, local_error ? local_error->message : "[null]", + ic->status ? ( + ic->status->result == IMAPX_OK ? "OK" : + ic->status->result == IMAPX_NO ? "NO" : + ic->status->result == IMAPX_BAD ? "BAD" : + ic->status->result == IMAPX_PREAUTH ? "PREAUTH" : + ic->status->result == IMAPX_BYE ? "BYE" : "???") : "[null]", + ic->status ? ic->status->text : "[null]"); - data = camel_imapx_job_get_data (job); - g_return_if_fail (data != NULL); + is->priv->current_command = NULL; + is->priv->continuation_command = NULL; + } else { + c (is->priv->tagprefix, "%s: current command:%p doesn't match passed-in command:%p success:%d local-error:%s result:%s status-text:'%s'\n", G_STRFUNC, + is->priv->current_command, ic, success, local_error ? local_error->message : "[null]", + ic->status ? ( + ic->status->result == IMAPX_OK ? "OK" : + ic->status->result == IMAPX_NO ? "NO" : + ic->status->result == IMAPX_BAD ? "BAD" : + ic->status->result == IMAPX_PREAUTH ? "PREAUTH" : + ic->status->result == IMAPX_BYE ? "BYE" : "???") : "[null]", + ic->status ? ic->status->text : "[null]"); + } - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error subscribing to folder")); - camel_imapx_job_take_error (job, local_error); + COMMAND_UNLOCK (is); - } else { - CamelIMAPXStore *imapx_store; + /* Server reported error. */ + if (success && ic->status && ic->status->result != IMAPX_OK) { + g_set_error ( + &local_error, CAMEL_ERROR, + CAMEL_ERROR_GENERIC, + "%s", ic->status->text); + } - /* Perform the same processing as imapx_untagged_list() - * would if the server notified us of a subscription. */ + if (local_error) { + /* Sadly, G_IO_ERROR_FAILED is also used for 'Connection reset by peer' error; + since GLib 2.44 is used G_IO_ERROR_CONNECTION_CLOSED, which is the same as G_IO_ERROR_BROKEN_PIPE */ + if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_FAILED) || + g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE) || + g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) { + local_error->domain = CAMEL_IMAPX_SERVER_ERROR; + local_error->code = CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT; + } - imapx_store = camel_imapx_server_ref_store (is); + if (error_prefix && local_error) + g_prefix_error (&local_error, "%s: ", error_prefix); - camel_imapx_mailbox_subscribed (data->mailbox); - camel_imapx_store_emit_mailbox_updated (imapx_store, data->mailbox); + g_propagate_error (error, local_error); - g_clear_object (&imapx_store); + success = FALSE; } - imapx_unregister_job (is, job); + g_clear_object (&input_stream); + g_clear_object (&output_stream); + + return success; } -static gboolean -imapx_job_subscribe_mailbox_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) +static void +imapx_disconnect (CamelIMAPXServer *is) { - CamelIMAPXCommand *ic; - MailboxData *data; + g_cancellable_cancel (is->priv->cancellable); - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); + g_mutex_lock (&is->priv->stream_lock); - ic = camel_imapx_command_new ( - is, "SUBSCRIBE", NULL, - "SUBSCRIBE %M", data->mailbox); + if (is->priv->connection) { + /* No need to wait for close for too long */ + imapx_server_set_connection_timeout (is->priv->connection, 3); + } - ic->pri = job->pri; - camel_imapx_command_set_job (ic, job); - ic->complete = imapx_command_subscribe_mailbox_done; + g_clear_object (&is->priv->input_stream); + g_clear_object (&is->priv->output_stream); + g_clear_object (&is->priv->connection); + g_clear_object (&is->priv->subprocess); - imapx_command_queue (is, ic); + if (is->priv->cinfo) { + imapx_free_capability (is->priv->cinfo); + is->priv->cinfo = NULL; + } - camel_imapx_command_unref (ic); + g_mutex_unlock (&is->priv->stream_lock); - return TRUE; -} + g_mutex_lock (&is->priv->select_lock); + is->priv->last_selected_mailbox_change_stamp = 0; + g_weak_ref_set (&is->priv->select_mailbox, NULL); + g_weak_ref_set (&is->priv->select_pending, NULL); + g_mutex_unlock (&is->priv->select_lock); -/* ********************************************************************** */ + is->priv->is_cyrus = FALSE; + is->priv->state = IMAPX_DISCONNECTED; -static void -imapx_command_unsubscribe_mailbox_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - MailboxData *data; - GError *local_error = NULL; + g_mutex_lock (&is->priv->idle_lock); + is->priv->idle_state = IMAPX_IDLE_STATE_OFF; + g_cond_broadcast (&is->priv->idle_cond); + g_mutex_unlock (&is->priv->idle_lock); +} - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); +/* Client commands */ +gboolean +camel_imapx_server_connect_sync (CamelIMAPXServer *is, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - data = camel_imapx_job_get_data (job); - g_return_if_fail (data != NULL); + if (is->priv->state == IMAPX_SHUTDOWN) { + g_set_error ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_UNAVAILABLE, + "Shutting down"); + return FALSE; + } - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error unsubscribing from folder")); - camel_imapx_job_take_error (job, local_error); + if (is->priv->state >= IMAPX_INITIALISED) + return TRUE; - } else { - CamelIMAPXStore *imapx_store; + is->priv->is_cyrus = FALSE; - /* Perform the same processing as imapx_untagged_list() - * would if the server notified us of an unsubscription. */ + if (!imapx_reconnect (is, cancellable, error)) + return FALSE; - imapx_store = camel_imapx_server_ref_store (is); + g_mutex_lock (&is->priv->stream_lock); - camel_imapx_mailbox_unsubscribed (data->mailbox); - camel_imapx_store_emit_mailbox_updated (imapx_store, data->mailbox); + if (CAMEL_IMAPX_LACK_CAPABILITY (is->priv->cinfo, NAMESPACE)) { + g_mutex_unlock (&is->priv->stream_lock); - g_clear_object (&imapx_store); + /* This also creates a needed faux NAMESPACE */ + if (!camel_imapx_server_list_sync (is, "INBOX", 0, cancellable, error)) + return FALSE; + } else { + g_mutex_unlock (&is->priv->stream_lock); } - imapx_unregister_job (is, job); + return TRUE; } -static gboolean -imapx_job_unsubscribe_mailbox_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) +gboolean +camel_imapx_server_disconnect_sync (CamelIMAPXServer *is, + GCancellable *cancellable, + GError **error) { - CamelIMAPXCommand *ic; - MailboxData *data; + GCancellable *idle_cancellable; + gboolean success = TRUE; - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - ic = camel_imapx_command_new ( - is, "UNSUBSCRIBE", NULL, - "UNSUBSCRIBE %M", data->mailbox); + g_mutex_lock (&is->priv->idle_lock); + idle_cancellable = is->priv->idle_cancellable; + if (idle_cancellable) + g_object_ref (idle_cancellable); + g_mutex_unlock (&is->priv->idle_lock); + + if (idle_cancellable) + g_cancellable_cancel (idle_cancellable); + g_clear_object (&idle_cancellable); - ic->pri = job->pri; - camel_imapx_command_set_job (ic, job); - ic->complete = imapx_command_unsubscribe_mailbox_done; + g_mutex_lock (&is->priv->stream_lock); + if (is->priv->connection) { + /* No need to wait for close for too long */ + imapx_server_set_connection_timeout (is->priv->connection, 3); + } + g_mutex_unlock (&is->priv->stream_lock); - imapx_command_queue (is, ic); + /* Ignore errors here. */ + camel_imapx_server_stop_idle_sync (is, cancellable, NULL); - camel_imapx_command_unref (ic); + g_mutex_lock (&is->priv->stream_lock); + if (is->priv->connection) + success = g_io_stream_close (is->priv->connection, cancellable, error); + g_mutex_unlock (&is->priv->stream_lock); - return TRUE; -} + imapx_disconnect (is); -/* ********************************************************************** */ + return success; +} -static void -imapx_command_update_quota_info_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) +gboolean +camel_imapx_server_query_auth_types_sync (CamelIMAPXServer *is, + GCancellable *cancellable, + GError **error) { - CamelIMAPXJob *job; - GError *local_error = NULL; - - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); - - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error retrieving quota information")); - camel_imapx_job_take_error (job, local_error); - } + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - imapx_unregister_job (is, job); + return imapx_connect_to_server (is, cancellable, error); } -static gboolean -imapx_job_update_quota_info_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) +CamelStream * +camel_imapx_server_get_message_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + CamelFolderSummary *summary, + CamelDataCache *message_cache, + const gchar *message_uid, + GCancellable *cancellable, + GError **error) { - CamelIMAPXCommand *ic; - CamelIMAPXMailbox *mailbox; + CamelMessageInfo *mi; + CamelStream *result_stream = NULL; + CamelIMAPXSettings *settings; + GIOStream *cache_stream; + gsize data_size; + gboolean use_multi_fetch; + gboolean success, retrying = FALSE; + GError *local_error = NULL; - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_val_if_fail (mailbox != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), NULL); + g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL); + g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), NULL); + g_return_val_if_fail (message_uid != NULL, NULL); - ic = camel_imapx_command_new ( - is, "GETQUOTAROOT", NULL, - "GETQUOTAROOT %M", mailbox); - ic->pri = job->pri; - camel_imapx_command_set_job (ic, job); - ic->complete = imapx_command_update_quota_info_done; + if (!camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error)) + return NULL; - imapx_command_queue (is, ic); + mi = camel_folder_summary_get (summary, message_uid); + if (mi == NULL) { + g_set_error ( + error, CAMEL_FOLDER_ERROR, + CAMEL_FOLDER_ERROR_INVALID_UID, + _("Cannot get message with message ID %s: %s"), + message_uid, _("No such message available.")); + return NULL; + } - camel_imapx_command_unref (ic); + /* This makes sure that if any file is left on the disk, it is not reused. + That can happen when the previous message download had been cancelled + or finished with an error. */ + camel_data_cache_remove (message_cache, "tmp", message_uid, NULL); - g_clear_object (&mailbox); + cache_stream = camel_data_cache_add (message_cache, "tmp", message_uid, error); + if (cache_stream == NULL) { + camel_message_info_unref (mi); + return NULL; + } - return TRUE; -} + settings = camel_imapx_server_ref_settings (is); + data_size = ((CamelMessageInfoBase *) mi)->size; + use_multi_fetch = data_size > MULTI_SIZE && camel_imapx_settings_get_use_multi_fetch (settings); + g_object_unref (settings); -/* ********************************************************************** */ + g_warn_if_fail (is->priv->get_message_stream == NULL); -static void -imapx_command_uid_search_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - SearchData *data; - GError *local_error = NULL; + is->priv->get_message_stream = cache_stream; - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); + try_again: + if (use_multi_fetch) { + CamelIMAPXCommand *ic; + gsize fetch_offset = 0; - data = camel_imapx_job_get_data (job); - g_return_if_fail (data != NULL); + do { + camel_operation_progress (cancellable, fetch_offset * 100 / data_size); - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error (&local_error, "%s: ", _("Search failed")); - camel_imapx_job_take_error (job, local_error); - } + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_GET_MESSAGE, "UID FETCH %t (BODY.PEEK[]", message_uid); + camel_imapx_command_add (ic, "<%u.%u>", fetch_offset, MULTI_SIZE); + camel_imapx_command_add (ic, ")"); + fetch_offset += MULTI_SIZE; - /* Don't worry about the success state and presence of search - * results not agreeing here. camel_imapx_server_uid_search() - * will disregard the search results if an error occurred. */ - g_mutex_lock (&is->priv->search_results_lock); - data->results = is->priv->search_results; - is->priv->search_results = NULL; - g_mutex_unlock (&is->priv->search_results_lock); + success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching message"), cancellable, &local_error); - imapx_unregister_job (is, job); -} + camel_imapx_command_unref (ic); + ic = NULL; -static gboolean -imapx_job_uid_search_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXCommand *ic; - CamelIMAPXMailbox *mailbox; - SearchData *data; + if (success) { + gsize really_fetched = g_seekable_tell (G_SEEKABLE (is->priv->get_message_stream)); - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); + /* Don't automatically stop when we reach the reported message + * size -- some crappy servers (like Microsoft Exchange) have + * a tendency to lie about it. Keep going (one request at a + * time) until the data actually stop coming. */ + if (fetch_offset < data_size || + fetch_offset == really_fetched) { + /* just continue */ + } else { + break; + } + } + } while (success); + } else { + CamelIMAPXCommand *ic; - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_val_if_fail (mailbox != NULL, FALSE); + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_GET_MESSAGE, "UID FETCH %t (BODY.PEEK[])", message_uid); - ic = camel_imapx_command_new ( - is, "UID SEARCH", mailbox, - "UID SEARCH %t", data->criteria); - ic->pri = job->pri; - camel_imapx_command_set_job (ic, job); - ic->complete = imapx_command_uid_search_done; + success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching message"), cancellable, &local_error); - imapx_command_queue (is, ic); + camel_imapx_command_unref (ic); + } - camel_imapx_command_unref (ic); + if (success && !retrying && !g_seekable_tell (G_SEEKABLE (is->priv->get_message_stream))) { + /* Nothing had been read from the server. Maybe this connection + doesn't know about the message on the server side yet, thus + invoke NOOP and retry. */ + CamelIMAPXCommand *ic; - g_object_unref (mailbox); + retrying = TRUE; - return TRUE; -} + c (is->priv->tagprefix, "%s: Returned no message data, retrying after NOOP\n", G_STRFUNC); -/* ********************************************************************** */ + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NOOP, "NOOP"); -static void -imapx_command_noop_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - GError *local_error = NULL; + success = camel_imapx_server_process_command_sync (is, ic, _("Error performing NOOP"), cancellable, &local_error); - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); + camel_imapx_command_unref (ic); - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error performing NOOP")); - camel_imapx_job_take_error (job, local_error); + if (success) + goto try_again; } - imapx_unregister_job (is, job); -} + is->priv->get_message_stream = NULL; -static gboolean -imapx_job_noop_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXCommand *ic; - CamelIMAPXMailbox *mailbox; + if (success) { + if (local_error == NULL) { + g_io_stream_close (cache_stream, cancellable, &local_error); + g_prefix_error ( + &local_error, "%s: ", + _("Failed to close the tmp stream")); + } - /* This may be NULL. */ - mailbox = camel_imapx_job_ref_mailbox (job); + if (local_error == NULL && + g_cancellable_set_error_if_cancelled (cancellable, &local_error)) { + g_prefix_error ( + &local_error, "%s: ", + _("Error fetching message")); + } - ic = camel_imapx_command_new ( - is, "NOOP", mailbox, "NOOP"); + if (local_error == NULL) { + gchar *cur_filename; + gchar *tmp_filename; + gchar *dirname; - camel_imapx_command_set_job (ic, job); - ic->complete = imapx_command_noop_done; - if (mailbox != NULL) - ic->pri = IMAPX_PRIORITY_REFRESH_INFO; - else - ic->pri = IMAPX_PRIORITY_NOOP; + cur_filename = camel_data_cache_get_filename (message_cache, "cur", message_uid); + tmp_filename = camel_data_cache_get_filename (message_cache, "tmp", message_uid); - imapx_command_queue (is, ic); + dirname = g_path_get_dirname (cur_filename); + g_mkdir_with_parents (dirname, 0700); + g_free (dirname); - camel_imapx_command_unref (ic); + if (g_rename (tmp_filename, cur_filename) == 0) { + /* Exchange the "tmp" stream for the "cur" stream. */ + g_clear_object (&cache_stream); + cache_stream = camel_data_cache_get (message_cache, "cur", message_uid, &local_error); + } else { + g_set_error ( + &local_error, G_FILE_ERROR, + g_file_error_from_errno (errno), + "%s: %s", + _("Failed to copy the tmp file"), + g_strerror (errno)); + } + + g_free (cur_filename); + g_free (tmp_filename); + } + + /* Delete the 'tmp' file only if the operation succeeded. It's because + cancelled operations end before they are properly finished (IMAP-protocol speaking), + thus if any other GET_MESSAGE operation was waiting for this job, then it + realized that the message was not downloaded and opened its own "tmp" file, but + of the same name, thus this remove would drop file which could be used + by a different GET_MESSAGE job. */ + if (!local_error && !g_cancellable_is_cancelled (cancellable)) + camel_data_cache_remove (message_cache, "tmp", message_uid, NULL); + } - g_clear_object (&mailbox); + if (!local_error) { + result_stream = camel_stream_new (cache_stream); + } else { + g_propagate_error (error, local_error); + } - return TRUE; + g_clear_object (&cache_stream); + + return result_stream; } -/* ********************************************************************** */ +gboolean +camel_imapx_server_sync_message_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + CamelFolderSummary *summary, + CamelDataCache *message_cache, + const gchar *message_uid, + GCancellable *cancellable, + GError **error) +{ + gchar *cache_file = NULL; + gboolean is_cached; + struct stat st; + gboolean success = TRUE; -/* FIXME: this is basically a copy of the same in camel-imapx-utils.c */ -static struct { - const gchar *name; - guint32 flag; -} flags_table[] = { - { "\\ANSWERED", CAMEL_MESSAGE_ANSWERED }, - { "\\DELETED", CAMEL_MESSAGE_DELETED }, - { "\\DRAFT", CAMEL_MESSAGE_DRAFT }, - { "\\FLAGGED", CAMEL_MESSAGE_FLAGGED }, - { "\\SEEN", CAMEL_MESSAGE_SEEN }, - { "\\RECENT", CAMEL_IMAPX_MESSAGE_RECENT }, - { "JUNK", CAMEL_MESSAGE_JUNK }, - { "NOTJUNK", CAMEL_MESSAGE_NOTJUNK } -}; + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE); + g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), FALSE); + g_return_val_if_fail (message_uid != NULL, FALSE); -/* - * flags 00101000 - * sflags 01001000 - * ^ 01100000 - * ~flags 11010111 - * & 01000000 - * - * &flags 00100000 - */ + /* Check if the cache file already exists and is non-empty. */ + cache_file = camel_data_cache_get_filename (message_cache, "cur", message_uid); + is_cached = (g_stat (cache_file, &st) == 0 && st.st_size > 0); + g_free (cache_file); -static void -imapx_command_sync_changes_done (CamelIMAPXServer *is, - CamelIMAPXCommand *ic) -{ - CamelIMAPXJob *job; - CamelIMAPXMailbox *mailbox; - CamelFolder *folder; - CamelStore *parent_store; - SyncChangesData *data; - const gchar *full_name; - GError *local_error = NULL; + if (!is_cached) { + CamelStream *stream; - job = camel_imapx_command_get_job (ic); - g_return_if_fail (CAMEL_IS_IMAPX_JOB (job)); + stream = camel_imapx_server_get_message_sync ( + is, mailbox, summary, + message_cache, message_uid, + cancellable, error); - data = camel_imapx_job_get_data (job); - g_return_if_fail (data != NULL); + success = (stream != NULL); - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_if_fail (mailbox != NULL); + g_clear_object (&stream); + } - folder = imapx_server_ref_folder (is, mailbox); - g_return_if_fail (folder != NULL); + return success; +} - g_atomic_int_add (&job->commands, -1); +gboolean +camel_imapx_server_copy_message_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + CamelIMAPXMailbox *destination, + GPtrArray *uids, + gboolean delete_originals, + gboolean remove_deleted_flags, + GCancellable *cancellable, + GError **error) +{ + GPtrArray *data_uids; + gint ii; + gboolean use_move_command = FALSE; + CamelIMAPXCommand *ic; + CamelFolder *folder; + GHashTable *source_infos; + gboolean remove_junk_flags; + gboolean success = TRUE; - full_name = camel_folder_get_full_name (folder); - parent_store = camel_folder_get_parent_store (folder); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (destination), FALSE); + g_return_val_if_fail (uids != NULL, FALSE); - /* If this worked, we should really just update the changes that we - * sucessfully stored, so we dont have to worry about sending them - * again ... - * But then we'd have to track which uid's we actually updated, so - * its easier just to refresh all of the ones we got. - * - * Not that ... given all the asynchronicity going on, we're guaranteed - * that what we just set is actually what is on the server now .. but - * if it isn't, i guess we'll fix up next refresh */ - - if (camel_imapx_command_set_error_if_failed (ic, &local_error)) { - g_prefix_error ( - &local_error, "%s: ", - _("Error syncing changes")); - camel_imapx_job_take_error (job, local_error); - imapx_unregister_job (is, job); - goto exit; + /* To get permanent flags. That's okay if the "SELECT" fails here, as it can be + due to the folder being write-only; just ignore the error and continue. */ + camel_imapx_server_ensure_selected_sync (is, destination, cancellable, NULL); - /* lock cache ? */ - } else { - guint32 unseen, permanentflags; - gint i; + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; - permanentflags = camel_imapx_mailbox_get_permanentflags (mailbox); + if (!camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error)) + return FALSE; - for (i = 0; i < data->changed_uids->len; i++) { - CamelIMAPXMessageInfo *xinfo = (CamelIMAPXMessageInfo *) camel_folder_summary_get (folder->summary, - data->changed_uids->pdata[i]); + folder = imapx_server_ref_folder (is, mailbox); + g_return_val_if_fail (folder != NULL, FALSE); - if (!xinfo) - continue; + remove_deleted_flags = remove_deleted_flags || (folder->folder_flags & CAMEL_FOLDER_IS_TRASH) != 0; + remove_junk_flags = (folder->folder_flags & CAMEL_FOLDER_IS_JUNK) != 0; - xinfo->server_flags = xinfo->info.flags & CAMEL_IMAPX_SERVER_FLAGS; - if (!data->remove_deleted_flags || - !(xinfo->info.flags & CAMEL_MESSAGE_DELETED)) { - xinfo->info.flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED; - } else { - /* to stare back the \Deleted flag */ - xinfo->server_flags &= ~CAMEL_MESSAGE_DELETED; - xinfo->info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED; - } - xinfo->info.dirty = TRUE; - if ((permanentflags & CAMEL_MESSAGE_USER) != 0 || - camel_flag_list_size (&xinfo->server_user_flags) == 0) - camel_flag_list_copy (&xinfo->server_user_flags, &xinfo->info.user_flags); + /* If we're moving messages, prefer "UID MOVE" if supported. */ + if (delete_originals) { + g_mutex_lock (&is->priv->stream_lock); - camel_folder_summary_touch (folder->summary); - camel_message_info_unref (xinfo); + if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, MOVE)) { + delete_originals = FALSE; + use_move_command = TRUE; } - /* Apply the changes to server-side unread count; it won't tell - * us of these changes, of course. */ - unseen = camel_imapx_mailbox_get_unseen (mailbox); - unseen += data->unread_change; - camel_imapx_mailbox_set_unseen (mailbox, unseen); + g_mutex_unlock (&is->priv->stream_lock); } - if (g_atomic_int_get (&job->commands) == 0) { - if (folder->summary && (folder->summary->flags & CAMEL_FOLDER_SUMMARY_DIRTY) != 0) { - CamelStoreInfo *si; - - /* ... and store's summary when folder's summary is dirty */ - si = camel_store_summary_path (CAMEL_IMAPX_STORE (parent_store)->summary, full_name); - if (si) { - if (si->total != camel_folder_summary_get_saved_count (folder->summary) || - si->unread != camel_folder_summary_get_unread_count (folder->summary)) { - si->total = camel_folder_summary_get_saved_count (folder->summary); - si->unread = camel_folder_summary_get_unread_count (folder->summary); - camel_store_summary_touch (CAMEL_IMAPX_STORE (parent_store)->summary); - } - - camel_store_summary_info_unref (CAMEL_IMAPX_STORE (parent_store)->summary, si); - } - } + source_infos = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, camel_message_info_unref); + data_uids = g_ptr_array_new (); - camel_folder_summary_save_to_db (folder->summary, NULL); - camel_store_summary_save (CAMEL_IMAPX_STORE (parent_store)->summary); + for (ii = 0; ii < uids->len; ii++) { + gchar *uid = (gchar *) camel_pstring_strdup (uids->pdata[ii]); - imapx_unregister_job (is, job); - } else { - /* Make sure no command will starve in a queue */ - QUEUE_LOCK (is); - imapx_command_start_next (is); - QUEUE_UNLOCK (is); + g_ptr_array_add (data_uids, uid); + g_hash_table_insert (source_infos, uid, camel_folder_summary_get (folder->summary, uid)); } -exit: - g_object_unref (folder); - g_object_unref (mailbox); -} + g_ptr_array_sort (data_uids, (GCompareFunc) imapx_uids_array_cmp); -static gboolean -imapx_job_sync_changes_start (CamelIMAPXJob *job, - CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) -{ - SyncChangesData *data; - CamelFolder *folder; - CamelIMAPXMailbox *mailbox; - guint32 i, j, permanentflags; - struct _uidset_state ss; - GPtrArray *uids; - gint on; + ii = 0; + while (ii < data_uids->len && success) { + struct _uidset_state uidset; + gint last_index = ii; - data = camel_imapx_job_get_data (job); - g_return_val_if_fail (data != NULL, FALSE); + imapx_uidset_init (&uidset, 0, MAX_COMMAND_LEN); - mailbox = camel_imapx_job_ref_mailbox (job); - g_return_val_if_fail (mailbox != NULL, FALSE); + if (use_move_command) + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_MOVE_MESSAGE, "UID MOVE "); + else + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_COPY_MESSAGE, "UID COPY "); - folder = imapx_server_ref_folder (is, mailbox); - g_return_val_if_fail (folder != NULL, FALSE); + while (ii < data_uids->len) { + const gchar *uid = (gchar *) g_ptr_array_index (data_uids, ii); - permanentflags = camel_imapx_mailbox_get_permanentflags (mailbox); - uids = data->changed_uids; + ii++; - for (on = 0; on < 2; on++) { - guint32 orset = on ? data->on_set : data->off_set; - GArray *user_set = on ? data->on_user : data->off_user; + if (imapx_uidset_add (&uidset, ic, uid) == 1) + break; + } - for (j = 0; j < G_N_ELEMENTS (flags_table); j++) { - guint32 flag = flags_table[j].flag; - CamelIMAPXCommand *ic = NULL; + imapx_uidset_done (&uidset, ic); - if ((orset & flag) == 0) - continue; + camel_imapx_command_add (ic, " %M", destination); - c (is->tagprefix, "checking/storing %s flags '%s'\n", on?"on":"off", flags_table[j].name); - imapx_uidset_init (&ss, 0, 100); - for (i = 0; i < uids->len; i++) { - CamelIMAPXMessageInfo *info; - gboolean remove_deleted_flag; - guint32 flags; - guint32 sflags; - gint send; + imapx_free_status (is->priv->copyuid_status); + is->priv->copyuid_status = NULL; - info = (CamelIMAPXMessageInfo *) - camel_folder_summary_get ( - folder->summary, - uids->pdata[i]); + success = camel_imapx_server_process_command_sync (is, ic, + use_move_command ? _("Error moving messages") : _("Error copying messages"), + cancellable, error); - if (info == NULL) - continue; + if (success) { + struct _status_info *copyuid_status = is->priv->copyuid_status; - flags = (info->info.flags & CAMEL_IMAPX_SERVER_FLAGS) & permanentflags; - sflags = (info->server_flags & CAMEL_IMAPX_SERVER_FLAGS) & permanentflags; - send = 0; + if (ic->status && ic->status->condition == IMAPX_COPYUID) + copyuid_status = ic->status; - remove_deleted_flag = - data->remove_deleted_flags && - (flags & CAMEL_MESSAGE_DELETED); + if (copyuid_status && copyuid_status->u.copyuid.uids && + copyuid_status->u.copyuid.copied_uids && + copyuid_status->u.copyuid.uids->len == copyuid_status->u.copyuid.copied_uids->len) { + CamelFolder *destination_folder; + + destination_folder = imapx_server_ref_folder (is, destination); + if (destination_folder) { + CamelMessageInfo *source_info, *destination_info; + CamelFolderChangeInfo *changes; + gint ii; + + changes = camel_folder_change_info_new (); + + for (ii = 0; ii < copyuid_status->u.copyuid.uids->len; ii++) { + gchar *uid; + gboolean is_new = FALSE; + + uid = g_strdup_printf ("%d", g_array_index (copyuid_status->u.copyuid.uids, guint32, ii)); + source_info = g_hash_table_lookup (source_infos, uid); + g_free (uid); + + if (!source_info) + continue; + + uid = g_strdup_printf ("%d", g_array_index (copyuid_status->u.copyuid.copied_uids, guint32, ii)); + destination_info = camel_folder_summary_get (folder->summary, uid); + + if (!destination_info) { + is_new = TRUE; + destination_info = camel_message_info_clone (source_info); + destination_info->summary = destination_folder->summary; + camel_pstring_free (destination_info->uid); + destination_info->uid = camel_pstring_strdup (uid); + } + + g_free (uid); + + imapx_set_message_info_flags_for_new_message ( + destination_info, + ((CamelMessageInfoBase *) source_info)->flags, + ((CamelMessageInfoBase *) source_info)->user_flags, + TRUE, + ((CamelMessageInfoBase *) source_info)->user_tags, + camel_imapx_mailbox_get_permanentflags (destination)); + if (remove_deleted_flags) + camel_message_info_set_flags (destination_info, CAMEL_MESSAGE_DELETED, 0); + if (remove_junk_flags) + camel_message_info_set_flags (destination_info, CAMEL_MESSAGE_JUNK, 0); + if (is_new) + camel_folder_summary_add (destination_folder->summary, destination_info); + camel_folder_change_info_add_uid (changes, destination_info->uid); - if (remove_deleted_flag) { - /* Remove the DELETED flag so the - * message appears normally in the - * real Trash folder when copied. */ - flags &= ~CAMEL_MESSAGE_DELETED; - } + if (!is_new) + camel_message_info_unref (destination_info); + } - if ( (on && (((flags ^ sflags) & flags) & flag)) - || (!on && (((flags ^ sflags) & ~flags) & flag))) { - if (ic == NULL) { - ic = camel_imapx_command_new ( - is, "STORE", mailbox, - "UID STORE "); - ic->complete = imapx_command_sync_changes_done; - camel_imapx_command_set_job (ic, job); - ic->pri = job->pri; + if (camel_folder_change_info_changed (changes)) { + camel_folder_summary_touch (destination_folder->summary); + camel_folder_summary_save_to_db (destination_folder->summary, NULL); + camel_folder_changed (destination_folder, changes); } - send = imapx_uidset_add (&ss, ic, camel_message_info_uid (info)); - } - if (send == 1 || (i == uids->len - 1 && ic && imapx_uidset_done (&ss, ic))) { - g_atomic_int_add (&job->commands, 1); - camel_imapx_command_add (ic, " %tFLAGS.SILENT (%t)", on?"+":"-", flags_table[j].name); - imapx_command_queue (is, ic); - camel_imapx_command_unref (ic); - ic = NULL; - } - if (flag == CAMEL_MESSAGE_SEEN) { - /* Remember how the server's unread count will change if this - * command succeeds */ - if (on) - data->unread_change--; - else - data->unread_change++; - } - /* The second round and the server doesn't support saving user flags, - thus store them at least locally */ - if (on && (permanentflags & CAMEL_MESSAGE_USER) == 0) { - camel_flag_list_copy (&info->server_user_flags, &info->info.user_flags); + camel_folder_change_info_free (changes); + g_object_unref (destination_folder); } - - camel_message_info_unref (info); } - g_warn_if_fail (ic == NULL); - } + if (delete_originals || use_move_command) { + CamelFolderChangeInfo *changes = NULL; + gint jj; - if (user_set && (permanentflags & CAMEL_MESSAGE_USER) != 0) { - CamelIMAPXCommand *ic = NULL; + camel_folder_freeze (folder); - for (j = 0; j < user_set->len; j++) { - struct _imapx_flag_change *c = &g_array_index (user_set, struct _imapx_flag_change, j); + for (jj = last_index; jj < ii; jj++) { + const gchar *uid = uids->pdata[jj]; - imapx_uidset_init (&ss, 0, 100); - for (i = 0; i < c->infos->len; i++) { - CamelIMAPXMessageInfo *info = c->infos->pdata[i]; + if (delete_originals) { + camel_folder_delete_message (folder, uid); + } else { + if (camel_folder_summary_remove_uid (folder->summary, uid)) { + if (!changes) + changes = camel_folder_change_info_new (); - if (ic == NULL) { - ic = camel_imapx_command_new ( - is, "STORE", mailbox, - "UID STORE "); - ic->complete = imapx_command_sync_changes_done; - camel_imapx_command_set_job (ic, job); - ic->pri = job->pri; + camel_folder_change_info_remove_uid (changes, uid); + } } + } - if (imapx_uidset_add (&ss, ic, camel_message_info_uid (info)) == 1 - || (i == c->infos->len - 1 && imapx_uidset_done (&ss, ic))) { - g_atomic_int_add (&job->commands, 1); - camel_imapx_command_add (ic, " %tFLAGS.SILENT (%t)", on?"+":"-", c->name); - imapx_command_queue (is, ic); - camel_imapx_command_unref (ic); - ic = NULL; - } + if (changes && camel_folder_change_info_changed (changes)) { + camel_folder_summary_touch (folder->summary); + camel_folder_summary_save_to_db (folder->summary, NULL); + camel_folder_changed (folder, changes); } + + camel_folder_thaw (folder); + + if (changes) + camel_folder_change_info_free (changes); } } - } - g_object_unref (folder); - g_object_unref (mailbox); + imapx_free_status (is->priv->copyuid_status); + is->priv->copyuid_status = NULL; - if (g_atomic_int_get (&job->commands) == 0) { - imapx_unregister_job (is, job); - } else { - /* Make sure no command will starve in a queue */ - QUEUE_LOCK (is); - imapx_command_start_next (is); - QUEUE_UNLOCK (is); + camel_imapx_command_unref (ic); } - return TRUE; + g_hash_table_destroy (source_infos); + g_ptr_array_foreach (data_uids, (GFunc) camel_pstring_free, NULL); + g_ptr_array_free (data_uids, TRUE); + g_object_unref (folder); + + return success; } -static gboolean -imapx_job_sync_changes_matches (CamelIMAPXJob *job, - CamelIMAPXMailbox *mailbox, - const gchar *uid) +static const gchar * +get_month_str (gint month) { - return camel_imapx_job_has_mailbox (job, mailbox); + static const gchar tm_months[][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + if (month < 1 || month > 12) + return NULL; + + return tm_months[month - 1]; } -static void -imapx_abort_all_commands (CamelIMAPXServer *is, - const GError *error) +gboolean +camel_imapx_server_append_message_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + CamelFolderSummary *summary, + CamelDataCache *message_cache, + CamelMimeMessage *message, + const CamelMessageInfo *mi, + gchar **appended_uid, + GCancellable *cancellable, + GError **error) { - CamelIMAPXCommandQueue *queue; - GList *head, *link; + gchar *uid = NULL, *path = NULL; + CamelMimeFilter *filter; + CamelIMAPXCommand *ic; + CamelMessageInfo *info; + GIOStream *base_stream; + GOutputStream *output_stream; + GOutputStream *filter_stream; + gint res; + time_t date_time; + gboolean success; + + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE); + g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), FALSE); + g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE); + /* CamelMessageInfo can be NULL. */ + + /* That's okay if the "SELECT" fails here, as it can be due to + the folder being write-only; just ignore the error and continue. */ + camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, NULL); - /* Transfer all pending and active commands to a separate - * command queue to complete them without holding QUEUE_LOCK. */ + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + /* Append just assumes we have no/a dodgy connection. We dump + * stuff into the 'new' directory, and let the summary know it's + * there. Then we fire off a no-reply job which will asynchronously + * upload the message at some point in the future, and fix up the + * summary to match */ - queue = camel_imapx_command_queue_new (); + /* chen cleanup this later */ + uid = imapx_get_temp_uid (); + base_stream = camel_data_cache_add (message_cache, "new", uid, error); + if (base_stream == NULL) { + g_prefix_error (error, _("Cannot create spool file: ")); + g_free (uid); + return FALSE; + } - imapx_server_set_shutdown_error (is, error); + output_stream = g_io_stream_get_output_stream (base_stream); + filter = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_CRLF); + filter_stream = camel_filter_output_stream_new (output_stream, filter); - QUEUE_LOCK (is); + g_filter_output_stream_set_close_base_stream ( + G_FILTER_OUTPUT_STREAM (filter_stream), FALSE); - camel_imapx_command_queue_transfer (is->queue, queue); - camel_imapx_command_queue_transfer (is->active, queue); + res = camel_data_wrapper_write_to_output_stream_sync ( + CAMEL_DATA_WRAPPER (message), + filter_stream, cancellable, error); - head = camel_imapx_command_queue_peek_head_link (queue); - for (link = head; link != NULL; link = g_list_next (link)) { - CamelIMAPXCommand *ic = link->data; + g_object_unref (base_stream); + g_object_unref (filter_stream); + g_object_unref (filter); - if (ic) - imapx_server_command_removed (is, ic); + if (res == -1) { + g_prefix_error (error, _("Cannot create spool file: ")); + camel_data_cache_remove (message_cache, "new", uid, NULL); + g_free (uid); + return FALSE; } - QUEUE_UNLOCK (is); + date_time = camel_mime_message_get_date (message, NULL); + path = camel_data_cache_get_filename (message_cache, "new", uid); + info = camel_folder_summary_info_new_from_message (summary, message, NULL); + info->uid = camel_pstring_strdup (uid); - head = camel_imapx_command_queue_peek_head_link (queue); + if (mi != NULL) { + struct icaltimetype icaltime; + CamelMessageInfoBase *base_info = (CamelMessageInfoBase *) info; + const CamelFlag *flag; + const CamelTag *tag; - for (link = head; link != NULL; link = g_list_next (link)) { - CamelIMAPXCommand *ic = link->data; + base_info->flags = camel_message_info_flags (mi); + base_info->size = camel_message_info_size (mi); - /* Sanity check the CamelIMAPXCommand before proceeding. - * XXX We are actually getting reports of crashes here... - * not sure how this is happening but it's happening. */ - if (ic == NULL) - continue; + flag = camel_message_info_user_flags (mi); + while (flag != NULL) { + if (*flag->name != '\0') + camel_flag_set ( + &base_info->user_flags, + flag->name, TRUE); + flag = flag->next; + } + + tag = camel_message_info_user_tags (mi); + while (tag != NULL) { + if (*tag->name != '\0') + camel_tag_set ( + &base_info->user_tags, + tag->name, tag->value); + tag = tag->next; + } + + if (date_time > 0) { + icaltime = icaltime_from_timet (date_time, FALSE); + if (!icaltime_is_valid_time (icaltime)) + date_time = -1; + } + + if (date_time <= 0) + date_time = camel_message_info_date_received (mi); - /* Insert an error into the CamelIMAPXCommand to be - * propagated when the completion callback function - * calls camel_imapx_command_set_error_if_failed(). */ - camel_imapx_command_failed (ic, error); + if (date_time > 0) { + icaltime = icaltime_from_timet (date_time, FALSE); + if (!icaltime_is_valid_time (icaltime)) + date_time = -1; + } + } - /* Invoke the completion callback function so it can - * perform any cleanup processing and unregister its - * CamelIMAPXJob. */ - ic->complete (is, ic); + if (!camel_message_info_size (info)) { + CamelStreamNull *sn = (CamelStreamNull *) camel_stream_null_new (); + + camel_data_wrapper_write_to_stream_sync ( + CAMEL_DATA_WRAPPER (message), + CAMEL_STREAM (sn), NULL, NULL); + ((CamelMessageInfoBase *) info)->size = sn->written; + g_object_unref (sn); } - camel_imapx_command_queue_free (queue); + g_free (uid); - QUEUE_LOCK (is); + if (camel_mime_message_has_attachment (message)) + ((CamelMessageInfoBase *) info)->flags |= CAMEL_MESSAGE_ATTACHMENTS; - /* Abort also any pending jobs which are not in the command queues yet */ - if (!g_queue_is_empty (&is->jobs)) { - GList *jobs, *iter; + if (date_time > 0) { + gchar *date_time_str; + struct tm stm; - jobs = g_list_copy (g_queue_peek_head_link (&is->jobs)); - g_list_foreach (jobs, (GFunc) camel_imapx_job_ref, NULL); + gmtime_r (&date_time, &stm); - for (iter = jobs; iter != NULL; iter = g_list_next (iter)) { - CamelIMAPXJob *job = iter->data; + /* Store always in UTC */ + date_time_str = g_strdup_printf ( + "\"%02d-%s-%04d %02d:%02d:%02d +0000\"", + stm.tm_mday, + get_month_str (stm.tm_mon + 1), + stm.tm_year + 1900, + stm.tm_hour, + stm.tm_min, + stm.tm_sec); - camel_imapx_job_take_error (job, g_error_copy (error)); - camel_imapx_job_done (job); - } + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_APPEND_MESSAGE, "APPEND %M %F %t %P", + mailbox, + ((CamelMessageInfoBase *) info)->flags, + ((CamelMessageInfoBase *) info)->user_flags, + date_time_str, + path); - g_list_free_full (jobs, (GDestroyNotify) camel_imapx_job_unref); + g_free (date_time_str); + } else { + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_APPEND_MESSAGE, "APPEND %M %F %P", + mailbox, + ((CamelMessageInfoBase *) info)->flags, + ((CamelMessageInfoBase *) info)->user_flags, + path); } - QUEUE_UNLOCK (is); -} + success = camel_imapx_server_process_command_sync (is, ic, _("Error appending message"), cancellable, error); -/* ********************************************************************** */ + if (success) { + CamelIMAPXFolder *imapx_folder; + CamelFolder *folder; + CamelMessageInfo *mi; + gchar *cur, *old_uid; + guint32 uidvalidity; -static gboolean -imapx_ready_to_read (GInputStream *input_stream, - CamelIMAPXServer *is) -{ - GOutputStream *output_stream; - GCancellable *cancellable; - GError *local_error = NULL; + folder = imapx_server_ref_folder (is, mailbox); + g_return_val_if_fail (folder != NULL, FALSE); - /* XXX Don't use the passed in GInputStream because that's - * the CamelIMAPXInputStream base stream. We need the - * CamelIMAPXInputStream itself. */ + uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox); - input_stream = camel_imapx_server_ref_input_stream (is); - output_stream = camel_imapx_server_ref_output_stream (is); + imapx_folder = CAMEL_IMAPX_FOLDER (folder); - cancellable = g_weak_ref_get (&is->priv->parser_cancellable); + /* Append done. If we the server supports UIDPLUS we will get + * an APPENDUID response with the new uid. This lets us move the + * message we have directly to the cache and also create a correctly + * numbered MessageInfo, without losing any information. Otherwise + * we have to wait for the server to let us know it was appended. */ + + mi = camel_message_info_clone (info); + old_uid = g_strdup (info->uid); + + if (ic->status && ic->status->condition == IMAPX_APPENDUID) { + c (is->priv->tagprefix, "Got appenduid %d %d\n", (gint) ic->status->u.appenduid.uidvalidity, (gint) ic->status->u.appenduid.uid); + if (ic->status->u.appenduid.uidvalidity == uidvalidity) { + if (appended_uid) + *appended_uid = g_strdup_printf ("%u", (guint) ic->status->u.appenduid.uid); + mi->uid = camel_pstring_add (g_strdup_printf ("%u", (guint) ic->status->u.appenduid.uid), TRUE); + + cur = camel_data_cache_get_filename (imapx_folder->cache, "cur", mi->uid); + if (g_rename (path, cur) == -1 && errno != ENOENT) { + g_warning ("%s: Failed to rename '%s' to '%s': %s", G_STRFUNC, path, cur, g_strerror (errno)); + } - while (imapx_step (is, input_stream, cancellable, &local_error)) { - gint bytes_buffered; + imapx_set_message_info_flags_for_new_message ( + mi, + ((CamelMessageInfoBase *) info)->flags, + ((CamelMessageInfoBase *) info)->user_flags, + TRUE, + ((CamelMessageInfoBase *) info)->user_tags, + camel_imapx_mailbox_get_permanentflags (mailbox)); - bytes_buffered = camel_imapx_input_stream_buffered ( - CAMEL_IMAPX_INPUT_STREAM (input_stream)); - if (bytes_buffered == 0) - break; - } + camel_folder_summary_add (folder->summary, mi); - if (g_cancellable_is_cancelled (cancellable)) { - gboolean active_queue_is_empty, is_shutdown_request; + g_mutex_lock (&is->priv->changes_lock); + camel_folder_change_info_add_uid (is->priv->changes, mi->uid); + g_mutex_unlock (&is->priv->changes_lock); - QUEUE_LOCK (is); - active_queue_is_empty = - camel_imapx_command_queue_is_empty (is->active); - is_shutdown_request = is->state == IMAPX_SHUTDOWN; - QUEUE_UNLOCK (is); + mi = NULL; - if (!is_shutdown_request && (active_queue_is_empty || imapx_in_idle (is))) { - g_cancellable_reset (cancellable); - g_clear_error (&local_error); - } else { - /* Cancelled error should be set. */ - g_warn_if_fail (local_error != NULL); + g_free (cur); + } else { + c (is->priv->tagprefix, "but uidvalidity changed \n"); + } } - } - g_clear_object (&input_stream); - g_clear_object (&output_stream); - g_clear_object (&cancellable); + camel_data_cache_remove (imapx_folder->cache, "new", old_uid, NULL); + g_free (old_uid); - if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) { - QUEUE_LOCK (is); - if (camel_imapx_command_queue_is_empty (is->active) && is->state != IMAPX_SHUTDOWN) { - camel_imapx_debug (io, is->tagprefix, "Ignoring timeout error, nothing was waiting (original error: %s)\n", local_error->message); - g_clear_error (&local_error); - } - QUEUE_UNLOCK (is); + camel_imapx_command_unref (ic); + if (mi) + camel_message_info_unref (mi); + g_object_unref (folder); } - if (local_error != NULL) { - camel_imapx_debug (io, is->tagprefix, "Data read failed with error '%s'\n", local_error->message); - - /* Sadly, G_IO_ERROR_FAILED is also used for 'Connection reset by peer' error */ - if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_FAILED) || - g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) { - local_error->domain = CAMEL_IMAPX_SERVER_ERROR; - local_error->code = CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT; - } - - imapx_server_set_shutdown_error (is, local_error); - - /* Call the signal early, certain thread interleaving can cause the closed connection - being reused on the following reconnect attempt. There is also re-setting - the shutdown_error above, because the signal handler in connection manager - also calls camel_imapx_server_shutdown(), but without the error, while we want - to have there propagated the "try reconnect" error instead. As there is no - guarantee that it'll be called, then we also quit the parser's mainloop and - call the imapx_abort_all_commands() below - just in case. */ - g_signal_emit (is, signals[SHUTDOWN], 0, local_error); - - g_main_loop_quit (is->priv->parser_main_loop); - imapx_abort_all_commands (is, local_error); - g_clear_error (&local_error); - return G_SOURCE_REMOVE; - } + g_free (path); - return G_SOURCE_CONTINUE; + return success; } -/* - * The main processing (reading) loop. - * - * Main area of locking required is command_queue - * and command_start_next, the 'literal' command, - * the jobs queue, the active queue, the queue - * queue. */ -static gpointer -imapx_parser_thread (gpointer user_data) +gboolean +camel_imapx_server_noop_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) { - CamelIMAPXServer *is; - GInputStream *input_stream; - GCancellable *cancellable; - GSource *pollable_source; - GError *shutdown_error; - - is = CAMEL_IMAPX_SERVER (user_data); - - /* Do not use CamelOperation here, because it can be cancelled at - * an application end with camel_operation_cancel_all() call, which - * is done too early, before any pending jobs are properly finished - * (it can be IDLE job, or save of folder changes back to the server). - */ - cancellable = g_cancellable_new (); - g_weak_ref_set (&is->priv->parser_cancellable, cancellable); - - input_stream = camel_imapx_server_ref_input_stream (is); - g_return_val_if_fail (input_stream != NULL, NULL); + gboolean success = TRUE; - g_main_context_push_thread_default (is->priv->parser_main_context); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + /* Mailbox may be NULL. */ - pollable_source = g_pollable_input_stream_create_source ( - G_POLLABLE_INPUT_STREAM (input_stream), cancellable); - g_source_set_callback ( - pollable_source, - (GSourceFunc) imapx_ready_to_read, - g_object_ref (is), - (GDestroyNotify) g_object_unref); - g_source_attach ( - pollable_source, - is->priv->parser_main_context); - g_source_unref (pollable_source); + if (mailbox) + success = camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error); - g_clear_object (&cancellable); - g_clear_object (&input_stream); + if (success) { + CamelIMAPXCommand *ic; - g_main_loop_run (is->priv->parser_main_loop); + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NOOP, "NOOP"); - QUEUE_LOCK (is); - is->state = IMAPX_SHUTDOWN; - QUEUE_UNLOCK (is); + success = camel_imapx_server_process_command_sync (is, ic, _("Error performing NOOP"), cancellable, error); - g_main_context_pop_thread_default (is->priv->parser_main_context); + camel_imapx_command_unref (ic); + } - shutdown_error = imapx_server_dup_shutdown_error (is); + return success; +} - g_signal_emit (is, signals[SHUTDOWN], 0, shutdown_error); +/* ********************************************************************** */ - g_clear_error (&shutdown_error); +static gint +imapx_refresh_info_uid_cmp (gconstpointer ap, + gconstpointer bp, + gboolean ascending) +{ + guint av, bv; - g_object_unref (is); + av = g_ascii_strtoull ((const gchar *) ap, NULL, 10); + bv = g_ascii_strtoull ((const gchar *) bp, NULL, 10); - return NULL; + if (av < bv) + return ascending ? -1 : 1; + else if (av > bv) + return ascending ? 1 : -1; + else + return 0; } -static void -imapx_server_set_store (CamelIMAPXServer *server, - CamelIMAPXStore *store) +static gint +imapx_uids_array_cmp (gconstpointer ap, + gconstpointer bp) { - g_return_if_fail (CAMEL_IS_IMAPX_STORE (store)); + const gchar **a = (const gchar **) ap; + const gchar **b = (const gchar **) bp; - g_weak_ref_set (&server->priv->store, store); + return imapx_refresh_info_uid_cmp (*a, *b, TRUE); } -static void -imapx_server_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) +static gint +imapx_uids_desc_cmp (gconstpointer ap, + gconstpointer bp) { - switch (property_id) { - case PROP_STORE: - imapx_server_set_store ( - CAMEL_IMAPX_SERVER (object), - g_value_get_object (value)); - return; - } + const gchar *a = (const gchar *) ap; + const gchar *b = (const gchar *) bp; - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + return imapx_refresh_info_uid_cmp (a, b, FALSE); } static void -imapx_server_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) +imapx_server_process_fetch_changes_infos (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + CamelFolder *folder, + GHashTable *infos, + GHashTable *known_uids, + GSList **out_fetch_summary_uids, + guint64 from_uidl, + guint64 to_uidl) { - switch (property_id) { - case PROP_STORE: - g_value_take_object ( - value, - camel_imapx_server_ref_store ( - CAMEL_IMAPX_SERVER (object))); - return; - } + GHashTableIter iter; + gpointer key, value; + CamelFolderSummary *summary; - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is)); + g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox)); + g_return_if_fail (CAMEL_IS_FOLDER (folder)); + g_return_if_fail (infos != NULL); + + if (out_fetch_summary_uids) + g_return_if_fail (*out_fetch_summary_uids == NULL); + + summary = folder->summary; + + g_hash_table_iter_init (&iter, infos); + while (g_hash_table_iter_next (&iter, &key, &value)) { + const gchar *uid = key; + FetchChangesInfo *nfo = value; + CamelMessageInfo *minfo; + + if (!uid || !nfo) + continue; + + if (known_uids) + g_hash_table_insert (known_uids, (gpointer) camel_pstring_strdup (uid), GINT_TO_POINTER (1)); + + if (!camel_folder_summary_check_uid (summary, uid) || + !(minfo = camel_folder_summary_get (summary, uid))) { + if (out_fetch_summary_uids) { + *out_fetch_summary_uids = g_slist_prepend (*out_fetch_summary_uids, + (gpointer) camel_pstring_strdup (uid)); + } + + continue; + } + + if (imapx_update_message_info_flags ( + minfo, + nfo->server_flags, + nfo->server_user_flags, + camel_imapx_mailbox_get_permanentflags (mailbox), + folder, FALSE)) { + g_mutex_lock (&is->priv->changes_lock); + camel_folder_change_info_change_uid (is->priv->changes, camel_message_info_uid (minfo)); + g_mutex_unlock (&is->priv->changes_lock); + } + + camel_message_info_unref (minfo); + } } -static void -imapx_server_dispose (GObject *object) +static gboolean +imapx_server_fetch_changes (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + CamelFolder *folder, + GHashTable *known_uids, + guint64 from_uidl, + guint64 to_uidl, + GCancellable *cancellable, + GError **error) { - CamelIMAPXServer *server = CAMEL_IMAPX_SERVER (object); - gboolean idle_main_loop_is_running; - gboolean parser_main_loop_is_running; + GSList *fetch_summary_uids = NULL; + GHashTable *infos; /* uid ~> FetchChangesInfo */ + CamelIMAPXCommand *ic; + gboolean success; + + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + if (!from_uidl) + from_uidl = 1; - /* Server should be shut down already. Warn if - * the idle or parser threads are still running. */ - idle_main_loop_is_running = - g_main_loop_is_running (server->priv->idle_main_loop); - parser_main_loop_is_running = - g_main_loop_is_running (server->priv->parser_main_loop); - g_warn_if_fail (!idle_main_loop_is_running); - g_warn_if_fail (!parser_main_loop_is_running); - - if (server->priv->parser_thread != NULL) { - g_thread_unref (server->priv->parser_thread); - server->priv->parser_thread = NULL; - } - - if (server->priv->idle_thread != NULL) { - g_thread_unref (server->priv->idle_thread); - server->priv->idle_thread = NULL; + if (to_uidl > 0) { + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_REFRESH_INFO, "UID FETCH %lld:%lld (UID FLAGS)", from_uidl, to_uidl); + } else { + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_REFRESH_INFO, "UID FETCH %lld:* (UID FLAGS)", from_uidl); } - imapx_disconnect (server); + g_return_val_if_fail (is->priv->fetch_changes_mailbox == NULL, FALSE); + g_return_val_if_fail (is->priv->fetch_changes_folder == NULL, FALSE); + g_return_val_if_fail (is->priv->fetch_changes_infos == NULL, FALSE); - g_weak_ref_set (&server->priv->store, NULL); + infos = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, fetch_changes_info_free); -#if GLIB_CHECK_VERSION(2,39,0) - g_clear_object (&server->priv->subprocess); -#endif + is->priv->fetch_changes_mailbox = mailbox; + is->priv->fetch_changes_folder = folder; + is->priv->fetch_changes_infos = infos; + is->priv->fetch_changes_last_progress = 0; - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (camel_imapx_server_parent_class)->dispose (object); -} + camel_operation_push_message (cancellable, + _("Scanning for changed messages in '%s'"), + camel_folder_get_display_name (folder)); -static void -imapx_server_finalize (GObject *object) -{ - CamelIMAPXServer *is = CAMEL_IMAPX_SERVER (object); + success = camel_imapx_server_process_command_sync (is, ic, _("Error scanning changes"), cancellable, error); - g_mutex_clear (&is->priv->stream_lock); + camel_operation_pop_message (cancellable); + camel_imapx_command_unref (ic); - camel_imapx_command_queue_free (is->queue); - camel_imapx_command_queue_free (is->active); - camel_imapx_command_queue_free (is->done); - - is->queue = NULL; - is->active = NULL; - is->done = NULL; + /* It can partly succeed. */ + imapx_server_process_fetch_changes_infos (is, mailbox, folder, infos, known_uids, &fetch_summary_uids, from_uidl, to_uidl); - g_rec_mutex_clear (&is->queue_lock); - g_mutex_clear (&is->priv->select_lock); + g_hash_table_remove_all (infos); - g_main_loop_unref (is->priv->parser_main_loop); - g_main_context_unref (is->priv->parser_main_context); + if (success && fetch_summary_uids) { + struct _uidset_state uidset; + GSList *link; - camel_folder_change_info_free (is->priv->changes); + ic = NULL; + imapx_uidset_init (&uidset, 0, 100); - g_free (is->priv->context); - g_hash_table_destroy (is->priv->untagged_handlers); + camel_operation_push_message (cancellable, + _("Fetching summary information for new messages in '%s'"), + camel_folder_get_display_name (folder)); - if (is->priv->inactivity_timeout != NULL) - g_source_unref (is->priv->inactivity_timeout); - g_mutex_clear (&is->priv->inactivity_timeout_lock); + fetch_summary_uids = g_slist_sort (fetch_summary_uids, imapx_uids_desc_cmp); - g_free (is->priv->status_data_items); - g_free (is->priv->list_return_opts); + for (link = fetch_summary_uids; link; link = g_slist_next (link)) { + const gchar *uid = link->data; - if (is->priv->search_results != NULL) - g_array_unref (is->priv->search_results); - g_mutex_clear (&is->priv->search_results_lock); + if (!uid) + continue; - g_hash_table_destroy (is->priv->known_alerts); - g_mutex_clear (&is->priv->known_alerts_lock); + if (!ic) + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_REFRESH_INFO, "UID FETCH "); - g_rec_mutex_clear (&is->priv->idle_lock); - g_main_loop_unref (is->priv->idle_main_loop); - g_main_context_unref (is->priv->idle_main_context); + if (imapx_uidset_add (&uidset, ic, uid) == 1 || (!link->next && ic && imapx_uidset_done (&uidset, ic))) { + camel_imapx_command_add (ic, " (RFC822.SIZE RFC822.HEADER FLAGS)"); - g_mutex_clear (&is->priv->jobs_prop_lock); - g_hash_table_destroy (is->priv->jobs_prop_folder_paths); + success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching message info"), cancellable, error); - g_mutex_clear (&is->priv->shutdown_error_lock); - g_clear_error (&is->priv->shutdown_error); + camel_imapx_command_unref (ic); + ic = NULL; - g_weak_ref_clear (&is->priv->store); - g_weak_ref_clear (&is->priv->parser_cancellable); - g_weak_ref_clear (&is->priv->select_mailbox); - g_weak_ref_clear (&is->priv->select_closing); - g_weak_ref_clear (&is->priv->select_pending); + if (!success) + break; - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (camel_imapx_server_parent_class)->finalize (object); -} + imapx_server_process_fetch_changes_infos (is, mailbox, folder, infos, NULL, NULL, 0, 0); + g_hash_table_remove_all (infos); + } + } -static void -imapx_server_constructed (GObject *object) -{ - CamelIMAPXServer *server; + camel_operation_pop_message (cancellable); - /* Chain up to parent's method. */ - G_OBJECT_CLASS (camel_imapx_server_parent_class)->constructed (object); + imapx_server_process_fetch_changes_infos (is, mailbox, folder, infos, NULL, NULL, 0, 0); + } - server = CAMEL_IMAPX_SERVER (object); - server->tagprefix = 'Z'; -} + g_return_val_if_fail (is->priv->fetch_changes_mailbox == mailbox, FALSE); + g_return_val_if_fail (is->priv->fetch_changes_folder == folder, FALSE); + g_return_val_if_fail (is->priv->fetch_changes_infos == infos, FALSE); -static void -imapx_server_mailbox_select (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox) -{ - imapx_server_add_job_mailbox (is, mailbox); + is->priv->fetch_changes_mailbox = NULL; + is->priv->fetch_changes_folder = NULL; + is->priv->fetch_changes_infos = NULL; + + g_slist_free_full (fetch_summary_uids, (GDestroyNotify) camel_pstring_free); + g_hash_table_destroy (infos); - e ( - is->tagprefix, - "%s::mailbox-select (\"%s\")\n", - G_OBJECT_TYPE_NAME (is), - camel_imapx_mailbox_get_name (mailbox)); + return success; } -static void -imapx_server_mailbox_closed (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox) +gboolean +camel_imapx_server_refresh_info_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) { - imapx_server_remove_job_mailbox (is, mailbox); + CamelIMAPXCommand *ic; + CamelIMAPXMailbox *selected_mailbox; + CamelIMAPXSummary *imapx_summary; + CamelFolder *folder; + GHashTable *known_uids; + guint32 messages; + guint32 unseen; + guint32 uidnext; + guint32 uidvalidity; + guint64 highestmodseq; + guint32 total; + guint64 uidl; + gboolean need_rescan; + gboolean success; - e ( - is->tagprefix, - "%s::mailbox-closed (\"%s\")\n", - G_OBJECT_TYPE_NAME (is), - camel_imapx_mailbox_get_name (mailbox)); -} + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); -static void -camel_imapx_server_class_init (CamelIMAPXServerClass *class) -{ - GObjectClass *object_class; + selected_mailbox = camel_imapx_server_ref_pending_or_selected (is); + if (selected_mailbox == mailbox) { + success = camel_imapx_server_noop_sync (is, mailbox, cancellable, error); + } else { + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_STATUS, "STATUS %M (%t)", mailbox, is->priv->status_data_items); - g_type_class_add_private (class, sizeof (CamelIMAPXServerPrivate)); + success = camel_imapx_server_process_command_sync (is, ic, _("Error running STATUS"), cancellable, error); - object_class = G_OBJECT_CLASS (class); - object_class->set_property = imapx_server_set_property; - object_class->get_property = imapx_server_get_property; - object_class->finalize = imapx_server_finalize; - object_class->dispose = imapx_server_dispose; - object_class->constructed = imapx_server_constructed; + camel_imapx_command_unref (ic); + } + g_clear_object (&selected_mailbox); - class->mailbox_select = imapx_server_mailbox_select; - class->mailbox_closed = imapx_server_mailbox_closed; + if (!success) + return FALSE; - g_object_class_install_property ( - object_class, - PROP_STORE, - g_param_spec_object ( - "store", - "Store", - "IMAPX store for this server", - CAMEL_TYPE_IMAPX_STORE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); + folder = imapx_server_ref_folder (is, mailbox); + g_return_val_if_fail (folder != NULL, FALSE); - signals[MAILBOX_SELECT] = g_signal_new ( - "mailbox-select", - G_OBJECT_CLASS_TYPE (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (CamelIMAPXServerClass, mailbox_select), - NULL, NULL, NULL, - G_TYPE_NONE, 1, - CAMEL_TYPE_IMAPX_MAILBOX); + imapx_summary = CAMEL_IMAPX_SUMMARY (folder->summary); - signals[MAILBOX_CLOSED] = g_signal_new ( - "mailbox-closed", - G_OBJECT_CLASS_TYPE (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (CamelIMAPXServerClass, mailbox_closed), - NULL, NULL, NULL, - G_TYPE_NONE, 1, - CAMEL_TYPE_IMAPX_MAILBOX); + messages = camel_imapx_mailbox_get_messages (mailbox); + unseen = camel_imapx_mailbox_get_unseen (mailbox); + uidnext = camel_imapx_mailbox_get_uidnext (mailbox); + uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox); + highestmodseq = camel_imapx_mailbox_get_highestmodseq (mailbox); + total = camel_folder_summary_count (folder->summary); - /** - * CamelIMAPXServer::shutdown - * @server: the #CamelIMAPXServer which emitted the signal - * @error: a #GError, which caused the shutdown; can be %NULL - **/ - signals[SHUTDOWN] = g_signal_new ( - "shutdown", - G_OBJECT_CLASS_TYPE (class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (CamelIMAPXServerClass, shutdown), - NULL, NULL, - g_cclosure_marshal_VOID__BOXED, - G_TYPE_NONE, 1, G_TYPE_ERROR); -} + need_rescan = + (uidvalidity > 0 && uidvalidity != imapx_summary->validity) || + total != messages || + imapx_summary->uidnext != uidnext || + camel_folder_summary_get_unread_count (folder->summary) != unseen || + imapx_summary->modseq != highestmodseq; -static void -camel_imapx_server_init (CamelIMAPXServer *is) -{ - GMainContext *main_context; + if (!need_rescan) { + g_object_unref (folder); + return TRUE; + } - is->priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is); + if (!camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error)) { + g_object_unref (folder); + return FALSE; + } - is->priv->untagged_handlers = create_initial_untagged_handler_table (); + if (is->priv->use_qresync && imapx_summary->modseq > 0 && uidvalidity > 0) { + imapx_summary->modseq = highestmodseq; + if (total != messages || + camel_folder_summary_get_unread_count (folder->summary) != unseen || + imapx_summary->modseq != highestmodseq) { + c ( + is->priv->tagprefix, + "Eep, after QRESYNC we're out of sync. " + "total %u / %u, unread %u / %u, modseq %" + G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT "\n", + total, messages, + camel_folder_summary_get_unread_count (folder->summary), + unseen, + imapx_summary->modseq, + highestmodseq); + } else { + c ( + is->priv->tagprefix, + "OK, after QRESYNC we're still in sync. " + "total %u / %u, unread %u / %u, modseq %" + G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT "\n", + total, messages, + camel_folder_summary_get_unread_count (folder->summary), + unseen, + imapx_summary->modseq, + highestmodseq); + g_object_unref (folder); + return TRUE; + } + } - g_mutex_init (&is->priv->stream_lock); - g_mutex_init (&is->priv->inactivity_timeout_lock); - g_mutex_init (&is->priv->select_lock); - g_mutex_init (&is->priv->search_results_lock); - g_mutex_init (&is->priv->known_alerts_lock); - g_mutex_init (&is->priv->jobs_prop_lock); - g_mutex_init (&is->priv->shutdown_error_lock); + if (total > 0) { + gchar *uid = camel_imapx_dup_uid_from_summary_index (folder, total - 1); + if (uid) { + uidl = g_ascii_strtoull (uid, NULL, 10); + g_free (uid); + uidl++; + } else { + uidl = 1; + } + } else { + uidl = 1; + } - g_weak_ref_init (&is->priv->store, NULL); - g_weak_ref_init (&is->priv->parser_cancellable, NULL); - g_weak_ref_init (&is->priv->select_mailbox, NULL); - g_weak_ref_init (&is->priv->select_closing, NULL); - g_weak_ref_init (&is->priv->select_pending, NULL); + camel_folder_summary_prepare_fetch_all (folder->summary, NULL); - is->priv->jobs_prop_folder_paths = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - is->priv->jobs_prop_command_count = 0; - is->priv->jobs_prop_expensive_command_count = 0; + known_uids = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL); - is->queue = camel_imapx_command_queue_new (); - is->active = camel_imapx_command_queue_new (); - is->done = camel_imapx_command_queue_new (); + success = imapx_server_fetch_changes (is, mailbox, folder, known_uids, uidl, 0, cancellable, error); + if (success && uidl != 1) + success = imapx_server_fetch_changes (is, mailbox, folder, known_uids, 0, uidl, cancellable, error); - g_queue_init (&is->jobs); + if (success) { + CamelFolderChangeInfo *changes; + GList *removed = NULL; + GPtrArray *array; + gint ii; - g_rec_mutex_init (&is->queue_lock); + camel_folder_summary_lock (folder->summary); - is->state = IMAPX_DISCONNECTED; + changes = camel_folder_change_info_new (); - is->priv->changes = camel_folder_change_info_new (); + array = camel_folder_summary_get_array (folder->summary); + for (ii = 0; array && ii < array->len; ii++) { + const gchar *uid = array->pdata[ii]; - is->priv->known_alerts = g_hash_table_new_full ( - (GHashFunc) g_str_hash, - (GEqualFunc) g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) NULL); + if (!uid) + continue; - /* Initialize parser thread structs. */ + if (!g_hash_table_contains (known_uids, uid)) { + removed = g_list_prepend (removed, (gpointer) uid); + camel_folder_change_info_remove_uid (changes, uid); + } + } - main_context = g_main_context_new (); + camel_folder_summary_unlock (folder->summary); - is->priv->parser_main_loop = g_main_loop_new (main_context, FALSE); - is->priv->parser_main_context = g_main_context_ref (main_context); - is->priv->shutdown_error = NULL; + if (removed != NULL) { + camel_folder_summary_remove_uids (folder->summary, removed); + camel_folder_summary_touch (folder->summary); - g_main_context_unref (main_context); + /* Shares UIDs with the 'array'. */ + g_list_free (removed); + } - /* Initialize IDLE thread structs. */ + if (camel_folder_change_info_changed (changes)) { + camel_folder_summary_save_to_db (folder->summary, NULL); + imapx_update_store_summary (folder); + camel_folder_changed (folder, changes); + } - main_context = g_main_context_new (); + camel_folder_change_info_free (changes); + camel_folder_summary_free_array (array); + } - g_rec_mutex_init (&is->priv->idle_lock); - is->priv->idle_main_loop = g_main_loop_new (main_context, FALSE); - is->priv->idle_main_context = g_main_context_ref (main_context); + g_hash_table_destroy (known_uids); + g_object_unref (folder); - g_main_context_unref (main_context); + return success; } -CamelIMAPXServer * -camel_imapx_server_new (CamelIMAPXStore *store) +static void +imapx_sync_free_user (GArray *user_set) { - g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), NULL); + gint i; - return g_object_new ( - CAMEL_TYPE_IMAPX_SERVER, - "store", store, NULL); -} + if (user_set == NULL) + return; -CamelIMAPXStore * -camel_imapx_server_ref_store (CamelIMAPXServer *server) -{ - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), NULL); + for (i = 0; i < user_set->len; i++) { + struct _imapx_flag_change *flag_change = &g_array_index (user_set, struct _imapx_flag_change, i); + GPtrArray *infos = flag_change->infos; + gint j; - return g_weak_ref_get (&server->priv->store); + for (j = 0; j < infos->len; j++) { + CamelMessageInfo *info = g_ptr_array_index (infos, j); + camel_message_info_unref (info); + } + + g_ptr_array_free (infos, TRUE); + g_free (flag_change->name); + } + g_array_free (user_set, TRUE); } -CamelIMAPXSettings * -camel_imapx_server_ref_settings (CamelIMAPXServer *server) +static void +imapx_unset_folder_flagged_flag (CamelFolderSummary *summary, + GPtrArray *changed_uids, + gboolean except_deleted_messages) { - CamelIMAPXStore *store; - CamelSettings *settings; + CamelMessageInfo *info; + gboolean changed = FALSE; + gint ii; - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), NULL); + g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary)); + g_return_if_fail (changed_uids != NULL); - store = camel_imapx_server_ref_store (server); - settings = camel_service_ref_settings (CAMEL_SERVICE (store)); - g_object_unref (store); + for (ii = 0; ii < changed_uids->len; ii++) { + info = camel_folder_summary_get (summary, changed_uids->pdata[ii]); - return CAMEL_IMAPX_SETTINGS (settings); + if (info) { + CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info; + + /* some infos could be only 'dirty' (needed to save into summary) */ + if ((mi->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0 && + (!except_deleted_messages || (mi->flags & CAMEL_MESSAGE_DELETED) == 0)) { + mi->flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED; + mi->dirty = TRUE; + changed = TRUE; + } + + camel_message_info_unref (info); + } + } + + if (changed) { + camel_folder_summary_touch (summary); + camel_folder_summary_save_to_db (summary, NULL); + } } -/** - * camel_imapx_server_ref_input_stream: - * @is: a #CamelIMAPXServer - * - * Returns the #GInputStream for @is, which is owned by either a - * #GTcpConnection or a #GSubprocess. If the #CamelIMAPXServer is not - * yet connected or has lost its connection, the function returns %NULL. - * - * The returned #GInputStream is referenced for thread-safety and must - * be unreferenced with g_object_unref() when finished with it. - * - * Returns: a #GInputStream, or %NULL - * - * Since: 3.12 - **/ -GInputStream * -camel_imapx_server_ref_input_stream (CamelIMAPXServer *is) -{ - GInputStream *input_stream = NULL; +static void +imapx_server_info_changed_cb (CamelIMAPXSummary *summary, + CamelMessageInfo *info, + gpointer user_data) +{ + GHashTable *changed_meanwhile = user_data; + + g_return_if_fail (info != NULL); + g_return_if_fail (changed_meanwhile != NULL); + + /* The UID can be NULL in case of a newly fetched message, for example when creating + the message info in imapx_untagged_fetch() by camel_folder_summary_info_new_from_parser() */ + if (camel_message_info_uid (info)) { + g_hash_table_insert (changed_meanwhile, + (gpointer) camel_pstring_strdup (camel_message_info_uid (info)), + GINT_TO_POINTER (1)); + } +} - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); +gboolean +camel_imapx_server_sync_changes_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + gboolean can_influence_flags, + GCancellable *cancellable, + GError **error) +{ + guint i, jj, on, on_orset, off_orset; + GPtrArray *changed_uids; + GArray *on_user = NULL, *off_user = NULL; + CamelFolder *folder; + CamelIMAPXMessageInfo *info; + GHashTable *changed_meanwhile; + gulong changed_meanwhile_handler_id; + guint32 permanentflags; + struct _uidset_state uidset; + gint unread_change = 0; + gboolean use_real_junk_path = FALSE; + gboolean use_real_trash_path = FALSE; + gboolean remove_deleted_flags = FALSE; + gboolean nothing_to_do; + gboolean success; - g_mutex_lock (&is->priv->stream_lock); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - if (is->priv->input_stream != NULL) - input_stream = g_object_ref (is->priv->input_stream); + folder = imapx_server_ref_folder (is, mailbox); + g_return_val_if_fail (folder != NULL, FALSE); - g_mutex_unlock (&is->priv->stream_lock); + /* We calculate two masks, a mask of all flags which have been + * turned off and a mask of all flags which have been turned + * on. If either of these aren't 0, then we have work to do, + * and we fire off a job to do it. + * + * User flags are a bit more tricky, we rely on the user + * flags being sorted, and then we create a bunch of lists; + * one for each flag being turned off, including each + * info being turned off, and one for each flag being turned on. + */ + changed_uids = camel_folder_summary_get_changed (folder->summary); - return input_stream; -} + if (changed_uids->len == 0) { + camel_folder_free_uids (folder, changed_uids); + g_object_unref (folder); + return TRUE; + } -/** - * camel_imapx_server_ref_output_stream: - * @is: a #CamelIMAPXServer - * - * Returns the #GOutputStream for @is, which is owned by either a - * #GTcpConnection or a #GSubprocess. If the #CamelIMAPXServer is not - * yet connected or has lost its connection, the function returns %NULL. - * - * The returned #GOutputStream is referenced for thread-safety and must - * be unreferenced with g_object_unref() when finished with it. - * - * Returns: a #GOutputStream, or %NULL - * - * Since: 3.12 - **/ -GOutputStream * -camel_imapx_server_ref_output_stream (CamelIMAPXServer *is) -{ - GOutputStream *output_stream = NULL; + changed_meanwhile = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL); + changed_meanwhile_handler_id = g_signal_connect (folder->summary, "info-changed", + G_CALLBACK (imapx_server_info_changed_cb), changed_meanwhile); - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); + if (can_influence_flags) { + CamelIMAPXSettings *settings; - g_mutex_lock (&is->priv->stream_lock); + settings = camel_imapx_server_ref_settings (is); + use_real_junk_path = camel_imapx_settings_get_use_real_junk_path (settings); + use_real_trash_path = camel_imapx_settings_get_use_real_trash_path (settings); + if (use_real_trash_path) { + CamelFolder *trash_folder = NULL; + gchar *real_trash_path; + + real_trash_path = camel_imapx_settings_dup_real_trash_path (settings); + if (real_trash_path) + trash_folder = camel_store_get_folder_sync ( + camel_folder_get_parent_store (folder), + real_trash_path, 0, cancellable, NULL); - if (is->priv->output_stream != NULL) - output_stream = g_object_ref (is->priv->output_stream); + /* Remove deleted flags in all but the trash folder itself */ + remove_deleted_flags = !trash_folder || trash_folder != folder; - g_mutex_unlock (&is->priv->stream_lock); + use_real_trash_path = trash_folder != NULL; - return output_stream; -} + g_clear_object (&trash_folder); + g_free (real_trash_path); + } + g_object_unref (settings); + } -/** - * camel_imapx_server_ref_selected: - * @is: a #CamelIMAPXServer - * - * Returns the #CamelIMAPXMailbox representing the currently selected - * mailbox (or mailbox being selected if a SELECT - * command is in progress) on the IMAP server, or %NULL if no mailbox - * is currently selected or being selected on the server. - * - * The returned #CamelIMAPXMailbox is reference for thread-safety and - * should be unreferenced with g_object_unref() when finished with it. - * - * Returns: a #CamelIMAPXMailbox, or %NULL - * - * Since: 3.12 - **/ -CamelIMAPXMailbox * -camel_imapx_server_ref_selected (CamelIMAPXServer *is) -{ - CamelIMAPXMailbox *mailbox; + if (changed_uids->len > 20) + camel_folder_summary_prepare_fetch_all (folder->summary, NULL); - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); + off_orset = on_orset = 0; + for (i = 0; i < changed_uids->len; i++) { + guint32 flags, sflags; + CamelFlag *uflags, *suflags; + const gchar *uid; + guint j = 0; - g_mutex_lock (&is->priv->select_lock); + uid = g_ptr_array_index (changed_uids, i); - mailbox = g_weak_ref_get (&is->priv->select_mailbox); - if (mailbox == NULL) - mailbox = g_weak_ref_get (&is->priv->select_closing); - if (mailbox == NULL) - mailbox = g_weak_ref_get (&is->priv->select_pending); + info = (CamelIMAPXMessageInfo *) + camel_folder_summary_get (folder->summary, uid); - g_mutex_unlock (&is->priv->select_lock); + if (info == NULL) + continue; - return mailbox; -} + if (!(info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED)) { + camel_message_info_unref (info); + continue; + } -static void -imapx_disconnect (CamelIMAPXServer *is) -{ - g_mutex_lock (&is->priv->stream_lock); + flags = info->info.flags & CAMEL_IMAPX_SERVER_FLAGS; + sflags = info->server_flags & CAMEL_IMAPX_SERVER_FLAGS; - g_clear_object (&is->priv->input_stream); - g_clear_object (&is->priv->output_stream); - g_clear_object (&is->priv->connection); -#if GLIB_CHECK_VERSION(2,39,0) - g_clear_object (&is->priv->subprocess); -#endif + if (can_influence_flags) { + gboolean move_to_real_junk; + gboolean move_to_real_trash; + + move_to_real_junk = + use_real_junk_path && + (flags & CAMEL_MESSAGE_JUNK); + + move_to_real_trash = + use_real_trash_path && remove_deleted_flags && + (flags & CAMEL_MESSAGE_DELETED); + + if (move_to_real_junk) + camel_imapx_folder_add_move_to_real_junk ( + CAMEL_IMAPX_FOLDER (folder), uid); + + if (move_to_real_trash) + camel_imapx_folder_add_move_to_real_trash ( + CAMEL_IMAPX_FOLDER (folder), uid); + } - g_mutex_unlock (&is->priv->stream_lock); + if (flags != sflags) { + off_orset |= (flags ^ sflags) & ~flags; + on_orset |= (flags ^ sflags) & flags; + } - g_mutex_lock (&is->priv->select_lock); - g_weak_ref_set (&is->priv->select_mailbox, NULL); - g_weak_ref_set (&is->priv->select_closing, NULL); - g_weak_ref_set (&is->priv->select_pending, NULL); - g_mutex_unlock (&is->priv->select_lock); + uflags = info->info.user_flags; + suflags = info->server_user_flags; + while (uflags || suflags) { + gint res; - if (is->cinfo) { - imapx_free_capability (is->cinfo); - is->cinfo = NULL; - } + if (uflags) { + if (suflags) + res = strcmp (uflags->name, suflags->name); + else if (*uflags->name) + res = -1; + else { + uflags = uflags->next; + continue; + } + } else { + res = 1; + } - is->state = IMAPX_DISCONNECTED; -} + if (res == 0) { + uflags = uflags->next; + suflags = suflags->next; + } else { + GArray *user_set; + CamelFlag *user_flag; + struct _imapx_flag_change *change = NULL, add = { 0 }; -/* Client commands */ -gboolean -camel_imapx_server_connect (CamelIMAPXServer *is, - GCancellable *cancellable, - GError **error) -{ - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + if (res < 0) { + if (on_user == NULL) + on_user = g_array_new (FALSE, FALSE, sizeof (struct _imapx_flag_change)); + user_set = on_user; + user_flag = uflags; + uflags = uflags->next; + } else { + if (off_user == NULL) + off_user = g_array_new (FALSE, FALSE, sizeof (struct _imapx_flag_change)); + user_set = off_user; + user_flag = suflags; + suflags = suflags->next; + } - if (is->state == IMAPX_SHUTDOWN) { - g_set_error ( - error, CAMEL_SERVICE_ERROR, - CAMEL_SERVICE_ERROR_UNAVAILABLE, - "Shutting down"); - return FALSE; + /* Could sort this and binary search */ + for (j = 0; j < user_set->len; j++) { + change = &g_array_index (user_set, struct _imapx_flag_change, j); + if (strcmp (change->name, user_flag->name) == 0) + goto found; + } + add.name = g_strdup (user_flag->name); + add.infos = g_ptr_array_new (); + g_array_append_val (user_set, add); + change = &add; + found: + camel_message_info_ref (info); + g_ptr_array_add (change->infos, info); + } + } + + camel_message_info_unref (info); } - if (is->state >= IMAPX_INITIALISED) - return TRUE; + nothing_to_do = + (on_orset == 0) && + (off_orset == 0) && + (on_user == NULL) && + (off_user == NULL); - if (!imapx_reconnect (is, cancellable, error)) - return FALSE; + if (nothing_to_do) { + g_signal_handler_disconnect (folder->summary, changed_meanwhile_handler_id); + + imapx_sync_free_user (on_user); + imapx_sync_free_user (off_user); + imapx_unset_folder_flagged_flag (folder->summary, changed_uids, remove_deleted_flags); + camel_folder_free_uids (folder, changed_uids); + g_hash_table_destroy (changed_meanwhile); + g_object_unref (folder); + return TRUE; + } - is->priv->parser_thread = g_thread_new ( - NULL, imapx_parser_thread, g_object_ref (is)); + if (!camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error)) { + g_signal_handler_disconnect (folder->summary, changed_meanwhile_handler_id); - if (CAMEL_IMAPX_LACK_CAPABILITY (is->cinfo, NAMESPACE)) { - /* This also creates a needed faux NAMESPACE */ - if (!camel_imapx_server_list (is, "INBOX", 0, cancellable, error)) - return FALSE; + imapx_sync_free_user (on_user); + imapx_sync_free_user (off_user); + camel_folder_free_uids (folder, changed_uids); + g_hash_table_destroy (changed_meanwhile); + g_object_unref (folder); + return FALSE; } - return TRUE; -} + permanentflags = camel_imapx_mailbox_get_permanentflags (mailbox); -static CamelStream * -imapx_server_get_message (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - CamelFolderSummary *summary, - CamelDataCache *message_cache, - const gchar *message_uid, - gint pri, - GCancellable *cancellable, - GError **error) -{ - CamelStream *stream = NULL; - CamelIMAPXJob *job; - CamelMessageInfo *mi; - GIOStream *cache_stream; - GetMessageData *data; - gboolean registered; + success = TRUE; + for (on = 0; on < 2 && success; on++) { + guint32 orset = on ? on_orset : off_orset; + GArray *user_set = on ? on_user : off_user; - while (job = imapx_server_ref_job (is, mailbox, IMAPX_JOB_GET_MESSAGE, message_uid), job != NULL) { - /* Promote the existing GET_MESSAGE - * job's priority if ours is higher. */ - if (pri > job->pri) - job->pri = pri; - - /* Wait for the job to finish. */ - camel_imapx_job_wait (job, NULL); - camel_imapx_job_unref (job); - - /* Disregard errors here. If we failed to retreive the - * message from cache (implying the job we were waiting - * on failed or got cancelled), we'll just re-fetch it. */ - cache_stream = camel_data_cache_get ( - message_cache, "cur", message_uid, NULL); - if (cache_stream != NULL) { - /* Return new file stream, instead of a DataCache's to not fight - on its content and position with other jobs, if any. */ - gchar *filename = camel_data_cache_get_filename (message_cache, "cur", message_uid); - stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0, NULL); - g_free (filename); - g_object_unref (cache_stream); - - if (stream) - return stream; - } - } + for (jj = 0; jj < G_N_ELEMENTS (flags_table) && success; jj++) { + guint32 flag = flags_table[jj].flag; + CamelIMAPXCommand *ic = NULL; - QUEUE_LOCK (is); + if ((orset & flag) == 0) + continue; - if (g_cancellable_set_error_if_cancelled (cancellable, error)) { - QUEUE_UNLOCK (is); + c (is->priv->tagprefix, "checking/storing %s flags '%s'\n", on ? "on" : "off", flags_table[jj].name); + imapx_uidset_init (&uidset, 0, 100); + for (i = 0; i < changed_uids->len && success; i++) { + CamelIMAPXMessageInfo *info; + gboolean remove_deleted_flag; + guint32 flags; + guint32 sflags; + gint send; - return NULL; - } + info = (CamelIMAPXMessageInfo *) + camel_folder_summary_get ( + folder->summary, + changed_uids->pdata[i]); - mi = camel_folder_summary_get (summary, message_uid); - if (mi == NULL) { - g_set_error ( - error, CAMEL_FOLDER_ERROR, - CAMEL_FOLDER_ERROR_INVALID_UID, - _("Cannot get message with message ID %s: %s"), - message_uid, _("No such message available.")); - QUEUE_UNLOCK (is); - return NULL; - } + if (info == NULL) + continue; - /* This makes sure that if any file is left on the disk, it is not reused. - That can happen when the previous message download had been cancelled - or finished with an error. */ - camel_data_cache_remove (message_cache, "tmp", message_uid, NULL); + flags = (info->info.flags & CAMEL_IMAPX_SERVER_FLAGS) & permanentflags; + sflags = (info->server_flags & CAMEL_IMAPX_SERVER_FLAGS) & permanentflags; + send = 0; - cache_stream = camel_data_cache_add (message_cache, "tmp", message_uid, error); - if (cache_stream == NULL) { - QUEUE_UNLOCK (is); - return NULL; - } + remove_deleted_flag = + remove_deleted_flags && + (flags & CAMEL_MESSAGE_DELETED); + + if (remove_deleted_flag) { + /* Remove the DELETED flag so the + * message appears normally in the + * real Trash folder when copied. */ + flags &= ~CAMEL_MESSAGE_DELETED; + } - data = g_slice_new0 (GetMessageData); - data->uid = g_strdup (message_uid); - data->message_cache = g_object_ref (message_cache); - data->stream = g_object_ref (cache_stream); - data->size = ((CamelMessageInfoBase *) mi)->size; - if (data->size > MULTI_SIZE) - data->use_multi_fetch = TRUE; - - job = camel_imapx_job_new (cancellable); - job->pri = pri; - job->type = IMAPX_JOB_GET_MESSAGE; - job->start = imapx_job_get_message_start; - job->matches = imapx_job_get_message_matches; + if ( (on && (((flags ^ sflags) & flags) & flag)) + || (!on && (((flags ^ sflags) & ~flags) & flag))) { + if (ic == NULL) { + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_SYNC_CHANGES, "UID STORE "); + } + send = imapx_uidset_add (&uidset, ic, camel_message_info_uid (info)); + } + if (send == 1 || (i == changed_uids->len - 1 && ic && imapx_uidset_done (&uidset, ic))) { + camel_imapx_command_add (ic, " %tFLAGS.SILENT (%t)", on ? "+" : "-", flags_table[jj].name); - camel_imapx_job_set_mailbox (job, mailbox); + success = camel_imapx_server_process_command_sync (is, ic, _("Error syncing changes"), cancellable, error); - camel_imapx_job_set_data ( - job, data, (GDestroyNotify) get_message_data_free); + camel_imapx_command_unref (ic); + ic = NULL; - g_clear_object (&cache_stream); - camel_message_info_unref (mi); + if (!success) + break; + } + if (flag == CAMEL_MESSAGE_SEEN) { + /* Remember how the server's unread count will change if this + * command succeeds */ + if (on) + unread_change--; + else + unread_change++; + } - registered = imapx_register_job (is, job, error); + /* The second round and the server doesn't support saving user flags, + thus store them at least locally */ + if (on && (permanentflags & CAMEL_MESSAGE_USER) == 0) { + camel_flag_list_copy (&info->server_user_flags, &info->info.user_flags); + } - QUEUE_UNLOCK (is); + camel_message_info_unref (info); + } - if (registered && camel_imapx_job_run (job, is, error)) - stream = camel_stream_new (data->stream); - else if (registered) - imapx_unregister_job (is, job); + g_warn_if_fail (ic == NULL); + } - camel_imapx_job_unref (job); + if (user_set && (permanentflags & CAMEL_MESSAGE_USER) != 0 && success) { + CamelIMAPXCommand *ic = NULL; - return stream; -} + for (jj = 0; jj < user_set->len && success; jj++) { + struct _imapx_flag_change *c = &g_array_index (user_set, struct _imapx_flag_change, jj); -CamelStream * -camel_imapx_server_get_message (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - CamelFolderSummary *summary, - CamelDataCache *message_cache, - const gchar *message_uid, - GCancellable *cancellable, - GError **error) -{ - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), NULL); - g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL); - g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), NULL); - g_return_val_if_fail (message_uid != NULL, NULL); + imapx_uidset_init (&uidset, 0, 100); + for (i = 0; i < c->infos->len; i++) { + CamelIMAPXMessageInfo *info = c->infos->pdata[i]; - return imapx_server_get_message ( - is, mailbox, summary, - message_cache, message_uid, - IMAPX_PRIORITY_GET_MESSAGE, - cancellable, error); -} + if (ic == NULL) + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_SYNC_CHANGES, "UID STORE "); -gboolean -camel_imapx_server_sync_message (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - CamelFolderSummary *summary, - CamelDataCache *message_cache, - const gchar *message_uid, - GCancellable *cancellable, - GError **error) -{ - gchar *cache_file = NULL; - gboolean is_cached; - struct stat st; - gboolean success = TRUE; + if (imapx_uidset_add (&uidset, ic, camel_message_info_uid (info)) == 1 + || (i == c->infos->len - 1 && imapx_uidset_done (&uidset, ic))) { + gchar *utf7; - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE); - g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), FALSE); - g_return_val_if_fail (message_uid != NULL, FALSE); + utf7 = camel_utf8_utf7 (c->name); - /* Check if the cache file already exists and is non-empty. */ - cache_file = camel_data_cache_get_filename ( - message_cache, "cur", message_uid); - is_cached = (g_stat (cache_file, &st) == 0 && st.st_size > 0); - g_free (cache_file); + camel_imapx_command_add (ic, " %tFLAGS.SILENT (%t)", on ? "+" : "-", utf7 ? utf7 : c->name); - if (!is_cached) { - CamelStream *stream; + g_free (utf7); - stream = imapx_server_get_message ( - is, mailbox, summary, - message_cache, message_uid, - IMAPX_PRIORITY_SYNC_MESSAGE, - cancellable, error); + success = camel_imapx_server_process_command_sync (is, ic, _("Error syncing changes"), cancellable, error); - success = (stream != NULL); + camel_imapx_command_unref (ic); + ic = NULL; - g_clear_object (&stream); + if (!success) + break; + } + } + } + } } - return success; -} + g_signal_handler_disconnect (folder->summary, changed_meanwhile_handler_id); + + if (success) { + CamelStore *parent_store; + guint32 unseen; + + parent_store = camel_folder_get_parent_store (folder); -gboolean -camel_imapx_server_copy_message (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - CamelIMAPXMailbox *destination, - GPtrArray *uids, - gboolean delete_originals, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXJob *job; - CopyMessagesData *data; - gint ii; - gboolean success; + camel_folder_summary_lock (folder->summary); - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (destination), FALSE); - g_return_val_if_fail (uids != NULL, FALSE); + for (i = 0; i < changed_uids->len; i++) { + CamelIMAPXMessageInfo *xinfo = (CamelIMAPXMessageInfo *) camel_folder_summary_get (folder->summary, + changed_uids->pdata[i]); - /* That's okay if the "SELECT" fails here, as it can be due to - the folder being write-only; just ignore the error and continue. */ - imapx_ensure_mailbox_permanentflags (is, destination, cancellable, NULL); + if (!xinfo) + continue; - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; + xinfo->server_flags = xinfo->info.flags & CAMEL_IMAPX_SERVER_FLAGS; + if (!remove_deleted_flags || + !(xinfo->info.flags & CAMEL_MESSAGE_DELETED)) { + xinfo->info.flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED; + } else { + /* to stare back the \Deleted flag */ + xinfo->server_flags &= ~CAMEL_MESSAGE_DELETED; + xinfo->info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED; + } + xinfo->info.dirty = TRUE; + if ((permanentflags & CAMEL_MESSAGE_USER) != 0 || + camel_flag_list_size (&xinfo->server_user_flags) == 0) + camel_flag_list_copy (&xinfo->server_user_flags, &xinfo->info.user_flags); - data = g_slice_new0 (CopyMessagesData); - data->destination = g_object_ref (destination); - data->uids = g_ptr_array_new (); - data->delete_originals = delete_originals; + if (g_hash_table_lookup (changed_meanwhile, changed_uids->pdata[i])) + xinfo->info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED; - /* If we're moving messages, prefer "UID MOVE" if supported. */ - if (data->delete_originals) { - if (CAMEL_IMAPX_HAVE_CAPABILITY (is->cinfo, MOVE)) { - data->delete_originals = FALSE; - data->use_move_command = TRUE; + camel_folder_summary_touch (folder->summary); + camel_message_info_unref (xinfo); } - } - for (ii = 0; ii < uids->len; ii++) - g_ptr_array_add (data->uids, g_strdup (uids->pdata[ii])); + camel_folder_summary_unlock (folder->summary); + + /* Apply the changes to server-side unread count; it won't tell + * us of these changes, of course. */ + unseen = camel_imapx_mailbox_get_unseen (mailbox); + unseen += unread_change; + camel_imapx_mailbox_set_unseen (mailbox, unseen); - job = camel_imapx_job_new (cancellable); - job->pri = IMAPX_PRIORITY_COPY_MESSAGE; - job->type = IMAPX_JOB_COPY_MESSAGE; - job->start = imapx_job_copy_messages_start; - job->matches = imapx_job_copy_messages_matches; + if (folder->summary && (folder->summary->flags & CAMEL_FOLDER_SUMMARY_DIRTY) != 0) { + CamelStoreInfo *si; - camel_imapx_job_set_mailbox (job, mailbox); + /* ... and store's summary when folder's summary is dirty */ + si = camel_store_summary_path (CAMEL_IMAPX_STORE (parent_store)->summary, camel_folder_get_full_name (folder)); + if (si) { + if (si->total != camel_folder_summary_get_saved_count (folder->summary) || + si->unread != camel_folder_summary_get_unread_count (folder->summary)) { + si->total = camel_folder_summary_get_saved_count (folder->summary); + si->unread = camel_folder_summary_get_unread_count (folder->summary); + camel_store_summary_touch (CAMEL_IMAPX_STORE (parent_store)->summary); + } - camel_imapx_job_set_data ( - job, data, (GDestroyNotify) copy_messages_data_free); + camel_store_summary_info_unref (CAMEL_IMAPX_STORE (parent_store)->summary, si); + } + } - success = imapx_submit_job (is, job, error); + camel_folder_summary_save_to_db (folder->summary, NULL); + camel_store_summary_save (CAMEL_IMAPX_STORE (parent_store)->summary); + } - camel_imapx_job_unref (job); + imapx_sync_free_user (on_user); + imapx_sync_free_user (off_user); + camel_folder_free_uids (folder, changed_uids); + g_hash_table_destroy (changed_meanwhile); + g_object_unref (folder); return success; } gboolean -camel_imapx_server_append_message (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - CamelFolderSummary *summary, - CamelDataCache *message_cache, - CamelMimeMessage *message, - const CamelMessageInfo *mi, - gchar **appended_uid, - GCancellable *cancellable, - GError **error) +camel_imapx_server_expunge_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) { - gchar *uid = NULL, *path = NULL; - CamelMimeFilter *filter; - CamelIMAPXJob *job; - CamelMessageInfo *info; - GIOStream *base_stream; - GOutputStream *output_stream; - GOutputStream *filter_stream; - AppendMessageData *data; - gint res; - time_t date_time; + CamelFolder *folder; gboolean success; g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE); - g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), FALSE); - g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE); - /* CamelMessageInfo can be NULL. */ - /* That's okay if the "SELECT" fails here, as it can be due to - the folder being write-only; just ignore the error and continue. */ - imapx_ensure_mailbox_permanentflags (is, mailbox, cancellable, NULL); + folder = imapx_server_ref_folder (is, mailbox); + g_return_val_if_fail (folder != NULL, FALSE); - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; + success = camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error); - /* Append just assumes we have no/a dodgy connection. We dump - * stuff into the 'new' directory, and let the summary know it's - * there. Then we fire off a no-reply job which will asynchronously - * upload the message at some point in the future, and fix up the - * summary to match */ + if (success) { + CamelIMAPXCommand *ic; - /* chen cleanup this later */ - uid = imapx_get_temp_uid (); - base_stream = camel_data_cache_add (message_cache, "new", uid, error); - if (base_stream == NULL) { - g_prefix_error (error, _("Cannot create spool file: ")); - g_free (uid); - return FALSE; - } + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_EXPUNGE, "EXPUNGE"); - output_stream = g_io_stream_get_output_stream (base_stream); - filter = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_CRLF); - filter_stream = camel_filter_output_stream_new (output_stream, filter); + success = camel_imapx_server_process_command_sync (is, ic, _("Error expunging message"), cancellable, error); + if (success) { + GPtrArray *uids; + CamelStore *parent_store; + const gchar *full_name; - g_filter_output_stream_set_close_base_stream ( - G_FILTER_OUTPUT_STREAM (filter_stream), FALSE); + full_name = camel_folder_get_full_name (folder); + parent_store = camel_folder_get_parent_store (folder); - res = camel_data_wrapper_write_to_output_stream_sync ( - CAMEL_DATA_WRAPPER (message), - filter_stream, cancellable, error); + camel_folder_summary_lock (folder->summary); - g_object_unref (base_stream); - g_object_unref (filter_stream); - g_object_unref (filter); + camel_folder_summary_save_to_db (folder->summary, NULL); + uids = camel_db_get_folder_deleted_uids (parent_store->cdb_r, full_name, NULL); - if (res == -1) { - g_prefix_error (error, _("Cannot create spool file: ")); - camel_data_cache_remove (message_cache, "new", uid, NULL); - g_free (uid); - return FALSE; - } + if (uids && uids->len) { + CamelFolderChangeInfo *changes; + GList *removed = NULL; + gint i; + + changes = camel_folder_change_info_new (); + for (i = 0; i < uids->len; i++) { + camel_folder_change_info_remove_uid (changes, uids->pdata[i]); + removed = g_list_prepend (removed, (gpointer) uids->pdata[i]); + } - date_time = camel_mime_message_get_date (message, NULL); - path = camel_data_cache_get_filename (message_cache, "new", uid); - info = camel_folder_summary_info_new_from_message ( - summary, message, NULL); - info->uid = camel_pstring_strdup (uid); - if (mi != NULL) { - CamelMessageInfoBase *base_info = (CamelMessageInfoBase *) info; - const CamelFlag *flag; - const CamelTag *tag; + camel_folder_summary_remove_uids (folder->summary, removed); + camel_folder_summary_save_to_db (folder->summary, NULL); - base_info->flags = camel_message_info_flags (mi); - base_info->size = camel_message_info_size (mi); + camel_folder_changed (folder, changes); + camel_folder_change_info_free (changes); - flag = camel_message_info_user_flags (mi); - while (flag != NULL) { - if (*flag->name != '\0') - camel_flag_set ( - &base_info->user_flags, - flag->name, TRUE); - flag = flag->next; - } + g_list_free (removed); + g_ptr_array_foreach (uids, (GFunc) camel_pstring_free, NULL); + } - tag = camel_message_info_user_tags (mi); - while (tag != NULL) { - if (*tag->name != '\0') - camel_tag_set ( - &base_info->user_tags, - tag->name, tag->value); - tag = tag->next; + if (uids) + g_ptr_array_free (uids, TRUE); + + camel_folder_summary_unlock (folder->summary); } - if (date_time <= 0) - date_time = camel_message_info_date_received (mi); + camel_imapx_command_unref (ic); } - g_free (uid); + g_clear_object (&folder); - if (camel_mime_message_has_attachment (message)) - ((CamelMessageInfoBase *) info)->flags |= CAMEL_MESSAGE_ATTACHMENTS; + return success; +} + +gboolean +camel_imapx_server_list_sync (CamelIMAPXServer *is, + const gchar *pattern, + CamelStoreGetFolderInfoFlags flags, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXCommand *ic; + gboolean success; + + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (pattern != NULL, FALSE); - /* So, we actually just want to let the server loop that - * messages need appending, i think. This is so the same - * mechanism is used for normal uploading as well as - * offline re-syncing when we go back online */ + if (is->priv->list_return_opts != NULL) { + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LIST, "LIST \"\" %s RETURN (%t)", + pattern, is->priv->list_return_opts); + } else { + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LIST, "LIST \"\" %s", + pattern); + } - data = g_slice_new0 (AppendMessageData); - data->info = info; /* takes ownership */ - data->path = path; /* takes ownership */ - data->date_time = date_time; - data->appended_uid = NULL; + success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching folders"), cancellable, error); - job = camel_imapx_job_new (cancellable); - job->pri = IMAPX_PRIORITY_APPEND_MESSAGE; - job->type = IMAPX_JOB_APPEND_MESSAGE; - job->start = imapx_job_append_message_start; - job->noreply = FALSE; + camel_imapx_command_unref (ic); - camel_imapx_job_set_mailbox (job, mailbox); + if (!success) + return FALSE; - camel_imapx_job_set_data ( - job, data, (GDestroyNotify) append_message_data_free); + if (!is->priv->list_return_opts) { + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LSUB, "LSUB \"\" %s", + pattern); - success = imapx_submit_job (is, job, error); + success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching subscribed folders"), cancellable, error); - if (appended_uid != NULL) { - *appended_uid = data->appended_uid; - data->appended_uid = NULL; + camel_imapx_command_unref (ic); } - camel_imapx_job_unref (job); - return success; } gboolean -camel_imapx_server_noop (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - GCancellable *cancellable, - GError **error) +camel_imapx_server_create_mailbox_sync (CamelIMAPXServer *is, + const gchar *mailbox_name, + GCancellable *cancellable, + GError **error) { - CamelIMAPXJob *job; + CamelIMAPXCommand *ic; gboolean success; g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - /* Mailbox may be NULL. */ - - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_NOOP; - job->start = imapx_job_noop_start; - job->pri = IMAPX_PRIORITY_NOOP; - - camel_imapx_job_set_mailbox (job, mailbox); + g_return_val_if_fail (mailbox_name != NULL, FALSE); - success = imapx_submit_job (is, job, error); + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_CREATE_MAILBOX, "CREATE %m", mailbox_name); - camel_imapx_job_unref (job); + success = camel_imapx_server_process_command_sync (is, ic, _("Error creating folder"), cancellable, error); - return success; -} + camel_imapx_command_unref (ic); -CamelFolderChangeInfo * -camel_imapx_server_refresh_info (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXJob *job; - RefreshInfoData *data; - CamelFolderChangeInfo *changes = NULL; - gboolean registered = TRUE; - const gchar *mailbox_name; + if (success) { + gchar *utf7_pattern; - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), NULL); + utf7_pattern = camel_utf8_utf7 (mailbox_name); - /* Don't run concurrent refreshes on the same mailbox. - * If a refresh is already in progress, let it finish - * and return no changes for this refresh request. */ - job = imapx_server_ref_job (is, mailbox, IMAPX_JOB_REFRESH_INFO, NULL); + /* List the new mailbox so we trigger our untagged + * LIST handler. This simulates being notified of + * a newly-created mailbox, so we can just let the + * callback functions handle the bookkeeping. */ + success = camel_imapx_server_list_sync (is, utf7_pattern, 0, cancellable, error); - if (job != NULL) { - camel_imapx_job_unref (job); - return camel_folder_change_info_new (); + g_free (utf7_pattern); } - if (!imapx_ensure_mailbox_permanentflags (is, mailbox, cancellable, error)) - return NULL; - - QUEUE_LOCK (is); + return success; +} - data = g_slice_new0 (RefreshInfoData); - data->changes = camel_folder_change_info_new (); +gboolean +camel_imapx_server_delete_mailbox_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXCommand *ic; + CamelIMAPXMailbox *inbox; + CamelIMAPXStore *imapx_store; + gboolean success; - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_REFRESH_INFO; - job->start = imapx_job_refresh_info_start; - job->matches = imapx_job_refresh_info_matches; - job->pri = IMAPX_PRIORITY_REFRESH_INFO; + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - camel_imapx_job_set_mailbox (job, mailbox); + /* Avoid camel_imapx_job_set_mailbox() here. We + * don't want to select the mailbox to be deleted. */ - mailbox_name = camel_imapx_mailbox_get_name (mailbox); + imapx_store = camel_imapx_server_ref_store (is); + /* Keep going, even if this returns NULL. */ + inbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX"); - if (camel_imapx_mailbox_is_inbox (mailbox_name)) - job->pri += 10; + /* Make sure the to-be-deleted folder is not + * selected by selecting INBOX for this operation. */ + success = camel_imapx_server_ensure_selected_sync (is, inbox, cancellable, error); + if (!success) { + g_clear_object (&inbox); + g_clear_object (&imapx_store); + return FALSE; + } - camel_imapx_job_set_data ( - job, data, (GDestroyNotify) refresh_info_data_free); + /* Just to make sure it'll not disappeare before the end of this function */ + g_object_ref (mailbox); - registered = imapx_register_job (is, job, error); + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_DELETE_MAILBOX, "DELETE %M", mailbox); - QUEUE_UNLOCK (is); + success = camel_imapx_server_process_command_sync (is, ic, _("Error deleting folder"), cancellable, error); - if (registered) - camel_imapx_job_guard_mailbox_update (job, mailbox); + camel_imapx_command_unref (ic); - if (registered && camel_imapx_job_run (job, is, error)) { - changes = data->changes; - data->changes = NULL; - } else if (registered) { - imapx_unregister_job (is, job); + if (success) { + camel_imapx_mailbox_deleted (mailbox); + camel_imapx_store_emit_mailbox_updated (imapx_store, mailbox); } - camel_imapx_job_unref (job); + g_clear_object (&inbox); + g_clear_object (&imapx_store); + g_clear_object (&mailbox); - return changes; + return success; } -static void -imapx_sync_free_user (GArray *user_set) +gboolean +camel_imapx_server_rename_mailbox_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + const gchar *new_mailbox_name, + GCancellable *cancellable, + GError **error) { - gint i; - - if (user_set == NULL) - return; - - for (i = 0; i < user_set->len; i++) { - struct _imapx_flag_change *flag_change = &g_array_index (user_set, struct _imapx_flag_change, i); - GPtrArray *infos = flag_change->infos; - gint j; - - for (j = 0; j < infos->len; j++) { - CamelMessageInfo *info = g_ptr_array_index (infos, j); - camel_message_info_unref (info); - } + CamelIMAPXCommand *ic; + CamelIMAPXMailbox *inbox; + CamelIMAPXStore *imapx_store; + gboolean success; - g_ptr_array_free (infos, TRUE); - g_free (flag_change->name); - } - g_array_free (user_set, TRUE); -} + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + g_return_val_if_fail (new_mailbox_name != NULL, FALSE); -static void -imapx_unset_folder_flagged_flag (CamelFolderSummary *summary, - GPtrArray *changed_uids, - gboolean except_deleted_messages) -{ - CamelMessageInfo *info; - gboolean changed = FALSE; - gint ii; + imapx_store = camel_imapx_server_ref_store (is); + inbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX"); + g_return_val_if_fail (inbox != NULL, FALSE); - g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary)); - g_return_if_fail (changed_uids != NULL); + /* We don't want to select the mailbox to be renamed. */ + success = camel_imapx_server_ensure_selected_sync (is, inbox, cancellable, error); + if (!success) { + g_clear_object (&inbox); + g_clear_object (&imapx_store); + return FALSE; + } - for (ii = 0; ii < changed_uids->len; ii++) { - info = camel_folder_summary_get (summary, changed_uids->pdata[ii]); + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_RENAME_MAILBOX, "RENAME %M %m", mailbox, new_mailbox_name); - if (info) { - CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info; + success = camel_imapx_server_process_command_sync (is, ic, _("Error renaming folder"), cancellable, error); - /* some infos could be only 'dirty' (needed to save into summary) */ - if ((mi->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0 && - (!except_deleted_messages || (mi->flags & CAMEL_MESSAGE_DELETED) == 0)) { - mi->flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED; - mi->dirty = TRUE; - changed = TRUE; - } + camel_imapx_command_unref (ic); - camel_message_info_unref (info); - } - } + if (success) { + /* Perform the same processing as imapx_untagged_list() + * would if the server notified us of a renamed mailbox. */ - if (changed) { - camel_folder_summary_touch (summary); - camel_folder_summary_save_to_db (summary, NULL); + camel_imapx_store_handle_mailbox_rename (imapx_store, mailbox, new_mailbox_name); } + + g_clear_object (&inbox); + g_clear_object (&imapx_store); + + return success; } -static gboolean -imapx_server_sync_changes (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - guint32 job_type, - gint pri, - GCancellable *cancellable, - GError **error) +gboolean +camel_imapx_server_subscribe_mailbox_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) { - guint i, on_orset, off_orset; - GPtrArray *changed_uids; - GArray *on_user = NULL, *off_user = NULL; - CamelFolder *folder; - CamelIMAPXMessageInfo *info; - CamelIMAPXJob *job; - CamelIMAPXSettings *settings; - SyncChangesData *data; - gboolean use_real_junk_path; - gboolean use_real_trash_path; - gboolean remove_deleted_flags; - gboolean nothing_to_do; - gboolean registered; - gboolean own_allocated_changed_uids = FALSE; - gboolean success = TRUE; + CamelIMAPXCommand *ic; + gboolean success; - folder = imapx_server_ref_folder (is, mailbox); - g_return_val_if_fail (folder != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - if (!imapx_ensure_mailbox_permanentflags (is, mailbox, cancellable, error)) - return FALSE; + /* We don't want to select the mailbox to be subscribed. */ + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_SUBSCRIBE_MAILBOX, "SUBSCRIBE %M", mailbox); - /* We calculate two masks, a mask of all flags which have been - * turned off and a mask of all flags which have been turned - * on. If either of these aren't 0, then we have work to do, - * and we fire off a job to do it. - * - * User flags are a bit more tricky, we rely on the user - * flags being sorted, and then we create a bunch of lists; - * one for each flag being turned off, including each - * info being turned off, and one for each flag being turned on. - */ - changed_uids = camel_folder_summary_get_changed (folder->summary); + success = camel_imapx_server_process_command_sync (is, ic, _("Error subscribing to folder"), cancellable, error); - if (changed_uids->len == 0) { - camel_folder_free_uids (folder, changed_uids); - g_object_unref (folder); - return TRUE; - } + camel_imapx_command_unref (ic); - settings = camel_imapx_server_ref_settings (is); - use_real_junk_path = - camel_imapx_settings_get_use_real_junk_path (settings); - use_real_trash_path = - camel_imapx_settings_get_use_real_trash_path (settings); - g_object_unref (settings); + if (success) { + CamelIMAPXStore *imapx_store; - remove_deleted_flags = use_real_trash_path && (job_type != IMAPX_JOB_EXPUNGE) != 0; + /* Perform the same processing as imapx_untagged_list() + * would if the server notified us of a subscription. */ - off_orset = on_orset = 0; - for (i = 0; i < changed_uids->len; i++) { - guint32 flags, sflags; - CamelFlag *uflags, *suflags; - const gchar *uid; - gboolean move_to_real_junk; - gboolean move_to_real_trash; - guint j = 0; + imapx_store = camel_imapx_server_ref_store (is); - uid = g_ptr_array_index (changed_uids, i); + camel_imapx_mailbox_subscribed (mailbox); + camel_imapx_store_emit_mailbox_updated (imapx_store, mailbox); - info = (CamelIMAPXMessageInfo *) - camel_folder_summary_get (folder->summary, uid); + g_clear_object (&imapx_store); + } - if (info == NULL) - continue; + return success; +} - if (!(info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED)) { - camel_message_info_unref (info); - continue; - } +gboolean +camel_imapx_server_unsubscribe_mailbox_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXCommand *ic; + gboolean success; - flags = info->info.flags & CAMEL_IMAPX_SERVER_FLAGS; - sflags = info->server_flags & CAMEL_IMAPX_SERVER_FLAGS; + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - move_to_real_junk = - use_real_junk_path && - (flags & CAMEL_MESSAGE_JUNK); - - move_to_real_trash = - use_real_trash_path && - (flags & CAMEL_MESSAGE_DELETED); - - if (move_to_real_junk) - camel_imapx_folder_add_move_to_real_junk ( - CAMEL_IMAPX_FOLDER (folder), uid); - - if (move_to_real_trash) - camel_imapx_folder_add_move_to_real_trash ( - CAMEL_IMAPX_FOLDER (folder), uid); + /* We don't want to select the mailbox to be unsubscribed. */ + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_UNSUBSCRIBE_MAILBOX, "UNSUBSCRIBE %M", mailbox); - if (flags != sflags) { - off_orset |= (flags ^ sflags) & ~flags; - on_orset |= (flags ^ sflags) & flags; - } + success = camel_imapx_server_process_command_sync (is, ic, _("Error unsubscribing from folder"), cancellable, error); - uflags = info->info.user_flags; - suflags = info->server_user_flags; - while (uflags || suflags) { - gint res; + camel_imapx_command_unref (ic); - if (uflags) { - if (suflags) - res = strcmp (uflags->name, suflags->name); - else if (*uflags->name) - res = -1; - else { - uflags = uflags->next; - continue; - } - } else { - res = 1; - } + if (success) { + CamelIMAPXStore *imapx_store; - if (res == 0) { - uflags = uflags->next; - suflags = suflags->next; - } else { - GArray *user_set; - CamelFlag *user_flag; - struct _imapx_flag_change *change = NULL, add = { 0 }; + /* Perform the same processing as imapx_untagged_list() + * would if the server notified us of an unsubscription. */ - if (res < 0) { - if (on_user == NULL) - on_user = g_array_new (FALSE, FALSE, sizeof (struct _imapx_flag_change)); - user_set = on_user; - user_flag = uflags; - uflags = uflags->next; - } else { - if (off_user == NULL) - off_user = g_array_new (FALSE, FALSE, sizeof (struct _imapx_flag_change)); - user_set = off_user; - user_flag = suflags; - suflags = suflags->next; - } + imapx_store = camel_imapx_server_ref_store (is); - /* Could sort this and binary search */ - for (j = 0; j < user_set->len; j++) { - change = &g_array_index (user_set, struct _imapx_flag_change, j); - if (strcmp (change->name, user_flag->name) == 0) - goto found; - } - add.name = g_strdup (user_flag->name); - add.infos = g_ptr_array_new (); - g_array_append_val (user_set, add); - change = &add; - found: - camel_message_info_ref (info); - g_ptr_array_add (change->infos, info); - } - } + camel_imapx_mailbox_unsubscribed (mailbox); + camel_imapx_store_emit_mailbox_updated (imapx_store, mailbox); - camel_message_info_unref (info); + g_clear_object (&imapx_store); } - nothing_to_do = - (on_orset == 0) && - (off_orset == 0) && - (on_user == NULL) && - (off_user == NULL); + return success; +} - if (nothing_to_do) { - imapx_sync_free_user (on_user); - imapx_sync_free_user (off_user); - imapx_unset_folder_flagged_flag (folder->summary, changed_uids, remove_deleted_flags); - camel_folder_free_uids (folder, changed_uids); - g_object_unref (folder); - return TRUE; - } +gboolean +camel_imapx_server_update_quota_info_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXCommand *ic; + gboolean success; - /* TODO above code should go into changes_start */ + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - job = imapx_server_ref_job (is, mailbox, IMAPX_JOB_SYNC_CHANGES, NULL); + g_mutex_lock (&is->priv->stream_lock); - if (job != NULL) { - GPtrArray *new_changed_uids; - GHashTable *known_uids; - GHashTableIter iter; - gpointer key, value; - gint ii; + if (CAMEL_IMAPX_LACK_CAPABILITY (is->priv->cinfo, QUOTA)) { + g_mutex_unlock (&is->priv->stream_lock); - known_uids = g_hash_table_new (g_str_hash, g_str_equal); - data = camel_imapx_job_get_data (job); + g_set_error_literal ( + error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("IMAP server does not support quotas")); + return FALSE; + } else { + g_mutex_unlock (&is->priv->stream_lock); + } - if (data && data->changed_uids) { - for (ii = 0; ii < changed_uids->len; ii++) { - g_hash_table_insert (known_uids, changed_uids->pdata[ii], GINT_TO_POINTER (1)); - } + success = camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error); + if (!success) + return FALSE; - for (ii = 0; ii < data->changed_uids->len; ii++) { - g_hash_table_remove (known_uids, data->changed_uids->pdata[ii]); - } - } + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_UPDATE_QUOTA_INFO, "GETQUOTAROOT %M", mailbox); - if (g_hash_table_size (known_uids) == 0) { - /* The pending job stores changes for the same UIDs */ - if (pri > job->pri) - job->pri = pri; - - camel_imapx_job_unref (job); - - imapx_sync_free_user (on_user); - imapx_sync_free_user (off_user); - camel_folder_free_uids (folder, changed_uids); - g_object_unref (folder); - g_hash_table_destroy (known_uids); - return TRUE; - } + success = camel_imapx_server_process_command_sync (is, ic, _("Error retrieving quota information"), cancellable, error); - new_changed_uids = g_ptr_array_sized_new (g_hash_table_size (known_uids)); + camel_imapx_command_unref (ic); - /* What left in known_uids are message info changes which are not being - saved in the pending job */ + return success; +} - g_hash_table_iter_init (&iter, known_uids); - while (g_hash_table_iter_next (&iter, &key, &value)) { - g_ptr_array_add (new_changed_uids, (gpointer) camel_pstring_strdup (key)); - } +GPtrArray * +camel_imapx_server_uid_search_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + const gchar *criteria_prefix, + const gchar *search_key, + const gchar * const *words, + GCancellable *cancellable, + GError **error) +{ + CamelIMAPXCommand *ic; + GArray *uid_search_results; + GPtrArray *results = NULL; + gint ii; + gboolean need_charset = FALSE; + gboolean success; - g_hash_table_destroy (known_uids); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), NULL); + g_return_val_if_fail (criteria_prefix != NULL, NULL); - camel_folder_free_uids (folder, changed_uids); - changed_uids = new_changed_uids; + success = camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error); + if (!success) + return FALSE; - /* Why would anyone define a virtual function for the free on the folder? */ - own_allocated_changed_uids = TRUE; + for (ii = 0; !need_charset && words && words[ii]; ii++) { + need_charset = !imapx_util_all_is_ascii (words[ii]); } - QUEUE_LOCK (is); + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_UID_SEARCH, "UID SEARCH"); + if (need_charset) + camel_imapx_command_add (ic, " CHARSET UTF-8"); + if (criteria_prefix && *criteria_prefix) + camel_imapx_command_add (ic, " %t", criteria_prefix); - data = g_slice_new0 (SyncChangesData); - data->folder = g_object_ref (folder); - data->changed_uids = changed_uids; /* takes ownership */ - data->own_allocated_changed_uids = own_allocated_changed_uids; - data->on_set = on_orset; - data->off_set = off_orset; - data->on_user = on_user; /* takes ownership */ - data->off_user = off_user; /* takes ownership */ - data->remove_deleted_flags = remove_deleted_flags; + if (search_key && words) { + for (ii = 0; words[ii]; ii++) { + camel_imapx_command_add (ic, " %t %s", search_key, words[ii]); + } + } - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_SYNC_CHANGES; - job->start = imapx_job_sync_changes_start; - job->matches = imapx_job_sync_changes_matches; - job->pri = pri; + success = camel_imapx_server_process_command_sync (is, ic, _("Search failed"), cancellable, error); - camel_imapx_job_set_mailbox (job, mailbox); + camel_imapx_command_unref (ic); - camel_imapx_job_set_data ( - job, data, (GDestroyNotify) sync_changes_data_free); + g_mutex_lock (&is->priv->search_results_lock); + uid_search_results = is->priv->search_results; + is->priv->search_results = NULL; + g_mutex_unlock (&is->priv->search_results_lock); - registered = imapx_register_job (is, job, error); + if (success) { + guint ii; - QUEUE_UNLOCK (is); + /* Convert the numeric UIDs to strings. */ - if (job_type == IMAPX_JOB_SYNC_CHANGES && registered) - camel_imapx_job_guard_mailbox_update (job, mailbox); + g_return_val_if_fail (uid_search_results != NULL, NULL); - success = registered && camel_imapx_job_run (job, is, error); + results = g_ptr_array_new_full (uid_search_results->len, (GDestroyNotify) camel_pstring_free); - if (!success && registered) - imapx_unregister_job (is, job); + for (ii = 0; ii < uid_search_results->len; ii++) { + const gchar *pooled_uid; + guint64 numeric_uid; + gchar *alloced_uid; - camel_imapx_job_unref (job); + numeric_uid = g_array_index (uid_search_results, guint64, ii); + alloced_uid = g_strdup_printf ("%" G_GUINT64_FORMAT, numeric_uid); + pooled_uid = camel_pstring_add (alloced_uid, TRUE); + g_ptr_array_add (results, (gpointer) pooled_uid); + } + } - g_object_unref (folder); + if (uid_search_results) + g_array_unref (uid_search_results); - return success; + return results; } -gboolean -camel_imapx_server_sync_changes (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - GCancellable *cancellable, - GError **error) +typedef struct _IdleThreadData { + CamelIMAPXServer *is; + GCancellable *idle_cancellable; + gint idle_stamp; +} IdleThreadData; + +static gpointer +imapx_server_idle_thread (gpointer user_data) { - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + IdleThreadData *itd = user_data; + CamelIMAPXServer *is; + CamelIMAPXMailbox *mailbox; + CamelIMAPXCommand *ic; + CamelIMAPXCommandPart *cp; + GCancellable *idle_cancellable; + GError *local_error = NULL; + gint previous_timeout = -1; + gboolean success = FALSE; + gboolean rather_disconnect = FALSE; - return imapx_server_sync_changes ( - is, mailbox, - IMAPX_JOB_SYNC_CHANGES, - IMAPX_PRIORITY_SYNC_CHANGES, - cancellable, error); -} + g_return_val_if_fail (itd != NULL, NULL); + + is = itd->is; + idle_cancellable = itd->idle_cancellable; -/* expunge-uids? */ -gboolean -camel_imapx_server_expunge (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXJob *job; - gboolean registered; - gboolean success; + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); + g_return_val_if_fail (G_IS_CANCELLABLE (idle_cancellable), NULL); - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + g_mutex_lock (&is->priv->idle_lock); - /* Do we really care to wait for this one to finish? */ - job = imapx_server_ref_job (is, mailbox, IMAPX_JOB_EXPUNGE, NULL); + if (g_cancellable_is_cancelled (idle_cancellable) || + is->priv->idle_stamp != itd->idle_stamp || + is->priv->idle_state != IMAPX_IDLE_STATE_SCHEDULED) { + g_cond_broadcast (&is->priv->idle_cond); + g_mutex_unlock (&is->priv->idle_lock); + + g_clear_object (&itd->is); + g_clear_object (&itd->idle_cancellable); + g_free (itd); - if (job != NULL) { - camel_imapx_job_unref (job); - return TRUE; + return NULL; } - QUEUE_LOCK (is); + is->priv->idle_state = IMAPX_IDLE_STATE_PREPARING; + g_cond_broadcast (&is->priv->idle_cond); - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_EXPUNGE; - job->start = imapx_job_expunge_start; - job->matches = imapx_job_expunge_matches; - job->pri = IMAPX_PRIORITY_EXPUNGE; + mailbox = is->priv->idle_mailbox; + if (mailbox) + g_object_ref (mailbox); - camel_imapx_job_set_mailbox (job, mailbox); + g_mutex_unlock (&is->priv->idle_lock); - registered = imapx_register_job (is, job, error); + if (!mailbox) + mailbox = camel_imapx_server_ref_selected (is); + + if (!mailbox) + goto exit; - QUEUE_UNLOCK (is); + success = camel_imapx_server_ensure_selected_sync (is, mailbox, idle_cancellable, &local_error); + if (!success) { + rather_disconnect = TRUE; + goto exit; + } - success = registered && camel_imapx_job_run (job, is, error); + ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_IDLE, "IDLE"); + camel_imapx_command_close (ic); - if (!success && registered) - imapx_unregister_job (is, job); + cp = g_queue_peek_head (&ic->parts); + cp->type |= CAMEL_IMAPX_COMMAND_CONTINUATION; - camel_imapx_job_unref (job); + g_mutex_lock (&is->priv->stream_lock); + /* Set the connection timeout to one minute more than the inactivity timeout */ + if (is->priv->connection) + previous_timeout = imapx_server_set_connection_timeout (is->priv->connection, INACTIVITY_TIMEOUT_SECONDS + 60); + g_mutex_unlock (&is->priv->stream_lock); - return success; -} + g_mutex_lock (&is->priv->idle_lock); + if (is->priv->idle_stamp == itd->idle_stamp && + is->priv->idle_state == IMAPX_IDLE_STATE_PREPARING) { + g_mutex_unlock (&is->priv->idle_lock); -gboolean -camel_imapx_server_list (CamelIMAPXServer *is, - const gchar *pattern, - CamelStoreGetFolderInfoFlags flags, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXJob *job; - ListData *data; - gboolean success; + /* Blocks, until the DONE is issued or on inactivity timeout, error, ... */ + success = camel_imapx_server_process_command_sync (is, ic, _("Error running IDLE"), idle_cancellable, &local_error); - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_return_val_if_fail (pattern != NULL, FALSE); + rather_disconnect = rather_disconnect || !success || g_cancellable_is_cancelled (idle_cancellable); + } else { + g_mutex_unlock (&is->priv->idle_lock); + } - data = g_slice_new0 (ListData); - data->pattern = g_strdup (pattern); + if (previous_timeout >= 0) { + g_mutex_lock (&is->priv->stream_lock); + if (is->priv->connection) + imapx_server_set_connection_timeout (is->priv->connection, previous_timeout); + g_mutex_unlock (&is->priv->stream_lock); + } - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_LIST; - job->start = imapx_job_list_start; - job->matches = imapx_job_list_matches; - job->pri = IMAPX_PRIORITY_LIST; + camel_imapx_command_unref (ic); - camel_imapx_job_set_data ( - job, data, (GDestroyNotify) list_data_free); + exit: + g_mutex_lock (&is->priv->idle_lock); + g_clear_object (&is->priv->idle_cancellable); + is->priv->idle_state = IMAPX_IDLE_STATE_OFF; + g_cond_broadcast (&is->priv->idle_cond); + g_mutex_unlock (&is->priv->idle_lock); + + if (success) + c (camel_imapx_server_get_tagprefix (is), "IDLE finished successfully\n"); + else if (local_error) + c (camel_imapx_server_get_tagprefix (is), "IDLE finished with error: %s%s\n", local_error->message, rather_disconnect ? "; rather disconnect" : ""); + else + c (camel_imapx_server_get_tagprefix (is), "IDLE finished without error%s\n", rather_disconnect ? "; rather disconnect" : ""); - /* sync operation which is triggered by user */ - if (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) - job->pri += 300; + if (rather_disconnect) { + imapx_disconnect (is); + } - success = imapx_submit_job (is, job, error); + g_clear_object (&mailbox); + g_clear_error (&local_error); - camel_imapx_job_unref (job); + g_clear_object (&itd->is); + g_clear_object (&itd->idle_cancellable); + g_free (itd); - return success; + return NULL; } -gboolean -camel_imapx_server_create_mailbox (CamelIMAPXServer *is, - const gchar *mailbox_name, - GCancellable *cancellable, - GError **error) +static gboolean +imapx_server_run_idle_thread_cb (gpointer user_data) { - CamelIMAPXJob *job; - MailboxData *data; - gboolean success; - - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_return_val_if_fail (mailbox_name != NULL, FALSE); - - data = g_slice_new0 (MailboxData); - data->mailbox_name = g_strdup (mailbox_name); + GWeakRef *is_weakref = user_data; + CamelIMAPXServer *is; - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_CREATE_MAILBOX; - job->start = imapx_job_create_mailbox_start; - job->pri = IMAPX_PRIORITY_MAILBOX_MGMT; + g_return_val_if_fail (is_weakref != NULL, FALSE); - camel_imapx_job_set_data ( - job, data, (GDestroyNotify) mailbox_data_free); + is = g_weak_ref_get (is_weakref); + if (!is) + return FALSE; - success = imapx_submit_job (is, job, error); + g_mutex_lock (&is->priv->idle_lock); - if (success) { - gchar *utf7_pattern; + if (g_main_current_source () == is->priv->idle_pending) { + if (!g_source_is_destroyed (g_main_current_source ()) && + is->priv->idle_state == IMAPX_IDLE_STATE_SCHEDULED) { + IdleThreadData *itd; + GThread *thread; + GError *local_error = NULL; + + itd = g_new0 (IdleThreadData, 1); + itd->is = g_object_ref (is); + itd->idle_cancellable = g_object_ref (is->priv->idle_cancellable); + itd->idle_stamp = is->priv->idle_stamp; + + thread = g_thread_try_new (NULL, imapx_server_idle_thread, itd, &local_error); + if (thread) { + g_thread_unref (thread); + } else { + g_warning ("%s: Failed to create IDLE thread: %s", G_STRFUNC, local_error ? local_error->message : "Unknown error"); - utf7_pattern = camel_utf8_utf7 (mailbox_name); + g_clear_object (&itd->is); + g_clear_object (&itd->idle_cancellable); + g_free (itd); + } - /* List the new mailbox so we trigger our untagged - * LIST handler. This simulates being notified of - * a newly-created mailbox, so we can just let the - * callback functions handle the bookkeeping. */ - success = camel_imapx_server_list ( - is, utf7_pattern, 0, cancellable, error); + g_clear_error (&local_error); + } - g_free (utf7_pattern); + g_source_unref (is->priv->idle_pending); + is->priv->idle_pending = NULL; } - camel_imapx_job_unref (job); + g_mutex_unlock (&is->priv->idle_lock); - return success; + return FALSE; } gboolean -camel_imapx_server_delete_mailbox (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - GCancellable *cancellable, - GError **error) +camel_imapx_server_can_use_idle (CamelIMAPXServer *is) { - CamelIMAPXJob *job; - MailboxData *data; - gboolean success; - - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + gboolean use_idle = FALSE; - /* Avoid camel_imapx_job_set_mailbox() here. We - * don't want to select the mailbox to be deleted. */ + g_mutex_lock (&is->priv->stream_lock); - data = g_slice_new0 (MailboxData); - data->mailbox = g_object_ref (mailbox); + /* No need for IDLE if the server supports NOTIFY. */ + if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, NOTIFY)) { + g_mutex_unlock (&is->priv->stream_lock); - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_DELETE_MAILBOX; - job->start = imapx_job_delete_mailbox_start; - job->pri = IMAPX_PRIORITY_MAILBOX_MGMT; + return FALSE; + } - camel_imapx_job_set_data ( - job, data, (GDestroyNotify) mailbox_data_free); + if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, IDLE)) { + CamelIMAPXSettings *settings; - success = imapx_submit_job (is, job, error); + settings = camel_imapx_server_ref_settings (is); + use_idle = camel_imapx_settings_get_use_idle (settings); + g_object_unref (settings); + } - camel_imapx_job_unref (job); + g_mutex_unlock (&is->priv->stream_lock); - return success; + return use_idle; } gboolean -camel_imapx_server_rename_mailbox (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - const gchar *new_mailbox_name, - GCancellable *cancellable, - GError **error) +camel_imapx_server_is_in_idle (CamelIMAPXServer *is) { - CamelIMAPXJob *job; - MailboxData *data; - gboolean success; + gboolean in_idle; g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - g_return_val_if_fail (new_mailbox_name != NULL, FALSE); - - /* Avoid camel_imapx_job_set_mailbox() here. We - * don't want to select the mailbox to be renamed. */ - - data = g_slice_new0 (MailboxData); - data->mailbox = g_object_ref (mailbox); - data->mailbox_name = g_strdup (new_mailbox_name); - - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_RENAME_MAILBOX; - job->start = imapx_job_rename_mailbox_start; - job->pri = IMAPX_PRIORITY_MAILBOX_MGMT; - - camel_imapx_job_set_data ( - job, data, (GDestroyNotify) mailbox_data_free); - success = imapx_submit_job (is, job, error); + g_mutex_lock (&is->priv->idle_lock); + in_idle = is->priv->idle_state != IMAPX_IDLE_STATE_OFF; + g_mutex_unlock (&is->priv->idle_lock); - camel_imapx_job_unref (job); - - return success; + return in_idle; } -gboolean -camel_imapx_server_subscribe_mailbox (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - GCancellable *cancellable, - GError **error) +CamelIMAPXMailbox * +camel_imapx_server_ref_idle_mailbox (CamelIMAPXServer *is) { - CamelIMAPXJob *job; - MailboxData *data; - gboolean success; - - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - - /* Avoid camel_imapx_job_set_mailbox() here. We - * don't want to select the mailbox to be subscribed. */ - - data = g_slice_new0 (MailboxData); - data->mailbox = g_object_ref (mailbox); + CamelIMAPXMailbox *mailbox = NULL; - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_SUBSCRIBE_MAILBOX; - job->start = imapx_job_subscribe_mailbox_start; - job->pri = IMAPX_PRIORITY_MAILBOX_MGMT; + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); - camel_imapx_job_set_data ( - job, data, (GDestroyNotify) mailbox_data_free); + g_mutex_lock (&is->priv->idle_lock); - success = imapx_submit_job (is, job, error); + if (is->priv->idle_state != IMAPX_IDLE_STATE_OFF) { + if (is->priv->idle_mailbox) + mailbox = g_object_ref (is->priv->idle_mailbox); + else + mailbox = camel_imapx_server_ref_selected (is); + } - camel_imapx_job_unref (job); + g_mutex_unlock (&is->priv->idle_lock); - return success; + return mailbox; } gboolean -camel_imapx_server_unsubscribe_mailbox (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - GCancellable *cancellable, - GError **error) +camel_imapx_server_schedule_idle_sync (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error) { - CamelIMAPXJob *job; - MailboxData *data; - gboolean success; - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + if (mailbox) + g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); - /* Avoid camel_imapx_job_set_mailbox() here. We - * don't want to select the mailbox to be unsubscribed. */ - - data = g_slice_new0 (MailboxData); - data->mailbox = g_object_ref (mailbox); + if (!camel_imapx_server_stop_idle_sync (is, cancellable, error)) + return FALSE; - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_UNSUBSCRIBE_MAILBOX; - job->start = imapx_job_unsubscribe_mailbox_start; - job->pri = IMAPX_PRIORITY_MAILBOX_MGMT; + if (!camel_imapx_server_can_use_idle (is)) + return TRUE; - camel_imapx_job_set_data ( - job, data, (GDestroyNotify) mailbox_data_free); + g_mutex_lock (&is->priv->idle_lock); - success = imapx_submit_job (is, job, error); + if (is->priv->idle_state != IMAPX_IDLE_STATE_OFF) { + g_warn_if_fail (is->priv->idle_state == IMAPX_IDLE_STATE_OFF); - camel_imapx_job_unref (job); + g_mutex_unlock (&is->priv->idle_lock); - return success; -} + return FALSE; + } -gboolean -camel_imapx_server_update_quota_info (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - GCancellable *cancellable, - GError **error) -{ - CamelIMAPXJob *job; - gboolean success; + g_warn_if_fail (is->priv->idle_cancellable == NULL); - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE); + is->priv->idle_cancellable = g_cancellable_new (); + is->priv->idle_stamp++; - if (CAMEL_IMAPX_LACK_CAPABILITY (is->cinfo, QUOTA)) { - g_set_error_literal ( - error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - _("IMAP server does not support quotas")); - return FALSE; + if (is->priv->idle_pending) { + g_source_destroy (is->priv->idle_pending); + g_source_unref (is->priv->idle_pending); } - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_UPDATE_QUOTA_INFO; - job->start = imapx_job_update_quota_info_start; - job->pri = IMAPX_PRIORITY_UPDATE_QUOTA_INFO; - - camel_imapx_job_set_mailbox (job, mailbox); + g_clear_object (&is->priv->idle_mailbox); + if (mailbox) + is->priv->idle_mailbox = g_object_ref (mailbox); - success = imapx_submit_job (is, job, error); + is->priv->idle_state = IMAPX_IDLE_STATE_SCHEDULED; + is->priv->idle_pending = g_timeout_source_new_seconds (IMAPX_IDLE_WAIT_SECONDS); + g_source_set_callback ( + is->priv->idle_pending, imapx_server_run_idle_thread_cb, + imapx_weak_ref_new (is), (GDestroyNotify) imapx_weak_ref_free); + g_source_attach (is->priv->idle_pending, NULL); - camel_imapx_job_unref (job); + g_mutex_unlock (&is->priv->idle_lock); - return success; + return TRUE; } -GPtrArray * -camel_imapx_server_uid_search (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox, - const gchar *criteria, - GCancellable *cancellable, - GError **error) +static void +imapx_server_wait_idle_stop_cancelled_cb (GCancellable *cancellable, + gpointer user_data) { - CamelIMAPXJob *job; - SearchData *data; - GPtrArray *results = NULL; + CamelIMAPXServer *is = user_data; - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); - g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), NULL); - g_return_val_if_fail (criteria != NULL, NULL); - - data = g_slice_new0 (SearchData); - data->criteria = g_strdup (criteria); - - job = camel_imapx_job_new (cancellable); - job->type = IMAPX_JOB_UID_SEARCH; - job->start = imapx_job_uid_search_start; - job->pri = IMAPX_PRIORITY_SEARCH; + g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is)); - camel_imapx_job_set_mailbox (job, mailbox); + g_mutex_lock (&is->priv->idle_lock); + g_cond_broadcast (&is->priv->idle_cond); + g_mutex_unlock (&is->priv->idle_lock); +} - camel_imapx_job_set_data ( - job, data, (GDestroyNotify) search_data_free); +gboolean +camel_imapx_server_stop_idle_sync (CamelIMAPXServer *is, + GCancellable *cancellable, + GError **error) +{ + GCancellable *idle_cancellable; + gulong handler_id = 0; + gboolean success = TRUE; - if (imapx_submit_job (is, job, error)) { - guint ii; + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - /* Convert the numeric UIDs to strings. */ + g_mutex_lock (&is->priv->idle_lock); - g_return_val_if_fail (data->results != NULL, NULL); + if (is->priv->idle_state == IMAPX_IDLE_STATE_OFF) { + g_mutex_unlock (&is->priv->idle_lock); + return TRUE; + } else if (is->priv->idle_state == IMAPX_IDLE_STATE_SCHEDULED) { + if (is->priv->idle_pending) { + g_source_destroy (is->priv->idle_pending); + g_source_unref (is->priv->idle_pending); + is->priv->idle_pending = NULL; + } - results = g_ptr_array_new_full ( - data->results->len, - (GDestroyNotify) camel_pstring_free); + is->priv->idle_state = IMAPX_IDLE_STATE_OFF; + g_cond_broadcast (&is->priv->idle_cond); + } - for (ii = 0; ii < data->results->len; ii++) { - const gchar *pooled_uid; - guint64 numeric_uid; - gchar *alloced_uid; + idle_cancellable = is->priv->idle_cancellable ? g_object_ref (is->priv->idle_cancellable) : NULL; - numeric_uid = g_array_index ( - data->results, guint64, ii); - alloced_uid = g_strdup_printf ( - "%" G_GUINT64_FORMAT, numeric_uid); - pooled_uid = camel_pstring_add (alloced_uid, TRUE); - g_ptr_array_add (results, (gpointer) pooled_uid); - } - } + g_clear_object (&is->priv->idle_cancellable); + g_clear_object (&is->priv->idle_mailbox); + is->priv->idle_stamp++; - camel_imapx_job_unref (job); + if (cancellable) { + g_mutex_unlock (&is->priv->idle_lock); - return results; -} + /* Do not hold the idle_lock here, because the callback can be called + immediately, which leads to a deadlock inside it. */ + handler_id = g_cancellable_connect (cancellable, G_CALLBACK (imapx_server_wait_idle_stop_cancelled_cb), is, NULL); -gboolean -camel_imapx_server_folder_name_in_jobs (CamelIMAPXServer *imapx_server, - const gchar *folder_path) -{ - gboolean res; + g_mutex_lock (&is->priv->idle_lock); + } - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), FALSE); - g_return_val_if_fail (folder_path != NULL, FALSE); + while (is->priv->idle_state == IMAPX_IDLE_STATE_PREPARING && + !g_cancellable_is_cancelled (cancellable)) { + g_cond_wait (&is->priv->idle_cond, &is->priv->idle_lock); + } - g_mutex_lock (&imapx_server->priv->jobs_prop_lock); + if (is->priv->idle_state == IMAPX_IDLE_STATE_RUNNING && + !g_cancellable_is_cancelled (cancellable)) { + is->priv->idle_state = IMAPX_IDLE_STATE_STOPPING; + g_cond_broadcast (&is->priv->idle_cond); + g_mutex_unlock (&is->priv->idle_lock); - res = GPOINTER_TO_INT (g_hash_table_lookup (imapx_server->priv->jobs_prop_folder_paths, folder_path)) > 0; + g_mutex_lock (&is->priv->stream_lock); + if (is->priv->output_stream) { + gint previous_timeout = -1; - g_mutex_unlock (&imapx_server->priv->jobs_prop_lock); + /* Set the connection timeout to some short time, no need to wait for it for too long */ + if (is->priv->connection) + previous_timeout = imapx_server_set_connection_timeout (is->priv->connection, 5); + + success = g_output_stream_flush (is->priv->output_stream, cancellable, error); + success = success && g_output_stream_write_all (is->priv->output_stream, "DONE\r\n", 6, NULL, cancellable, error); + success = success && g_output_stream_flush (is->priv->output_stream, cancellable, error); - return res; -} + if (previous_timeout >= 0 && is->priv->connection) + imapx_server_set_connection_timeout (is->priv->connection, previous_timeout); + } else { + success = FALSE; -gboolean -camel_imapx_server_has_expensive_command (CamelIMAPXServer *imapx_server) -{ - gboolean res; + /* This message won't get into UI. */ + g_set_error_literal (error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT, + "Reconnect after couldn't issue DONE command"); + } + g_mutex_unlock (&is->priv->stream_lock); + g_mutex_lock (&is->priv->idle_lock); + } - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), FALSE); + while (success && is->priv->idle_state != IMAPX_IDLE_STATE_OFF && + !g_cancellable_is_cancelled (cancellable)) { + g_cond_wait (&is->priv->idle_cond, &is->priv->idle_lock); + } - g_mutex_lock (&imapx_server->priv->jobs_prop_lock); + g_mutex_unlock (&is->priv->idle_lock); - res = imapx_server->priv->jobs_prop_expensive_command_count > 0; + if (cancellable && handler_id) + g_cancellable_disconnect (cancellable, handler_id); - g_mutex_unlock (&imapx_server->priv->jobs_prop_lock); + if (success && g_cancellable_is_cancelled (cancellable)) { + g_clear_error (error); - return res; -} + success = FALSE; -gint -camel_imapx_server_get_command_count (CamelIMAPXServer *imapx_server) -{ - guint32 res; + /* This message won't get into UI. */ + g_set_error_literal (error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT, + "Reconnect after cancelled IDLE stop command"); + } - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), -1); + if (!success) { + if (idle_cancellable) + g_cancellable_cancel (idle_cancellable); - g_mutex_lock (&imapx_server->priv->jobs_prop_lock); + g_mutex_lock (&is->priv->idle_lock); + is->priv->idle_state = IMAPX_IDLE_STATE_OFF; + g_mutex_unlock (&is->priv->idle_lock); - res = imapx_server->priv->jobs_prop_command_count; + imapx_disconnect (is); + } - g_mutex_unlock (&imapx_server->priv->jobs_prop_lock); + g_clear_object (&idle_cancellable); - return res; + return success; } /** @@ -9670,210 +6561,77 @@ camel_imapx_server_register_untagged_han return previous; } -/** - * camel_imapx_server_is_job_in_queue: - * @imapx_server: a #CamelIMAPXServer instance - * @mailbox: a mailbox to search job for - * @job_type: a job type specifier to search for - * @uid: optional message UID for which the job might be searched - * - * Searches queue of jobs for the particular job. The returned job - * is referenced for thread safety, unref it with camel_imapx_job_unref(). - * - * Returns: %NULL, if such job could not be found, or a referenced job. - **/ -CamelIMAPXJob * -camel_imapx_server_ref_job (CamelIMAPXServer *imapx_server, - CamelIMAPXMailbox *mailbox, - guint32 job_type, - const gchar *uid) +/* This function is not thread-safe. */ +const struct _capability_info * +camel_imapx_server_get_capability_info (CamelIMAPXServer *is) { - GList *head, *link; - CamelIMAPXJob *job = NULL; - gboolean found = FALSE; - - g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), NULL); - - QUEUE_LOCK (imapx_server); - - head = g_queue_peek_head_link (&imapx_server->jobs); - - for (link = head; link != NULL; link = g_list_next (link)) { - job = (CamelIMAPXJob *) link->data; - - if (!job || !(job->type & job_type)) - continue; - - if (camel_imapx_job_matches (job, mailbox, uid)) { - found = TRUE; - camel_imapx_job_ref (job); - break; - } - } - - QUEUE_UNLOCK (imapx_server); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); - return found ? job : NULL; + return is->priv->cinfo; } -/** - * camel_imapx_server_shutdown: - * @is: a #CamelIMAPXServer - * @error: a #GError with which cancel any pending jobs - * - * Signals the server to shut down command processing. A #CamelIMAPXStore - * should call this immediately before unreferencing its server instance. - * Note, the server instance may linger a short time after this function - * returns as its own worker threads finish. - * - * Since: 3.12 - **/ -void -camel_imapx_server_shutdown (CamelIMAPXServer *is, - const GError *error) +gboolean +camel_imapx_server_have_capability (CamelIMAPXServer *is, + guint32 capability) { - GCancellable *cancellable; - GError *shutdown_error_copy = NULL; - - g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is)); - - QUEUE_LOCK (is); - - is->state = IMAPX_SHUTDOWN; - - cancellable = g_weak_ref_get (&is->priv->parser_cancellable); - - QUEUE_UNLOCK (is); - - if (!error) { - shutdown_error_copy = imapx_server_dup_shutdown_error (is); - error = shutdown_error_copy; - } - - if (error) { - imapx_abort_all_commands (is, error); - } else { - GError *local_error = NULL; + gboolean have; - g_set_error ( - &local_error, CAMEL_SERVICE_ERROR, - CAMEL_SERVICE_ERROR_UNAVAILABLE, - "Shutting down"); - - imapx_abort_all_commands (is, local_error); - - g_clear_error (&local_error); - } + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); - g_main_loop_quit (is->priv->idle_main_loop); - g_main_loop_quit (is->priv->parser_main_loop); + g_mutex_lock (&is->priv->stream_lock); + have = is->priv->cinfo != NULL && (is->priv->cinfo->capa & capability) != 0; + g_mutex_unlock (&is->priv->stream_lock); - g_cancellable_cancel (cancellable); - g_clear_object (&cancellable); - g_clear_error (&shutdown_error_copy); + return have; } -static const gchar * -imapx_server_get_job_type_name (CamelIMAPXJob *job) +gboolean +camel_imapx_server_lack_capability (CamelIMAPXServer *is, + guint32 capability) { - if (!job) - return "[null]"; + gboolean lack; - switch (job->type) { - case IMAPX_JOB_GET_MESSAGE: - return "GET_MESSAGE"; - case IMAPX_JOB_APPEND_MESSAGE: - return "APPEND_MESSAGE"; - case IMAPX_JOB_COPY_MESSAGE: - return "COPY_MESSAGE"; - case IMAPX_JOB_FETCH_NEW_MESSAGES: - return "FETCH_NEW_MESSAGES"; - case IMAPX_JOB_REFRESH_INFO: - return "REFRESH_INFO"; - case IMAPX_JOB_SYNC_CHANGES: - return "SYNC_CHANGES"; - case IMAPX_JOB_EXPUNGE: - return "EXPUNGE"; - case IMAPX_JOB_NOOP: - return "NOOP"; - case IMAPX_JOB_IDLE: - return "IDLE"; - case IMAPX_JOB_LIST: - return "LIST"; - case IMAPX_JOB_CREATE_MAILBOX: - return "CREATE_MAILBOX"; - case IMAPX_JOB_DELETE_MAILBOX: - return "DELETE_MAILBOX"; - case IMAPX_JOB_RENAME_MAILBOX: - return "RENAME_MAILBOX"; - case IMAPX_JOB_SUBSCRIBE_MAILBOX: - return "SUBSCRIBE_MAILBOX"; - case IMAPX_JOB_UNSUBSCRIBE_MAILBOX: - return "UNSUBSCRIBE_MAILBOX"; - case IMAPX_JOB_UPDATE_QUOTA_INFO: - return "UPDATE_QUOTA_INFO"; - case IMAPX_JOB_UID_SEARCH: - return "UID_SEARCH"; - } + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE); + + g_mutex_lock (&is->priv->stream_lock); + lack = is->priv->cinfo != NULL && (is->priv->cinfo->capa & capability) == 0; + g_mutex_unlock (&is->priv->stream_lock); - return "???"; + return lack; } -static void -imapx_server_dump_one_queue (CamelIMAPXCommandQueue *queue, - const gchar *queue_name) +gchar +camel_imapx_server_get_tagprefix (CamelIMAPXServer *is) { - GList *iter; - gint ii; - - g_return_if_fail (queue != NULL); - g_return_if_fail (queue_name != NULL); - - if (camel_imapx_command_queue_is_empty (queue)) - return; - - printf (" Content of '%s':\n", queue_name); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), 0); - for (ii = 0, iter = camel_imapx_command_queue_peek_head_link (queue); iter != NULL; iter = g_list_next (iter), ii++) { - CamelIMAPXCommand *ic = iter->data; - CamelIMAPXJob *job = camel_imapx_command_get_job (ic); - - printf (" [%d] command:%p for job:%p (type:0x%x %s)\n", ii, ic, job, job ? job->type : 0, imapx_server_get_job_type_name (job)); - } + return is->priv->tagprefix; } -/* for debugging purposes only */ void -camel_imapx_server_dump_queue_status (CamelIMAPXServer *imapx_server) +camel_imapx_server_set_tagprefix (CamelIMAPXServer *is, + gchar tagprefix) { - g_return_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server)); - - QUEUE_LOCK (imapx_server); + g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is)); + g_return_if_fail ((tagprefix >= 'A' && tagprefix <= 'Z') || (tagprefix >= 'a' && tagprefix <= 'z')); - printf (" Queue status for server %p: jobs:%d queued:%d active:%d done:%d\n", imapx_server, - g_queue_get_length (&imapx_server->jobs), - camel_imapx_command_queue_get_length (imapx_server->queue), - camel_imapx_command_queue_get_length (imapx_server->active), - camel_imapx_command_queue_get_length (imapx_server->done)); + is->priv->tagprefix = tagprefix; +} - if (!g_queue_is_empty (&imapx_server->jobs)) { - GList *iter; - gint ii; +CamelIMAPXCommand * +camel_imapx_server_ref_current_command (CamelIMAPXServer *is) +{ + CamelIMAPXCommand *command; - printf (" Content of 'jobs':\n"); + g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL); - for (ii = 0, iter = g_queue_peek_head_link (&imapx_server->jobs); iter != NULL; iter = g_list_next (iter), ii++) { - CamelIMAPXJob *job = iter->data; + COMMAND_LOCK (is); - printf (" [%d] job:%p (type:0x%x %s) with pending commands:%d\n", ii, job, job ? job->type : 0, - imapx_server_get_job_type_name (job), - job ? g_atomic_int_get (&job->commands) : -1); - } - } + command = is->priv->current_command; + if (command) + camel_imapx_command_ref (command); - imapx_server_dump_one_queue (imapx_server->queue, "queue"); - imapx_server_dump_one_queue (imapx_server->active, "active"); - imapx_server_dump_one_queue (imapx_server->done, "done"); + COMMAND_UNLOCK (is); - QUEUE_UNLOCK (imapx_server); + return command; } diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.h.imapx-update-to-upstream 2014-11-07 08:34:59.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-server.h 2016-08-15 13:52:41.974976329 +0200 @@ -2,17 +2,18 @@ /* * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * - * This library is free software; you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . + * */ #ifndef CAMEL_IMAPX_SERVER_H @@ -105,37 +106,14 @@ struct _CamelIMAPXUntaggedRespHandlerDes struct _CamelIMAPXServer { GObject parent; CamelIMAPXServerPrivate *priv; - - /* Info about the current connection */ - struct _capability_info *cinfo; - - /* incoming jobs */ - GQueue jobs; - - gchar tagprefix; - gint state : 4; - - /* Current command/work queue. All commands are stored in one list, - * all the time, so they can be cleaned up in exception cases */ - GRecMutex queue_lock; - CamelIMAPXCommand *literal; - CamelIMAPXCommandQueue *queue; - CamelIMAPXCommandQueue *active; - CamelIMAPXCommandQueue *done; - - gboolean use_qresync; }; struct _CamelIMAPXServerClass { GObjectClass parent_class; /* Signals */ - void (*mailbox_select) (CamelIMAPXServer *is, + void (*refresh_mailbox) (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox); - void (*mailbox_closed) (CamelIMAPXServer *is, - CamelIMAPXMailbox *mailbox); - void (*shutdown) (CamelIMAPXServer *is, - const GError *error); }; GType camel_imapx_server_get_type (void); @@ -151,57 +129,100 @@ GOutputStream * camel_imapx_server_ref_o (CamelIMAPXServer *is); CamelIMAPXMailbox * camel_imapx_server_ref_selected (CamelIMAPXServer *is); -gboolean camel_imapx_server_connect (CamelIMAPXServer *is, +CamelIMAPXMailbox * + camel_imapx_server_ref_pending_or_selected + (CamelIMAPXServer *is); +const struct _capability_info * + camel_imapx_server_get_capability_info + (CamelIMAPXServer *is); +gboolean camel_imapx_server_have_capability + (CamelIMAPXServer *is, + guint32 capability); +gboolean camel_imapx_server_lack_capability + (CamelIMAPXServer *is, + guint32 capability); +gchar camel_imapx_server_get_tagprefix + (CamelIMAPXServer *is); +void camel_imapx_server_set_tagprefix + (CamelIMAPXServer *is, + gchar tagprefix); +CamelIMAPXCommand * + camel_imapx_server_ref_current_command + (CamelIMAPXServer *is); +gboolean camel_imapx_server_connect_sync (CamelIMAPXServer *is, GCancellable *cancellable, GError **error); -gboolean imapx_connect_to_server (CamelIMAPXServer *is, +gboolean camel_imapx_server_disconnect_sync + (CamelIMAPXServer *is, GCancellable *cancellable, GError **error); gboolean camel_imapx_server_is_connected (CamelIMAPXServer *imapx_server); CamelAuthenticationResult - camel_imapx_server_authenticate (CamelIMAPXServer *is, + camel_imapx_server_authenticate_sync + (CamelIMAPXServer *is, const gchar *mechanism, GCancellable *cancellable, GError **error); -void camel_imapx_server_shutdown (CamelIMAPXServer *is, - const GError *error); -gboolean camel_imapx_server_list (CamelIMAPXServer *is, +gboolean camel_imapx_server_query_auth_types_sync + (CamelIMAPXServer *is, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_server_mailbox_selected + (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox); +gboolean camel_imapx_server_ensure_selected_sync + (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_server_process_command_sync + (CamelIMAPXServer *is, + CamelIMAPXCommand *ic, + const gchar *error_prefix, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_server_list_sync (CamelIMAPXServer *is, const gchar *pattern, CamelStoreGetFolderInfoFlags flags, GCancellable *cancellable, GError **error); -CamelFolderChangeInfo * - camel_imapx_server_refresh_info (CamelIMAPXServer *is, +gboolean camel_imapx_server_refresh_info_sync + (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox, GCancellable *cancellable, GError **error); -gboolean camel_imapx_server_sync_changes (CamelIMAPXServer *is, +gboolean camel_imapx_server_sync_changes_sync + (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox, + gboolean can_influence_flags, GCancellable *cancellable, GError **error); -gboolean camel_imapx_server_expunge (CamelIMAPXServer *is, +gboolean camel_imapx_server_expunge_sync (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox, GCancellable *cancellable, GError **error); -gboolean camel_imapx_server_noop (CamelIMAPXServer *is, +gboolean camel_imapx_server_noop_sync (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox, GCancellable *cancellable, GError **error); -CamelStream * camel_imapx_server_get_message (CamelIMAPXServer *is, +CamelStream * camel_imapx_server_get_message_sync + (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox, CamelFolderSummary *summary, CamelDataCache *message_cache, const gchar *message_uid, GCancellable *cancellable, GError **error); -gboolean camel_imapx_server_copy_message (CamelIMAPXServer *is, +gboolean camel_imapx_server_copy_message_sync + (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox, CamelIMAPXMailbox *destination, GPtrArray *uids, gboolean delete_originals, + gboolean remove_deleted_flags, GCancellable *cancellable, GError **error); -gboolean camel_imapx_server_append_message +gboolean camel_imapx_server_append_message_sync (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox, CamelFolderSummary *summary, @@ -211,70 +232,73 @@ gboolean camel_imapx_server_append_messa gchar **append_uid, GCancellable *cancellable, GError **error); -gboolean camel_imapx_server_sync_message (CamelIMAPXServer *is, +gboolean camel_imapx_server_sync_message_sync + (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox, CamelFolderSummary *summary, CamelDataCache *message_cache, const gchar *message_uid, GCancellable *cancellable, GError **error); -gboolean camel_imapx_server_create_mailbox +gboolean camel_imapx_server_create_mailbox_sync (CamelIMAPXServer *is, const gchar *mailbox_name, GCancellable *cancellable, GError **error); -gboolean camel_imapx_server_delete_mailbox +gboolean camel_imapx_server_delete_mailbox_sync (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox, GCancellable *cancellable, GError **error); -gboolean camel_imapx_server_rename_mailbox +gboolean camel_imapx_server_rename_mailbox_sync (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox, const gchar *new_mailbox_name, GCancellable *cancellable, GError **error); -gboolean camel_imapx_server_subscribe_mailbox +gboolean camel_imapx_server_subscribe_mailbox_sync (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox, GCancellable *cancellable, GError **error); -gboolean camel_imapx_server_unsubscribe_mailbox +gboolean camel_imapx_server_unsubscribe_mailbox_sync (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox, GCancellable *cancellable, GError **error); -gboolean camel_imapx_server_update_quota_info +gboolean camel_imapx_server_update_quota_info_sync (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox, GCancellable *cancellable, GError **error); -GPtrArray * camel_imapx_server_uid_search (CamelIMAPXServer *is, +GPtrArray * camel_imapx_server_uid_search_sync + (CamelIMAPXServer *is, CamelIMAPXMailbox *mailbox, - const gchar *criteria, + const gchar *criteria_prefix, + const gchar *search_key, + const gchar * const *words, GCancellable *cancellable, GError **error); -gboolean camel_imapx_server_folder_name_in_jobs - (CamelIMAPXServer *imapx_server, - const gchar *folder_path); -gboolean camel_imapx_server_has_expensive_command - (CamelIMAPXServer *imapx_server); -gint camel_imapx_server_get_command_count - (CamelIMAPXServer *imapx_server); +gboolean camel_imapx_server_can_use_idle (CamelIMAPXServer *is); +gboolean camel_imapx_server_is_in_idle (CamelIMAPXServer *is); +CamelIMAPXMailbox * + camel_imapx_server_ref_idle_mailbox + (CamelIMAPXServer *is); +gboolean camel_imapx_server_schedule_idle_sync + (CamelIMAPXServer *is, + CamelIMAPXMailbox *mailbox, + GCancellable *cancellable, + GError **error); +gboolean camel_imapx_server_stop_idle_sync + (CamelIMAPXServer *is, + GCancellable *cancellable, + GError **error); + const CamelIMAPXUntaggedRespHandlerDesc * camel_imapx_server_register_untagged_handler (CamelIMAPXServer *is, const gchar *untagged_response, const CamelIMAPXUntaggedRespHandlerDesc *desc); -struct _CamelIMAPXJob * - camel_imapx_server_ref_job (CamelIMAPXServer *imapx_server, - CamelIMAPXMailbox *mailbox, - guint32 job_type, - const gchar *uid); - -/* for debugging purposes only */ -void camel_imapx_server_dump_queue_status - (CamelIMAPXServer *imapx_server); G_END_DECLS #endif /* CAMEL_IMAPX_SERVER_H */ diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.c.imapx-update-to-upstream 2014-12-02 16:09:44.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.c 2016-08-15 13:52:41.974976329 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-settings.c * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ @@ -31,9 +31,9 @@ struct _CamelIMAPXSettingsPrivate { gchar *real_trash_path; gchar *shell_command; - guint batch_fetch_count; guint concurrent_connections; + gboolean use_multi_fetch; gboolean check_all; gboolean check_subscribed; gboolean filter_all; @@ -55,7 +55,7 @@ struct _CamelIMAPXSettingsPrivate { enum { PROP_0, PROP_AUTH_MECHANISM, - PROP_BATCH_FETCH_COUNT, + PROP_USE_MULTI_FETCH, PROP_CHECK_ALL, PROP_CHECK_SUBSCRIBED, PROP_CONCURRENT_CONNECTIONS, @@ -102,10 +102,10 @@ imapx_settings_set_property (GObject *ob g_value_get_string (value)); return; - case PROP_BATCH_FETCH_COUNT: - camel_imapx_settings_set_batch_fetch_count ( + case PROP_USE_MULTI_FETCH: + camel_imapx_settings_set_use_multi_fetch ( CAMEL_IMAPX_SETTINGS (object), - g_value_get_uint (value)); + g_value_get_boolean (value)); return; case PROP_CHECK_ALL: @@ -270,10 +270,10 @@ imapx_settings_get_property (GObject *ob CAMEL_NETWORK_SETTINGS (object))); return; - case PROP_BATCH_FETCH_COUNT: - g_value_set_uint ( + case PROP_USE_MULTI_FETCH: + g_value_set_boolean ( value, - camel_imapx_settings_get_batch_fetch_count ( + camel_imapx_settings_get_use_multi_fetch ( CAMEL_IMAPX_SETTINGS (object))); return; @@ -487,14 +487,12 @@ camel_imapx_settings_class_init (CamelIM g_object_class_install_property ( object_class, - PROP_BATCH_FETCH_COUNT, - g_param_spec_uint ( - "batch-fetch-count", - "Batch Fetch Count", - "Number of envelopes to fetch at once", - 0, - G_MAXUINT, - 500, + PROP_USE_MULTI_FETCH, + g_param_spec_boolean ( + "use-multi-fetch", + "Use Multi Fetch", + "Whether allow downloading of large messages in chunks", + FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); @@ -776,50 +774,46 @@ camel_imapx_settings_init (CamelIMAPXSet } /** - * camel_imapx_settings_get_batch_fetch_count: + * camel_imapx_settings_get_use_multi_fetch: * @settings: a #CamelIMAPXSettings * - * Returns the number of message envelopes to fetch at once. - * - * This is a tunable performance parameter and probably should not be - * exposed in a graphical user interface. + * Returns whether large messages can be downloaded in chunks. + * The default is %TRUE, but some server can be slower when + * the messages are downloaded in parts, rather than in one call. * - * Returns: number of message envelopes to fetch at once + * Returns: whether large messages can be downloaded in chunks * - * Since: 3.2 + * Since: 3.20 **/ guint -camel_imapx_settings_get_batch_fetch_count (CamelIMAPXSettings *settings) +camel_imapx_settings_get_use_multi_fetch (CamelIMAPXSettings *settings) { g_return_val_if_fail (CAMEL_IS_IMAPX_SETTINGS (settings), 0); - return settings->priv->batch_fetch_count; + return settings->priv->use_multi_fetch; } /** - * camel_imapx_settings_set_batch_fetch_count: + * camel_imapx_settings_set_use_multi_fetch: * @settings: a #CamelIMAPXSettings - * @batch_fetch_count: number of message envelopes to fetch at once + * @use_multi_fetch: whether can download large messages in chunks * - * Sets the number of message envelopes to fetch at once. + * Sets whether can download large messages in chunks. * - * This is a tunable performance parameter and probably should not be - * exposed in a graphical user interface. - * - * Since: 3.2 + * Since: 3.20 **/ void -camel_imapx_settings_set_batch_fetch_count (CamelIMAPXSettings *settings, - guint batch_fetch_count) +camel_imapx_settings_set_use_multi_fetch (CamelIMAPXSettings *settings, + guint use_multi_fetch) { g_return_if_fail (CAMEL_IS_IMAPX_SETTINGS (settings)); - if (settings->priv->batch_fetch_count == batch_fetch_count) + if (settings->priv->use_multi_fetch == use_multi_fetch) return; - settings->priv->batch_fetch_count = batch_fetch_count; + settings->priv->use_multi_fetch = use_multi_fetch; - g_object_notify (G_OBJECT (settings), "batch-fetch-count"); + g_object_notify (G_OBJECT (settings), "use-multi-fetch"); } /** @@ -915,7 +909,7 @@ camel_imapx_settings_set_check_subscribe * * Returns: the number of concurrent connections to use * - * Since: 3.14 + * Since: 3.16 **/ guint camel_imapx_settings_get_concurrent_connections (CamelIMAPXSettings *settings) @@ -937,7 +931,7 @@ camel_imapx_settings_get_concurrent_conn * @concurrent_connections value will be clamped to these limits if * necessary. * - * Since: 3.14 + * Since: 3.16 **/ void camel_imapx_settings_set_concurrent_connections (CamelIMAPXSettings *settings, @@ -1563,7 +1557,7 @@ camel_imapx_settings_set_use_namespace ( * * Returns: whether to ignore namespace for other users * - * Since: 3.12.9 + * Since: 3.16 **/ gboolean camel_imapx_settings_get_ignore_other_users_namespace (CamelIMAPXSettings *settings) @@ -1580,7 +1574,7 @@ camel_imapx_settings_get_ignore_other_us * * Sets whether to ignore other users namespace. * - * Since: 3.12.9 + * Since: 3.16 **/ void camel_imapx_settings_set_ignore_other_users_namespace (CamelIMAPXSettings *settings, @@ -1604,7 +1598,7 @@ camel_imapx_settings_set_ignore_other_us * * Returns: whether to ignore namespace for shared folders * - * Since: 3.12.9 + * Since: 3.16 **/ gboolean camel_imapx_settings_get_ignore_shared_folders_namespace (CamelIMAPXSettings *settings) @@ -1621,7 +1615,7 @@ camel_imapx_settings_get_ignore_shared_f * * Sets whether to ignore shared folders namespace. * - * Since: 3.12.9 + * Since: 3.16 **/ void camel_imapx_settings_set_ignore_shared_folders_namespace (CamelIMAPXSettings *settings, diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.h.imapx-update-to-upstream 2014-12-02 16:07:55.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-settings.h 2016-08-15 13:52:41.974976329 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-settings.h * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ @@ -55,11 +55,11 @@ struct _CamelIMAPXSettingsClass { }; GType camel_imapx_settings_get_type (void) G_GNUC_CONST; -guint camel_imapx_settings_get_batch_fetch_count +guint camel_imapx_settings_get_use_multi_fetch (CamelIMAPXSettings *settings); -void camel_imapx_settings_set_batch_fetch_count +void camel_imapx_settings_set_use_multi_fetch (CamelIMAPXSettings *settings, - guint batch_fetch_count); + guint use_multi_fetch); gboolean camel_imapx_settings_get_check_all (CamelIMAPXSettings *settings); void camel_imapx_settings_set_check_all diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.c.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.c 2016-08-15 13:52:41.975976329 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-status-response.c * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ @@ -134,7 +134,7 @@ camel_imapx_status_response_new (CamelIM goto fail; if (tok != '(') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "status: expecting '('"); goto fail; } @@ -199,7 +199,7 @@ camel_imapx_status_response_new (CamelIM default: g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "unknown status attribute"); success = FALSE; break; @@ -218,7 +218,7 @@ camel_imapx_status_response_new (CamelIM if (tok != ')') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "status: expecting ')' or attribute"); goto fail; } diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.h.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-status-response.h 2016-08-15 13:52:41.975976329 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-status-response.h * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.c.imapx-update-to-upstream 2016-08-15 13:52:41.918976332 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.c 2016-08-15 13:52:41.977976329 +0200 @@ -1,21 +1,21 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* camel-imap-store.c : class for a imap store */ -/* - * Authors: Michael Zucchi +/* camel-imap-store.c : class for a imap store * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * - * This library is free software; you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . + * + * Authors: Michael Zucchi */ #ifdef HAVE_CONFIG_H @@ -59,7 +59,7 @@ #define e(...) camel_imapx_debug(extra, __VA_ARGS__) struct _CamelIMAPXStorePrivate { - CamelIMAPXConnManager *con_man; + CamelIMAPXConnManager *conn_man; CamelIMAPXServer *connecting_server; gboolean is_concurrent_connection; @@ -88,7 +88,8 @@ struct _CamelIMAPXStorePrivate { enum { PROP_0, PROP_CONNECTABLE, - PROP_HOST_REACHABLE + PROP_HOST_REACHABLE, + PROP_CONN_MANAGER }; enum { @@ -660,6 +661,13 @@ imapx_store_get_property (GObject *objec camel_network_service_get_host_reachable ( CAMEL_NETWORK_SERVICE (object))); return; + + case PROP_CONN_MANAGER: + g_value_set_object ( + value, + camel_imapx_store_get_conn_manager ( + CAMEL_IMAPX_STORE (object))); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -672,9 +680,9 @@ imapx_store_dispose (GObject *object) /* Force disconnect so we don't have it run later, * after we've cleaned up some stuff. */ - if (imapx_store->priv->con_man != NULL) { + if (imapx_store->priv->conn_man != NULL) { camel_service_disconnect_sync (CAMEL_SERVICE (imapx_store), FALSE, NULL, NULL); - g_clear_object (&imapx_store->priv->con_man); + g_clear_object (&imapx_store->priv->conn_man); } if (imapx_store->priv->settings_notify_handler_id > 0) { @@ -771,17 +779,14 @@ imapx_connect_sync (CamelService *servic GError **error) { CamelIMAPXStore *imapx_store; - CamelIMAPXServer *imapx_server; - gboolean success; - - imapx_store = CAMEL_IMAPX_STORE (service); - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error); - success = imapx_server != NULL; + /* Chain up to parent's method. */ + if (!CAMEL_SERVICE_CLASS (camel_imapx_store_parent_class)->connect_sync (service, cancellable, error)) + return FALSE; - g_clear_object (&imapx_server); + imapx_store = CAMEL_IMAPX_STORE (service); - return success; + return camel_imapx_conn_manager_connect_sync (imapx_store->priv->conn_man, cancellable, error); } static gboolean @@ -794,8 +799,8 @@ imapx_disconnect_sync (CamelService *ser priv = CAMEL_IMAPX_STORE_GET_PRIVATE (service); - if (priv->con_man != NULL) - camel_imapx_conn_manager_close_connections (priv->con_man, NULL); + if (priv->conn_man != NULL) + camel_imapx_conn_manager_disconnect_sync (priv->conn_man, cancellable, error); g_mutex_lock (&priv->server_lock); @@ -803,7 +808,8 @@ imapx_disconnect_sync (CamelService *ser g_mutex_unlock (&priv->server_lock); - return TRUE; + /* Chain up to parent's method. */ + return CAMEL_SERVICE_CLASS (camel_imapx_store_parent_class)->disconnect_sync (service, clean, cancellable, error); } static CamelAuthenticationResult @@ -818,12 +824,24 @@ imapx_authenticate_sync (CamelService *s priv = CAMEL_IMAPX_STORE_GET_PRIVATE (service); + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return CAMEL_AUTHENTICATION_ERROR; + /* This should have been set for us by connect_sync(). */ g_mutex_lock (&priv->server_lock); + if (!priv->connecting_server) { + g_mutex_unlock (&priv->server_lock); + + g_set_error_literal (error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE, + _("No IMAPx connection object provided")); + + return CAMEL_AUTHENTICATION_ERROR; + } + imapx_server = g_object_ref (priv->connecting_server); g_mutex_unlock (&priv->server_lock); - result = camel_imapx_server_authenticate ( + result = camel_imapx_server_authenticate_sync ( imapx_server, mechanism, cancellable, error); g_clear_object (&imapx_server); @@ -851,28 +869,32 @@ imapx_query_auth_types_sync (CamelServic GList *sasl_types = NULL; GList *t, *next; CamelIMAPXServer *server; + const struct _capability_info *cinfo; imapx_store = CAMEL_IMAPX_STORE (service); server = camel_imapx_server_new (imapx_store); - server->tagprefix = 'Z'; + camel_imapx_server_set_tagprefix (server, 'Z'); + + g_signal_emit_by_name (imapx_store->priv->conn_man, "connection-created", 0, server); - if (!imapx_connect_to_server (server, cancellable, error)) + if (!camel_imapx_server_query_auth_types_sync (server, cancellable, error)) goto exit; + cinfo = camel_imapx_server_get_capability_info (server); + sasl_types = camel_sasl_authtype_list (FALSE); for (t = sasl_types; t; t = next) { authtype = t->data; next = t->next; - if (!server->cinfo || !g_hash_table_lookup (server->cinfo->auth_types, authtype->authproto)) { + if (!cinfo || !g_hash_table_lookup (cinfo->auth_types, authtype->authproto)) { sasl_types = g_list_remove_link (sasl_types, t); g_list_free_1 (t); } } - sasl_types = g_list_prepend ( - sasl_types, &camel_imapx_password_authtype); + sasl_types = g_list_prepend (sasl_types, &camel_imapx_password_authtype); exit: g_object_unref (server); @@ -1003,6 +1025,7 @@ static CamelFolderInfo * get_folder_info_offline (CamelStore *store, const gchar *top, CamelStoreGetFolderInfoFlags flags, + GCancellable *cancellable, GError **error) { CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (store); @@ -1013,8 +1036,36 @@ get_folder_info_offline (CamelStore *sto GPtrArray *folders; GPtrArray *array; gboolean use_subscriptions; + gint top_len; guint ii; + if (g_strcmp0 (top, CAMEL_VTRASH_NAME) == 0 || + g_strcmp0 (top, CAMEL_VJUNK_NAME) == 0) { + CamelFolder *vfolder; + + vfolder = camel_store_get_folder_sync (store, top, 0, cancellable, error); + if (!vfolder) + return NULL; + + fi = imapx_store_build_folder_info (imapx_store, top, 0); + fi->unread = camel_folder_summary_get_unread_count (vfolder->summary); + fi->total = camel_folder_summary_get_saved_count (vfolder->summary); + + if (g_strcmp0 (top, CAMEL_VTRASH_NAME) == 0) + fi->flags = (fi->flags & ~CAMEL_FOLDER_TYPE_MASK) | + CAMEL_FOLDER_VIRTUAL | + CAMEL_FOLDER_VTRASH | + CAMEL_FOLDER_TYPE_TRASH; + else + fi->flags = (fi->flags & ~CAMEL_FOLDER_TYPE_MASK) | + CAMEL_FOLDER_VIRTUAL | + CAMEL_FOLDER_TYPE_JUNK; + + g_object_unref (vfolder); + + return fi; + } + service = CAMEL_SERVICE (store); settings = camel_service_ref_settings (service); @@ -1033,6 +1084,8 @@ get_folder_info_offline (CamelStore *sto top = ""; } + top_len = strlen (top); + /* folder_info_build will insert parent nodes as necessary and mark * them as noselect, which is information we actually don't have at * the moment. So let it do the right thing by bailing out if it's @@ -1053,7 +1106,8 @@ get_folder_info_offline (CamelStore *sto /* Filter by folder path. */ si_is_match = (include_inbox && si_is_inbox) || - g_str_has_prefix (folder_path, top); + (g_str_has_prefix (folder_path, top) && (top_len == 0 || + !folder_path[top_len] || folder_path[top_len] == '/')); if (!si_is_match) continue; @@ -1093,8 +1147,7 @@ get_folder_info_offline (CamelStore *sto g_clear_object (&mailbox); } - fi = imapx_store_build_folder_info ( - imapx_store, folder_path, 0); + fi = imapx_store_build_folder_info (imapx_store, folder_path, 0); fi->unread = si->unread; fi->total = si->total; if ((fi->flags & CAMEL_FOLDER_TYPE_MASK) != 0) @@ -1118,6 +1171,19 @@ get_folder_info_offline (CamelStore *sto if (!fi->child) fi->flags |= CAMEL_FOLDER_NOCHILDREN; + if (fi->unread == -1 && fi->total == -1) { + CamelIMAPXMailbox *mailbox; + + mailbox = camel_imapx_store_ref_mailbox (imapx_store, ((CamelIMAPXStoreInfo *) si)->mailbox_name); + + if (mailbox) { + fi->unread = camel_imapx_mailbox_get_unseen (mailbox); + fi->total = camel_imapx_mailbox_get_messages (mailbox); + } + + g_clear_object (&mailbox); + } + g_ptr_array_add (folders, fi); } @@ -1155,7 +1221,7 @@ collect_folder_info_for_list (CamelIMAPX } static gboolean -fetch_folder_info_for_pattern (CamelIMAPXServer *server, +fetch_folder_info_for_pattern (CamelIMAPXConnManager *conn_man, CamelIMAPXNamespace *namespace, const gchar *pattern, CamelStoreGetFolderInfoFlags flags, @@ -1168,22 +1234,9 @@ fetch_folder_info_for_pattern (CamelIMAP GError *local_error = NULL; gboolean success; - g_object_ref (server); - - imapx_store = camel_imapx_server_ref_store (server); - - success = camel_imapx_server_list (server, pattern, flags, cancellable, &local_error); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&server); - - server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error); - if (server) - success = camel_imapx_server_list (server, pattern, flags, cancellable, &local_error); - } + imapx_store = camel_imapx_conn_manager_ref_store (conn_man); - g_clear_object (&server); + success = camel_imapx_conn_manager_list_sync (conn_man, pattern, flags, cancellable, &local_error); if (!success) { g_clear_object (&imapx_store); @@ -1220,34 +1273,18 @@ fetch_folder_info_for_pattern (CamelIMAP } static gboolean -fetch_folder_info_for_inbox (CamelIMAPXServer *server, +fetch_folder_info_for_inbox (CamelIMAPXConnManager *conn_man, CamelStoreGetFolderInfoFlags flags, GHashTable *folder_info_results, GCancellable *cancellable, GError **error) { CamelIMAPXStore *imapx_store; - GError *local_error = NULL; gboolean success; - g_object_ref (server); - imapx_store = camel_imapx_server_ref_store (server); - - success = camel_imapx_server_list (server, "INBOX", flags, cancellable, &local_error); + imapx_store = camel_imapx_conn_manager_ref_store (conn_man); - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&server); - - server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error); - if (server) - success = camel_imapx_server_list (server, "INBOX", flags, cancellable, &local_error); - } - - g_clear_object (&server); - - if (local_error) - g_propagate_error (error, local_error); + success = camel_imapx_conn_manager_list_sync (conn_man, "INBOX", flags, cancellable, error); if (success) { CamelIMAPXMailbox *mailbox; @@ -1266,7 +1303,7 @@ fetch_folder_info_for_inbox (CamelIMAPXS static gboolean fetch_folder_info_for_namespace_category (CamelIMAPXStore *imapx_store, - CamelIMAPXServer *server, + CamelIMAPXConnManager *conn_man, CamelIMAPXNamespaceCategory category, CamelStoreGetFolderInfoFlags flags, GHashTable *folder_info_results, @@ -1298,7 +1335,7 @@ fetch_folder_info_for_namespace_category pattern = g_strdup_printf ("%s*", ns_prefix); success = fetch_folder_info_for_pattern ( - server, namespace, pattern, flags, + conn_man, namespace, pattern, flags, folder_info_results, cancellable, error); g_free (pattern); @@ -1316,7 +1353,7 @@ fetch_folder_info_for_namespace_category static gboolean fetch_folder_info_from_folder_path (CamelIMAPXStore *imapx_store, - CamelIMAPXServer *server, + CamelIMAPXConnManager *conn_man, const gchar *folder_path, CamelStoreGetFolderInfoFlags flags, GHashTable *folder_info_results, @@ -1354,7 +1391,7 @@ fetch_folder_info_from_folder_path (Came pattern = g_strdup_printf ("%s*", utf7_mailbox_name); success = fetch_folder_info_for_pattern ( - server, namespace, pattern, flags, + conn_man, namespace, pattern, flags, folder_info_results, cancellable, error); g_free (pattern); @@ -1490,14 +1527,12 @@ sync_folders (CamelIMAPXStore *imapx_sto GCancellable *cancellable, GError **error) { - CamelIMAPXServer *server; + CamelIMAPXConnManager *conn_man; GHashTable *folder_info_results; gboolean update_folder_list; gboolean success; - server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error); - if (server == NULL) - return FALSE; + conn_man = camel_imapx_store_get_conn_manager (imapx_store); /* mailbox name -> CamelFolderInfo */ folder_info_results = g_hash_table_new_full ( @@ -1520,13 +1555,13 @@ sync_folders (CamelIMAPXStore *imapx_sto if (root_folder_path != NULL && *root_folder_path != '\0') { success = fetch_folder_info_from_folder_path ( - imapx_store, server, root_folder_path, flags, + imapx_store, conn_man, root_folder_path, flags, folder_info_results, cancellable, error); } else { gboolean have_folder_info_for_inbox; success = fetch_folder_info_for_namespace_category ( - imapx_store, server, CAMEL_IMAPX_NAMESPACE_PERSONAL, flags | + imapx_store, conn_man, CAMEL_IMAPX_NAMESPACE_PERSONAL, flags | (update_folder_list ? CAMEL_STORE_FOLDER_INFO_SUBSCRIBED : 0), folder_info_results, cancellable, error); @@ -1538,7 +1573,7 @@ sync_folders (CamelIMAPXStore *imapx_sto * then LIST it explicitly. */ if (success && !have_folder_info_for_inbox) success = fetch_folder_info_for_inbox ( - server, flags, folder_info_results, + conn_man, flags, folder_info_results, cancellable, error); } @@ -1588,8 +1623,6 @@ sync_folders (CamelIMAPXStore *imapx_sto exit: g_hash_table_destroy (folder_info_results); - g_object_unref (server); - return success; } @@ -1606,7 +1639,7 @@ imapx_refresh_finfo (CamelSession *sessi display_name = camel_service_get_display_name (service); camel_operation_push_message ( - cancellable, _("Retrieving folder list for %s"), + cancellable, _("Retrieving folder list for '%s'"), display_name); if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) @@ -1628,42 +1661,30 @@ static void discover_inbox (CamelIMAPXStore *imapx_store, GCancellable *cancellable) { - CamelIMAPXServer *imapx_server; + CamelIMAPXConnManager *conn_man; CamelIMAPXMailbox *mailbox = NULL; const gchar *attribute; - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, NULL); - - if (imapx_server == NULL) - return; - + conn_man = camel_imapx_store_get_conn_manager (imapx_store); mailbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX"); if (mailbox == NULL) - goto exit; + return; attribute = CAMEL_IMAPX_LIST_ATTR_SUBSCRIBED; if (!camel_imapx_mailbox_has_attribute (mailbox, attribute)) { GError *local_error = NULL; gboolean success; - success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error); - if (imapx_server) - success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error); + success = camel_imapx_conn_manager_subscribe_mailbox_sync (conn_man, mailbox, cancellable, &local_error); + if (!success && !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_warning ("%s: Failed to subscribe INBOX: %s", G_STRFUNC, local_error ? local_error->message : "Unknown error"); } g_clear_error (&local_error); } -exit: g_clear_object (&mailbox); - g_clear_object (&imapx_server); } static gboolean @@ -1699,6 +1720,16 @@ imapx_can_refresh_folder (CamelStore *st res = store_class->can_refresh_folder (store, info, &local_error) || check_all || (check_subscribed && subscribed); + if (!res && !local_error) { + CamelFolder *folder; + + folder = camel_store_get_folder_sync (store, info->full_name, 0, NULL, &local_error); + if (folder && CAMEL_IS_IMAPX_FOLDER (folder)) + res = camel_imapx_folder_get_check_folder (CAMEL_IMAPX_FOLDER (folder)); + + g_clear_object (&folder); + } + if (local_error != NULL) g_propagate_error (error, local_error); @@ -1818,7 +1849,7 @@ imapx_store_get_folder_info_sync (CamelS g_mutex_lock (&imapx_store->priv->get_finfo_lock); if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) { - fi = get_folder_info_offline (store, top, flags, error); + fi = get_folder_info_offline (store, top, flags, cancellable, error); goto exit; } @@ -1828,7 +1859,7 @@ imapx_store_get_folder_info_sync (CamelS } /* XXX I don't know why the SUBSCRIBED flag matters here. */ - if (!initial_setup && flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) { + if (!initial_setup && (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) != 0) { time_t time_since_last_refresh; time_since_last_refresh = @@ -1836,24 +1867,29 @@ imapx_store_get_folder_info_sync (CamelS if (time_since_last_refresh > FINFO_REFRESH_INTERVAL) { CamelSession *session; + gchar *description; imapx_store->priv->last_refresh_time = time (NULL); session = camel_service_ref_session (service); + if (session) { + description = g_strdup_printf (_("Retrieving folder list for '%s'"), camel_service_get_display_name (service)); - camel_session_submit_job ( - session, (CamelSessionCallback) - imapx_refresh_finfo, - g_object_ref (store), - (GDestroyNotify) g_object_unref); + camel_session_submit_job ( + session, /*description,*/ (CamelSessionCallback) + imapx_refresh_finfo, + g_object_ref (store), + (GDestroyNotify) g_object_unref); - g_object_unref (session); + g_object_unref (session); + g_free (description); + } } } /* Avoid server interaction if the FAST flag is set. */ if (!initial_setup && flags & CAMEL_STORE_FOLDER_INFO_FAST) { - fi = get_folder_info_offline (store, top, flags, error); + fi = get_folder_info_offline (store, top, flags, cancellable, error); goto exit; } @@ -1866,7 +1902,7 @@ imapx_store_get_folder_info_sync (CamelS if (initial_setup && use_subscriptions) discover_inbox (imapx_store, cancellable); - fi = get_folder_info_offline (store, top, flags, error); + fi = get_folder_info_offline (store, top, flags, cancellable, error); exit: g_mutex_unlock (&imapx_store->priv->get_finfo_lock); @@ -1980,7 +2016,7 @@ imapx_store_create_folder_sync (CamelSto CamelIMAPXNamespaceResponse *namespace_response; CamelIMAPXNamespace *namespace; CamelIMAPXStore *imapx_store; - CamelIMAPXServer *imapx_server; + CamelIMAPXConnManager *conn_man; CamelFolder *folder; CamelIMAPXMailbox *parent_mailbox = NULL; CamelFolderInfo *fi = NULL; @@ -1990,13 +2026,9 @@ imapx_store_create_folder_sync (CamelSto gchar *mailbox_name = NULL; gchar separator; gboolean success; - GError *local_error = NULL; imapx_store = CAMEL_IMAPX_STORE (store); - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error); - - if (imapx_server == NULL) - return NULL; + conn_man = camel_imapx_store_get_conn_manager (imapx_store); if (parent_name == NULL || *parent_name == '\0') goto check_namespace; @@ -2067,19 +2099,7 @@ check_separator: /* This also LISTs the mailbox after creating it, which * triggers the CamelIMAPXStore::mailbox-created signal * and all the local processing that goes along with it. */ - success = camel_imapx_server_create_mailbox (imapx_server, mailbox_name, cancellable, &local_error); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error); - if (imapx_server) - success = camel_imapx_server_create_mailbox (imapx_server, mailbox_name, cancellable, &local_error); - } - - if (local_error) - g_propagate_error (error, local_error); + success = camel_imapx_conn_manager_create_mailbox_sync (conn_man, mailbox_name, cancellable, error); if (success) { fi = imapx_store_build_folder_info ( @@ -2090,8 +2110,6 @@ check_separator: exit: g_free (mailbox_name); - g_clear_object (&imapx_server); - return fi; } @@ -2103,10 +2121,9 @@ imapx_store_delete_folder_sync (CamelSto { CamelFolder *folder; CamelIMAPXStore *imapx_store; - CamelIMAPXServer *imapx_server; + CamelIMAPXConnManager *conn_man; CamelIMAPXMailbox *mailbox = NULL; gboolean success = FALSE; - GError *local_error = NULL; folder = camel_store_get_folder_sync ( store, folder_name, 0, cancellable, error); @@ -2115,29 +2132,14 @@ imapx_store_delete_folder_sync (CamelSto return FALSE; imapx_store = CAMEL_IMAPX_STORE (store); - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error); - - if (imapx_server == NULL) - goto exit; + conn_man = camel_imapx_store_get_conn_manager (imapx_store); mailbox = camel_imapx_folder_list_mailbox ( CAMEL_IMAPX_FOLDER (folder), cancellable, error); if (mailbox == NULL) goto exit; - success = camel_imapx_server_delete_mailbox (imapx_server, mailbox, cancellable, &local_error); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error); - if (imapx_server) - success = camel_imapx_server_delete_mailbox (imapx_server, mailbox, cancellable, &local_error); - } - - if (local_error) - g_propagate_error (error, local_error); + success = camel_imapx_conn_manager_delete_mailbox_sync (conn_man, mailbox, cancellable, error); if (success) imapx_delete_folder_from_cache (imapx_store, folder_name, TRUE); @@ -2145,7 +2147,6 @@ imapx_store_delete_folder_sync (CamelSto exit: g_clear_object (&folder); g_clear_object (&mailbox); - g_clear_object (&imapx_server); return success; } @@ -2161,7 +2162,7 @@ imapx_store_rename_folder_sync (CamelSto CamelService *service; CamelSettings *settings; CamelIMAPXStore *imapx_store; - CamelIMAPXServer *imapx_server; + CamelIMAPXConnManager *conn_man; CamelIMAPXMailbox *mailbox = NULL; CamelIMAPXMailbox *cloned_mailbox; gchar *new_mailbox_name = NULL; @@ -2184,10 +2185,7 @@ imapx_store_rename_folder_sync (CamelSto * in imapx_store_process_mailbox_attributes(). */ g_atomic_int_inc (&imapx_store->priv->syncing_folders); - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error); - - if (imapx_server == NULL) - goto exit; + conn_man = camel_imapx_store_get_conn_manager (imapx_store); folder = camel_store_get_folder_sync ( store, old, 0, cancellable, error); @@ -2208,30 +2206,11 @@ imapx_store_rename_folder_sync (CamelSto new_mailbox_name = camel_imapx_folder_path_to_mailbox (new, separator); if (use_subscriptions) { - success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error); - if (imapx_server) - success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error); - } - + camel_imapx_conn_manager_unsubscribe_mailbox_sync (conn_man, mailbox, cancellable, &local_error); g_clear_error (&local_error); } - success = camel_imapx_server_rename_mailbox (imapx_server, mailbox, new_mailbox_name, cancellable, &local_error); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error); - if (imapx_server) - success = camel_imapx_server_rename_mailbox (imapx_server, mailbox, new_mailbox_name, cancellable, &local_error); - } + success = camel_imapx_conn_manager_rename_mailbox_sync (conn_man, mailbox, new_mailbox_name, cancellable, &local_error); if (!success) { if (local_error) @@ -2241,19 +2220,14 @@ imapx_store_rename_folder_sync (CamelSto if (use_subscriptions) { gboolean success_2; - success_2 = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error); - - while (!success_2 && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error); - if (imapx_server) - success_2 = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error); + success_2 = camel_imapx_conn_manager_subscribe_mailbox_sync (conn_man, mailbox, cancellable, &local_error); + if (!success_2) { + g_warning ("%s: Failed to subscribe '%s': %s", G_STRFUNC, camel_imapx_mailbox_get_name (mailbox), + local_error ? local_error->message : "Unknown error"); } - g_clear_error (&local_error); } + goto exit; } @@ -2264,23 +2238,10 @@ imapx_store_rename_folder_sync (CamelSto /* Create a cloned CamelIMAPXMailbox with the new mailbox name. */ cloned_mailbox = camel_imapx_mailbox_clone (mailbox, new_mailbox_name); - camel_imapx_folder_set_mailbox ( - CAMEL_IMAPX_FOLDER (folder), cloned_mailbox); + camel_imapx_folder_set_mailbox (CAMEL_IMAPX_FOLDER (folder), cloned_mailbox); if (use_subscriptions) { - success = camel_imapx_server_subscribe_mailbox (imapx_server, cloned_mailbox, cancellable, &local_error); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error); - if (imapx_server) - success = camel_imapx_server_subscribe_mailbox (imapx_server, cloned_mailbox, cancellable, &local_error); - } - - if (local_error) - g_propagate_error (error, local_error); + success = camel_imapx_conn_manager_subscribe_mailbox_sync (conn_man, cloned_mailbox, cancellable, error); } g_clear_object (&cloned_mailbox); @@ -2289,15 +2250,322 @@ exit: g_free (new_mailbox_name); g_clear_object (&mailbox); - g_clear_object (&imapx_server); - /* This enabled CamelStore signal emissions + /* This enables CamelStore signal emissions * in imapx_store_process_mailbox_attributes() again. */ g_atomic_int_dec_and_test (&imapx_store->priv->syncing_folders); return success; } +static gboolean +imapx_is_gmail_server (CamelService *service) +{ + CamelSettings *settings; + gboolean is_gmail = FALSE; + + g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE); + + settings = camel_service_ref_settings (service); + if (CAMEL_IS_NETWORK_SETTINGS (settings)) { + gchar *host; + + host = camel_network_settings_dup_host (CAMEL_NETWORK_SETTINGS (settings)); + + is_gmail = host && ( + camel_strstrcase (host, ".gmail.com") != NULL || + camel_strstrcase (host, ".googlemail.com") != NULL); + + g_free (host); + } + + g_clear_object (&settings); + + return is_gmail; +} + +static gchar * +imapx_find_folder_for_initial_setup (CamelFolderInfo *root, + const gchar *path) +{ + CamelFolderInfo *finfo, *next; + gchar *folder_fullname = NULL; + gchar **path_parts; + gint ii; + + if (!root || !path) + return NULL; + + path_parts = g_strsplit (path, "/", -1); + if (!path_parts) + return NULL; + + finfo = root; + + for (ii = 0; path_parts[ii] && finfo; ii++) { + gchar *folded_path; + + folded_path = g_utf8_casefold (path_parts[ii], -1); + if (!folded_path) + break; + + for (next = NULL; finfo; finfo = finfo->next) { + gchar *folded_display_name; + gint cmp; + + if ((finfo->flags & (CAMEL_FOLDER_NOSELECT | CAMEL_FOLDER_VIRTUAL)) != 0) + continue; + + folded_display_name = g_utf8_casefold (finfo->display_name, -1); + if (!folded_display_name) + continue; + + cmp = g_utf8_collate (folded_path, folded_display_name); + + g_free (folded_display_name); + + if (cmp == 0) { + next = finfo; + break; + } + } + + g_free (folded_path); + + finfo = next; + if (finfo) { + if (path_parts[ii + 1]) + finfo = finfo->child; + else + folder_fullname = g_strdup (finfo->full_name); + } + } + + g_strfreev (path_parts); + + return folder_fullname; +} + +static void +imapx_check_initial_setup_group (CamelIMAPXStore *imapx_store, + CamelFolderInfo *finfo, + GHashTable *save_setup, + const gchar *list_attribute, + const gchar *main_key, + const gchar *additional_key, + const gchar *additional_key_value, + const gchar **names, + guint n_names) +{ + gchar *folder_fullname = NULL; + gint ii; + + g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store)); + g_return_if_fail (finfo != NULL); + g_return_if_fail (save_setup != NULL); + g_return_if_fail (main_key != NULL); + g_return_if_fail (names != NULL); + g_return_if_fail (n_names > 0); + + /* Prefer RFC 6154 "SPECIAL-USE" Flags, which are not locale sensitive */ + if (list_attribute) { + CamelIMAPXNamespaceResponse *namespace_response; + + namespace_response = camel_imapx_store_ref_namespaces (imapx_store); + if (namespace_response) { + GList *namespaces, *mailboxes, *link; + CamelIMAPXNamespace *user_namespace = NULL; + + namespaces = camel_imapx_namespace_response_list (namespace_response); + for (link = namespaces; link && !user_namespace; link = g_list_next (link)) { + CamelIMAPXNamespace *candidate = link->data; + + if (!candidate || camel_imapx_namespace_get_category (candidate) != CAMEL_IMAPX_NAMESPACE_PERSONAL) + continue; + + user_namespace = candidate; + } + + if (user_namespace) { + mailboxes = camel_imapx_store_list_mailboxes (imapx_store, user_namespace, NULL); + + for (link = mailboxes; link && !folder_fullname; link = g_list_next (link)) { + CamelIMAPXMailbox *mailbox = link->data; + + if (!mailbox || !camel_imapx_mailbox_has_attribute (mailbox, list_attribute)) + continue; + + folder_fullname = camel_imapx_mailbox_dup_folder_path (mailbox); + } + + g_list_free_full (mailboxes, g_object_unref); + } + + g_list_free_full (namespaces, g_object_unref); + g_object_unref (namespace_response); + } + } + + /* First check the folder names in the user's locale */ + for (ii = 0; ii < n_names && !folder_fullname; ii++) { + gchar *name; + + /* In the same level as the Inbox */ + folder_fullname = imapx_find_folder_for_initial_setup (finfo, g_dpgettext2 (GETTEXT_PACKAGE, "IMAPDefaults", names[ii])); + + if (folder_fullname) + break; + + /* as a subfolder of the Inbox */ + name = g_strconcat ("INBOX/", g_dpgettext2 (GETTEXT_PACKAGE, "IMAPDefaults", names[ii]), NULL); + folder_fullname = imapx_find_folder_for_initial_setup (finfo, name); + g_free (name); + } + + /* Then repeat with the english name (as written in the code) */ + for (ii = 0; ii < n_names && !folder_fullname; ii++) { + gchar *name; + + /* Do not try the same folder name twice */ + if (g_strcmp0 (g_dpgettext2 (GETTEXT_PACKAGE, "IMAPDefaults", names[ii]), names[ii]) == 0) + continue; + + folder_fullname = imapx_find_folder_for_initial_setup (finfo, names[ii]); + + if (folder_fullname) + break; + + name = g_strconcat ("INBOX/", names[ii], NULL); + folder_fullname = imapx_find_folder_for_initial_setup (finfo, name); + g_free (name); + } + + if (folder_fullname) { + g_hash_table_insert (save_setup, + g_strdup (main_key), + g_strdup (folder_fullname)); + + if (additional_key) { + g_hash_table_insert (save_setup, + g_strdup (additional_key), + g_strdup (additional_key_value)); + } + + g_free (folder_fullname); + } +} + +static gboolean +imapx_initial_setup_sync (CamelStore *store, + GHashTable *save_setup, + GCancellable *cancellable, + GError **error) +{ + /* Translators: The strings in "IMAPDefaults" context are folder names as can be presented + by the server; There's checked for the localized version of it and for the non-localized + version as well. It's always the folder name (eventually path) as provided by the server, + when returned in given localization. it can be checked semi-easily in the case of + the GMail variants, by changing the GMail interface language in the GMail Preferences. */ + const gchar *draft_names[] = { + NC_("IMAPDefaults", "[Gmail]/Drafts"), + NC_("IMAPDefaults", "Drafts"), + NC_("IMAPDefaults", "Draft") + }; + const gchar *templates_names[] = { + NC_("IMAPDefaults", "Templates") + }; + #ifdef CAMEL_STORE_SETUP_ARCHIVE_FOLDER + const gchar *archive_names[] = { + NC_("IMAPDefaults", "Archive") + }; + #endif + const gchar *sent_names[] = { + NC_("IMAPDefaults", "[Gmail]/Sent Mail"), + NC_("IMAPDefaults", "Sent"), + NC_("IMAPDefaults", "Sent Items"), + NC_("IMAPDefaults", "Sent Messages") + }; + const gchar *junk_names[] = { + NC_("IMAPDefaults", "[Gmail]/Spam"), + NC_("IMAPDefaults", "Junk"), + NC_("IMAPDefaults", "Junk E-mail"), + NC_("IMAPDefaults", "Junk Email"), + NC_("IMAPDefaults", "Spam"), + NC_("IMAPDefaults", "Bulk Mail") + }; + const gchar *trash_names[] = { + NC_("IMAPDefaults", "[Gmail]/Trash"), + NC_("IMAPDefaults", "Trash"), + NC_("IMAPDefaults", "Deleted Items"), + NC_("IMAPDefaults", "Deleted Messages") + }; + + CamelIMAPXStore *imapx_store; + CamelFolderInfo *finfo; + GError *local_error = NULL; + + g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), FALSE); + g_return_val_if_fail (save_setup != NULL, FALSE); + + finfo = camel_store_get_folder_info_sync (store, NULL, + CAMEL_STORE_FOLDER_INFO_RECURSIVE | CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL, + cancellable, &local_error); + + if (!finfo) { + if (local_error) { + g_propagate_error (error, local_error); + return FALSE; + } + + return TRUE; + } + + imapx_store = CAMEL_IMAPX_STORE (store); + + imapx_check_initial_setup_group (imapx_store, finfo, save_setup, + CAMEL_IMAPX_LIST_ATTR_DRAFTS, + CAMEL_STORE_SETUP_DRAFTS_FOLDER, NULL, NULL, + draft_names, G_N_ELEMENTS (draft_names)); + + imapx_check_initial_setup_group (imapx_store, finfo, save_setup, NULL, + CAMEL_STORE_SETUP_TEMPLATES_FOLDER, NULL, NULL, + templates_names, G_N_ELEMENTS (templates_names)); + + #ifdef CAMEL_STORE_SETUP_ARCHIVE_FOLDER + imapx_check_initial_setup_group (imapx_store, finfo, save_setup, + CAMEL_IMAPX_LIST_ATTR_ARCHIVE, + CAMEL_STORE_SETUP_ARCHIVE_FOLDER, NULL, NULL, + archive_names, G_N_ELEMENTS (archive_names)); + #endif + + /* Skip changing Sent folder for GMail, because GMail stores sent messages + automatically, thus it would make doubled copies on the server. */ + if (!imapx_is_gmail_server (CAMEL_SERVICE (store))) { + imapx_check_initial_setup_group (imapx_store, finfo, save_setup, + CAMEL_IMAPX_LIST_ATTR_SENT, + CAMEL_STORE_SETUP_SENT_FOLDER, NULL, NULL, + sent_names, G_N_ELEMENTS (sent_names)); + } + + /* It's a folder path inside the account, thus not use the 'f' type, but the 's' type. */ + imapx_check_initial_setup_group (imapx_store, finfo, save_setup, + CAMEL_IMAPX_LIST_ATTR_JUNK, + "Backend:Imapx Backend:real-junk-path:s", + "Backend:Imapx Backend:use-real-junk-path:b", "true", + junk_names, G_N_ELEMENTS (junk_names)); + + /* It's a folder path inside the account, thus not use the 'f' type, but the 's' type. */ + imapx_check_initial_setup_group (imapx_store, finfo, save_setup, + CAMEL_IMAPX_LIST_ATTR_TRASH, + "Backend:Imapx Backend:real-trash-path:s", + "Backend:Imapx Backend:use-real-trash-path:b", "true", + trash_names, G_N_ELEMENTS (trash_names)); + + camel_folder_info_free (finfo); + + return TRUE; +} + static void imapx_migrate_to_user_cache_dir (CamelService *service) { @@ -2341,7 +2609,7 @@ imapx_store_initable_init (GInitable *in store = CAMEL_STORE (initable); service = CAMEL_SERVICE (initable); - store->flags |= CAMEL_STORE_USE_CACHE_DIR; + store->flags |= CAMEL_STORE_USE_CACHE_DIR | CAMEL_STORE_SUPPORTS_INITIAL_SETUP; imapx_migrate_to_user_cache_dir (service); /* Chain up to parent interface's init() method. */ @@ -2460,6 +2728,8 @@ imapx_ensure_parents_subscribed (CamelIM /* Since this is a "fake" folder node, it is not selectable. */ fi->flags |= CAMEL_FOLDER_NOSELECT; + fi->unread = -1; + fi->total = -1; parents = g_slist_prepend (parents, fi); } @@ -2470,6 +2740,9 @@ imapx_ensure_parents_subscribed (CamelIM camel_subscribable_folder_subscribed (subscribable, fi); camel_folder_info_free (fi); } + + g_slist_free (parents); + g_free (parent); } static gboolean @@ -2480,16 +2753,12 @@ imapx_store_subscribe_folder_sync (Camel { CamelFolder *folder; CamelIMAPXStore *imapx_store; - CamelIMAPXServer *imapx_server; + CamelIMAPXConnManager *conn_man; CamelIMAPXMailbox *mailbox = NULL; gboolean success = FALSE; - GError *local_error = NULL; imapx_store = CAMEL_IMAPX_STORE (subscribable); - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error); - - if (imapx_server == NULL) - goto exit; + conn_man = camel_imapx_store_get_conn_manager (imapx_store); folder = camel_store_get_folder_sync ( CAMEL_STORE (subscribable), @@ -2504,19 +2773,7 @@ imapx_store_subscribe_folder_sync (Camel if (mailbox == NULL) goto exit; - success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error); - if (imapx_server) - success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error); - } - - if (local_error) - g_propagate_error (error, local_error); + success = camel_imapx_conn_manager_subscribe_mailbox_sync (conn_man, mailbox, cancellable, error); if (success) { CamelFolderInfo *fi; @@ -2532,7 +2789,6 @@ imapx_store_subscribe_folder_sync (Camel exit: g_clear_object (&mailbox); - g_clear_object (&imapx_server); return success; } @@ -2545,16 +2801,12 @@ imapx_store_unsubscribe_folder_sync (Cam { CamelFolder *folder; CamelIMAPXStore *imapx_store; - CamelIMAPXServer *imapx_server; + CamelIMAPXConnManager *conn_man; CamelIMAPXMailbox *mailbox = NULL; gboolean success = FALSE; - GError *local_error = NULL; imapx_store = CAMEL_IMAPX_STORE (subscribable); - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error); - - if (imapx_server == NULL) - goto exit; + conn_man = camel_imapx_store_get_conn_manager (imapx_store); folder = camel_store_get_folder_sync ( CAMEL_STORE (subscribable), @@ -2569,19 +2821,7 @@ imapx_store_unsubscribe_folder_sync (Cam if (mailbox == NULL) goto exit; - success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error); - - while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) { - g_clear_error (&local_error); - g_clear_object (&imapx_server); - - imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error); - if (imapx_server) - success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error); - } - - if (local_error) - g_propagate_error (error, local_error); + success = camel_imapx_conn_manager_unsubscribe_mailbox_sync (conn_man, mailbox, cancellable, error); if (success) { CamelFolderInfo *fi; @@ -2594,7 +2834,6 @@ imapx_store_unsubscribe_folder_sync (Cam exit: g_clear_object (&mailbox); - g_clear_object (&imapx_server); return success; } @@ -2677,11 +2916,23 @@ camel_imapx_store_class_init (CamelIMAPX store_class->create_folder_sync = imapx_store_create_folder_sync; store_class->delete_folder_sync = imapx_store_delete_folder_sync; store_class->rename_folder_sync = imapx_store_rename_folder_sync; + store_class->initial_setup_sync = imapx_initial_setup_sync; class->mailbox_created = imapx_store_mailbox_created; class->mailbox_renamed = imapx_store_mailbox_renamed; class->mailbox_updated = imapx_store_mailbox_updated; + g_object_class_install_property ( + object_class, + PROP_CONN_MANAGER, + g_param_spec_object ( + "conn-manager", + "Connection Manager", + "The Connection Manager being used for remote operations", + CAMEL_TYPE_IMAPX_CONN_MANAGER, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* Inherited from CamelNetworkService. */ g_object_class_override_property ( object_class, @@ -2751,7 +3002,7 @@ camel_imapx_store_init (CamelIMAPXStore { store->priv = CAMEL_IMAPX_STORE_GET_PRIVATE (store); - store->priv->con_man = camel_imapx_conn_manager_new (CAMEL_STORE (store)); + store->priv->conn_man = camel_imapx_conn_manager_new (CAMEL_STORE (store)); g_mutex_init (&store->priv->get_finfo_lock); @@ -2782,65 +3033,12 @@ camel_imapx_store_init (CamelIMAPXStore G_CALLBACK (imapx_store_update_store_flags), NULL); } -/** - * camel_imapx_store_ref_server: - * @store: a #CamelIMAPXStore - * @folder_name: name of a folder, for which it'll be used; can be %NULL - * @cancellable: a #GCancellable to use ofr possible new connection creation, or %NULL - * @error: return location for a #GError, or %NULL - * - * Returns the #CamelIMAPXServer for @store, if available. - * - * As a convenience, if the @store is not currently connected to an IMAP - * server, the function sets @error to %CAMEL_SERVER_ERROR_UNAVAILABLE and - * returns %NULL. If an operation can possibly be executed while offline, - * pass %NULL for @error. - * - * The returned #CamelIMAPXServer is referenced for thread-safety and must - * be unreferenced with g_object_unref() when finished with it. - * - * Returns: a #CamelIMAPXServer, or %NULL - * - * Since: 3.10 - **/ -CamelIMAPXServer * -camel_imapx_store_ref_server (CamelIMAPXStore *store, - const gchar *folder_name, - gboolean for_expensive_job, - GCancellable *cancellable, - GError **error) +CamelIMAPXConnManager * +camel_imapx_store_get_conn_manager (CamelIMAPXStore *store) { - CamelIMAPXServer *server = NULL; - CamelSession *session; - GError *local_error = NULL; - g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), NULL); - session = camel_service_ref_session (CAMEL_SERVICE (store)); - - if (camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)) && - camel_session_get_online (session)) - server = camel_imapx_conn_manager_get_connection ( - store->priv->con_man, folder_name, for_expensive_job, cancellable, &local_error); - - g_clear_object (&session); - - if (!server && (!local_error || local_error->domain == G_RESOLVER_ERROR)) { - if (!local_error) { - g_set_error ( - &local_error, CAMEL_SERVICE_ERROR, - CAMEL_SERVICE_ERROR_UNAVAILABLE, - _("You must be working online to complete this operation")); - } else { - local_error->domain = CAMEL_SERVICE_ERROR; - local_error->code = CAMEL_SERVICE_ERROR_UNAVAILABLE; - } - } - - if (local_error) - g_propagate_error (error, local_error); - - return server; + return store->priv->conn_man; } /* The caller should hold the store->priv->server_lock already, when calling this */ @@ -2881,19 +3079,6 @@ camel_imapx_store_is_connecting_concurre return res; } -void -camel_imapx_store_folder_op_done (CamelIMAPXStore *store, - CamelIMAPXServer *server, - const gchar *folder_name) -{ - g_return_if_fail (CAMEL_IS_IMAPX_STORE (store)); - g_return_if_fail (CAMEL_IS_IMAPX_SERVER (server)); - g_return_if_fail (folder_name != NULL); - - camel_imapx_conn_manager_update_con_info ( - store->priv->con_man, server, folder_name); -} - /** * camel_imapx_store_ref_namespaces: * @imapx_store: a #CamelIMAPXStore @@ -2908,7 +3093,7 @@ camel_imapx_store_folder_op_done (CamelI * * Returns: a #CamelIMAPXNamespaceResponse * - * Since: 3.12.2 + * Since: 3.16 **/ CamelIMAPXNamespaceResponse * camel_imapx_store_ref_namespaces (CamelIMAPXStore *imapx_store) @@ -3221,8 +3406,8 @@ imapx_store_rename_mailbox_unlocked (Cam new_child_name = g_strconcat ( new_mailbox_name, old_child_name + old_mailbox_name_length, NULL); - new_child = camel_imapx_mailbox_clone ( - old_child, new_child_name); + + new_child = camel_imapx_mailbox_clone (old_child, new_child_name); /* Add the new mailbox, remove the old mailbox. * Note we still have a reference on the old mailbox. */ @@ -3254,7 +3439,7 @@ imapx_store_rename_mailbox_unlocked (Cam * * Returns: a #CamelIMAPXMailbox, or %NULL * - * Since: 3.12.2 + * Since: 3.16 **/ CamelIMAPXMailbox * camel_imapx_store_ref_mailbox (CamelIMAPXStore *imapx_store, @@ -3297,7 +3482,7 @@ camel_imapx_store_ref_mailbox (CamelIMAP * * Returns: a list of #CamelIMAPXMailbox instances * - * Since: 3.12.2 + * Since: 3.16 **/ GList * camel_imapx_store_list_mailboxes (CamelIMAPXStore *imapx_store, @@ -3377,7 +3562,7 @@ camel_imapx_store_handle_list_response ( /* Fabricate a CamelIMAPXNamespaceResponse if the server lacks the * NAMESPACE capability and this is the first LIST / LSUB response. */ - if (CAMEL_IMAPX_LACK_CAPABILITY (imapx_server->cinfo, NAMESPACE)) { + if (camel_imapx_server_lack_capability (imapx_server, IMAPX_CAPABILITY_NAMESPACE)) { g_mutex_lock (&imapx_store->priv->namespaces_lock); if (imapx_store->priv->namespaces == NULL) { imapx_store->priv->namespaces = camel_imapx_namespace_response_faux_new (response); @@ -3441,7 +3626,7 @@ camel_imapx_store_handle_lsub_response ( /* Fabricate a CamelIMAPXNamespaceResponse if the server lacks the * NAMESPACE capability and this is the first LIST / LSUB response. */ - if (CAMEL_IMAPX_LACK_CAPABILITY (imapx_server->cinfo, NAMESPACE)) { + if (camel_imapx_server_lack_capability (imapx_server, IMAPX_CAPABILITY_NAMESPACE)) { g_mutex_lock (&imapx_store->priv->namespaces_lock); if (imapx_store->priv->namespaces == NULL) { imapx_store->priv->namespaces = camel_imapx_namespace_response_faux_new (response); @@ -3512,41 +3697,11 @@ camel_imapx_store_set_quota_info (CamelI g_mutex_unlock (&store->priv->quota_info_lock); } -/* Tries to find matching job among all active connections. - See camel_imapx_server_ref_job() for more information on parameters - and return values. -*/ -CamelIMAPXJob * -camel_imapx_store_ref_job (CamelIMAPXStore *imapx_store, - CamelIMAPXMailbox *mailbox, - guint32 job_type, - const gchar *uid) -{ - GList *servers, *siter; - CamelIMAPXJob *job = NULL; - - g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), NULL); - - servers = camel_imapx_conn_manager_get_connections (imapx_store->priv->con_man); - - for (siter = servers; siter; siter = g_list_next (siter)) { - CamelIMAPXServer *imapx_server = siter->data; - - job = camel_imapx_server_ref_job (imapx_server, mailbox, job_type, uid); - if (job) - break; - } - - g_list_free_full (servers, g_object_unref); - - return job; -} - /* for debugging purposes only */ void camel_imapx_store_dump_queue_status (CamelIMAPXStore *imapx_store) { g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store)); - camel_imapx_conn_manager_dump_queue_status (imapx_store->priv->con_man); + camel_imapx_conn_manager_dump_queue_status (imapx_store->priv->conn_man); } diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.h.imapx-update-to-upstream 2014-05-22 08:45:46.000000000 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store.h 2016-08-15 13:52:41.977976329 +0200 @@ -1,22 +1,21 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* camel-imap-store.h : class for an imap store */ - -/* - * Authors: Michael Zucchi +/* camel-imap-store.h : class for an imap store * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * - * This library is free software; you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . + * + * Authors: Michael Zucchi */ #ifndef CAMEL_IMAPX_STORE_H @@ -24,6 +23,7 @@ #include +#include "camel-imapx-conn-manager.h" #include "camel-imapx-server.h" /* Standard GObject macros */ @@ -75,22 +75,15 @@ struct _CamelIMAPXStoreClass { }; GType camel_imapx_store_get_type (void); -CamelIMAPXServer * - camel_imapx_store_ref_server (CamelIMAPXStore *store, - const gchar *folder_name, - gboolean for_expensive_job, - GCancellable *cancellable, - GError **error); +CamelIMAPXConnManager * + camel_imapx_store_get_conn_manager + (CamelIMAPXStore *store); void camel_imapx_store_set_connecting_server (CamelIMAPXStore *store, CamelIMAPXServer *server, gboolean is_concurrent_connection); gboolean camel_imapx_store_is_connecting_concurrent_connection (CamelIMAPXStore *imapx_store); -void camel_imapx_store_folder_op_done - (CamelIMAPXStore *store, - CamelIMAPXServer *server, - const gchar *folder_name); CamelIMAPXNamespaceResponse * camel_imapx_store_ref_namespaces (CamelIMAPXStore *imapx_store); @@ -127,12 +120,6 @@ void camel_imapx_store_set_quota_info (CamelIMAPXStore *store, const gchar *quota_root_name, const CamelFolderQuotaInfo *info); -struct _CamelIMAPXJob * - camel_imapx_store_ref_job (CamelIMAPXStore *imapx_store, - CamelIMAPXMailbox *mailbox, - guint32 job_type, - const gchar *uid); - /* for debugging purposes only */ void camel_imapx_store_dump_queue_status (CamelIMAPXStore *imapx_store); diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.c.imapx-update-to-upstream 2016-08-15 13:52:41.873976333 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.c 2016-08-15 13:52:41.977976329 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-store-summary.c * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ @@ -57,7 +57,7 @@ namespace_load (FILE *in) * delete all this cruft. */ for (j = 0; j < 3; j++) { - gint32 i, n; + gint32 i, n = 0; if (camel_file_util_decode_fixed_int32 (in, &n) == -1) goto exit; diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.h.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-store-summary.h 2016-08-15 13:52:41.977976329 +0200 @@ -1,17 +1,17 @@ /* * camel-imapx-store-summary.h * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . * */ diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.c.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.c 2016-08-15 13:52:41.978976329 +0200 @@ -2,21 +2,20 @@ /* * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * - * Authors: - * Michael Zucchi - * Dan Winship - * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . + * + * Authors: Michael Zucchi + * Dan Winship */ #ifdef HAVE_CONFIG_H @@ -35,6 +34,13 @@ #define CAMEL_IMAPX_SUMMARY_VERSION (4) +enum { + INFO_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + G_DEFINE_TYPE ( CamelIMAPXSummary, camel_imapx_summary, @@ -250,6 +256,15 @@ imapx_summary_message_info_clone (CamelF return copy; } +static void +imapx_summary_emit_info_changed (CamelMessageInfo *info) +{ + g_return_if_fail (info != NULL); + g_return_if_fail (CAMEL_IS_IMAPX_SUMMARY (info->summary)); + + g_signal_emit (info->summary, signals[INFO_CHANGED], 0, info); +} + static gboolean imapx_summary_info_set_user_flag (CamelMessageInfo *info, const gchar *id, @@ -257,18 +272,43 @@ imapx_summary_info_set_user_flag (CamelM { gboolean changed; - /* Chain up to parent's info_set_user_flag() method. */ - changed = CAMEL_FOLDER_SUMMARY_CLASS ( - camel_imapx_summary_parent_class)-> - info_set_user_flag (info, id, state); + /* Chain up to parent's method. */ + changed = CAMEL_FOLDER_SUMMARY_CLASS (camel_imapx_summary_parent_class)->info_set_user_flag (info, id, state); - /* there was a change, so do not forget to store it to server */ - if (changed) { - CamelIMAPXMessageInfo *imapx_info; + if (changed) + imapx_summary_emit_info_changed (info); - imapx_info = (CamelIMAPXMessageInfo *) info; - imapx_info->info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED; - } + return changed; +} + +static gboolean +imapx_summary_info_set_user_tag (CamelMessageInfo *info, + const gchar *name, + const gchar *value) +{ + gboolean changed; + + /* Chain up to parent's method. */ + changed = CAMEL_FOLDER_SUMMARY_CLASS (camel_imapx_summary_parent_class)->info_set_user_tag (info, name, value); + + if (changed) + imapx_summary_emit_info_changed (info); + + return changed; +} + +static gboolean +imapx_summary_info_set_flags (CamelMessageInfo *info, + guint32 flags, + guint32 set) +{ + gboolean changed; + + /* Chain up to parent's method. */ + changed = CAMEL_FOLDER_SUMMARY_CLASS (camel_imapx_summary_parent_class)->info_set_flags (info, flags, set); + + if (changed) + imapx_summary_emit_info_changed (info); return changed; } @@ -290,6 +330,17 @@ camel_imapx_summary_class_init (CamelIMA folder_summary_class->message_info_free = imapx_summary_message_info_free; folder_summary_class->message_info_clone = imapx_summary_message_info_clone; folder_summary_class->info_set_user_flag = imapx_summary_info_set_user_flag; + folder_summary_class->info_set_user_tag = imapx_summary_info_set_user_tag; + folder_summary_class->info_set_flags = imapx_summary_info_set_flags; + + signals[INFO_CHANGED] = g_signal_new ( + "info-changed", + G_OBJECT_CLASS_TYPE (class), + G_SIGNAL_RUN_LAST, + 0 /* G_STRUCT_OFFSET (CamelIMAPXSummaryClass, info_changed) */, + NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_POINTER /* CamelMessageInfo * */); } static void diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.h.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-summary.h 2016-08-15 13:52:41.978976329 +0200 @@ -1,21 +1,20 @@ /* * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * - * Authors: - * Michael Zucchi - * Dan Winship - * - * This library is free software you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . + * + * Authors: Michael Zucchi + * Dan Winship */ #ifndef CAMEL_IMAPX_SUMMARY_H diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.c --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.c.imapx-update-to-upstream 2016-08-15 13:52:41.873976333 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.c 2016-08-15 14:38:22.158860220 +0200 @@ -2,17 +2,18 @@ /* * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * - * This library is free software; you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . + * */ #ifdef HAVE_CONFIG_H @@ -123,7 +124,7 @@ imapx_parse_flags (CamelIMAPXInputStream if (tok != '(') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "expecting flag list"); return FALSE; } @@ -152,12 +153,21 @@ imapx_parse_flags (CamelIMAPXInputStream if (!match_found && user_flagsp != NULL) { const gchar *flag_name; + gchar *utf8; flag_name = rename_label_flag ( (gchar *) token, strlen ((gchar *) token), TRUE); - camel_flag_set (user_flagsp, flag_name, TRUE); + utf8 = camel_utf7_utf8 (flag_name); + if (utf8 && !g_utf8_validate (utf8, -1, NULL)) { + g_free (utf8); + utf8 = NULL; + } + + camel_flag_set (user_flagsp, utf8 ? utf8 : flag_name, TRUE); + + g_free (utf8); } g_free (upper); @@ -239,6 +249,7 @@ imapx_write_flags (GString *string, while (user_flags) { const gchar *flag_name; + gchar *utf7; flag_name = rename_label_flag ( user_flags->name, strlen (user_flags->name), FALSE); @@ -246,7 +257,12 @@ imapx_write_flags (GString *string, if (!first) g_string_append_c (string, ' '); first = FALSE; - g_string_append (string, flag_name); + + utf7 = camel_utf8_utf7 (flag_name); + + g_string_append (string, utf7 ? utf7 : flag_name); + + g_free (utf7); user_flags = user_flags->next; } @@ -261,17 +277,21 @@ imapx_update_user_flags (CamelMessageInf gboolean changed = FALSE; CamelMessageInfoBase *binfo = (CamelMessageInfoBase *) info; CamelIMAPXMessageInfo *xinfo = (CamelIMAPXMessageInfo *) info; - gboolean set_cal = FALSE; + gboolean set_cal = FALSE, set_note = FALSE; if (camel_flag_get (&binfo->user_flags, "$has_cal")) set_cal = TRUE; + if (camel_flag_get (&binfo->user_flags, "$has_note")) + set_note = TRUE; changed = camel_flag_list_copy (&binfo->user_flags, &server_user_flags); camel_flag_list_copy (&xinfo->server_user_flags, &server_user_flags); - /* reset the calendar flag if it was set in messageinfo before */ + /* reset the flags as they were set in messageinfo before */ if (set_cal) camel_flag_set (&binfo->user_flags, "$has_cal", TRUE); + if (set_note) + camel_flag_set (&binfo->user_flags, "$has_note", TRUE); return changed; } @@ -312,16 +332,13 @@ imapx_update_message_info_flags (CamelMe if (permanent_flags > 0) server_cleared &= permanent_flags; - camel_message_info_set_flags (( + changed = camel_message_info_set_flags (( CamelMessageInfo *) xinfo, server_set | server_cleared, (xinfo->info.flags | server_set) & ~server_cleared); xinfo->server_flags = server_flags; - xinfo->info.flags = xinfo->info.flags & ~CAMEL_MESSAGE_FOLDER_FLAGGED; xinfo->info.dirty = TRUE; - - changed = TRUE; } if ((permanent_flags & CAMEL_MESSAGE_USER) != 0 && imapx_update_user_flags (info, server_user_flags)) @@ -503,8 +520,11 @@ imapx_parse_capability (CamelIMAPXInputS /* Put it back so that imapx_untagged() isn't unhappy */ camel_imapx_input_stream_ungettoken ( stream, tok, token, len); - return cinfo; + break; case 43: + /* the CAPABILITY shouldn't start with a '+', ignore it then */ + if (!token) + break; token = (guchar *) g_strconcat ((gchar *) token, "+", NULL); free_token = TRUE; /* coverity[fallthrough] */ @@ -530,11 +550,14 @@ imapx_parse_capability (CamelIMAPXInputS break; default: g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "capability: expecting name"); break; } + if (tok == ']') + break; + tok = camel_imapx_input_stream_token ( stream, &token, &len, cancellable, &local_error); } @@ -842,26 +865,28 @@ imapx_parse_ext_optional (CamelIMAPXInpu dinfo->refcount = 1; /* should be string */ if (!camel_imapx_input_stream_astring (stream, &token, cancellable, &local_error)) { - camel_content_disposition_unref (dinfo); if (!local_error) g_set_error ( &local_error, - CAMEL_IMAPX_ERROR, 1, + CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "expecting string"); - g_propagate_error (error, local_error); - - return NULL; + goto done; } dinfo->disposition = g_strdup ((gchar *) token); imapx_parse_param_list ( - stream, &dinfo->params, cancellable, NULL); + stream, &dinfo->params, cancellable, + &local_error); + + if (local_error != NULL) + goto done; + break; case IMAPX_TOK_TOKEN: break; default: g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "body_fld_disp: expecting nil or list"); return NULL; } @@ -886,7 +911,7 @@ imapx_parse_ext_optional (CamelIMAPXInpu g_clear_error (&local_error); g_set_error ( &local_error, - CAMEL_IMAPX_ERROR, 1, + CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "expecting string"); break; } @@ -908,6 +933,7 @@ imapx_parse_ext_optional (CamelIMAPXInpu } + done: if (local_error != NULL) { g_propagate_error (error, local_error); if (dinfo) @@ -1003,10 +1029,10 @@ imapx_parse_body_fields (CamelIMAPXInput error: imapx_free_body (cinfo); - return cinfo; + return NULL; } -struct _camel_header_address * +CamelHeaderAddress * imapx_parse_address_list (CamelIMAPXInputStream *stream, GCancellable *cancellable, GError **error) @@ -1015,7 +1041,7 @@ imapx_parse_address_list (CamelIMAPXInpu guint len; guchar *token, *host; gchar *mbox; - struct _camel_header_address *list = NULL; + CamelHeaderAddress *list = NULL; GError *local_error = NULL; /* "(" 1*address ")" / nil */ @@ -1028,7 +1054,7 @@ imapx_parse_address_list (CamelIMAPXInpu } if (tok == '(') { - struct _camel_header_address *addr, *group = NULL; + CamelHeaderAddress *addr, *group = NULL; while (1) { /* address ::= "(" addr_name SPACE addr_adl SPACE addr_mailbox * SPACE addr_host ")" */ @@ -1040,7 +1066,7 @@ imapx_parse_address_list (CamelIMAPXInpu g_clear_error (&local_error); camel_header_address_list_clear (&list); g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "missing '(' for address"); return NULL; } @@ -1134,7 +1160,7 @@ imapx_parse_envelope (CamelIMAPXInputStr gint tok; guint len; guchar *token; - struct _camel_header_address *addr, *addr_from; + CamelHeaderAddress *addr, *addr_from; gchar *addrstr; struct _CamelMessageInfoBase *minfo = NULL; GError *local_error = NULL; @@ -1155,7 +1181,7 @@ imapx_parse_envelope (CamelIMAPXInputStr if (tok != '(') { g_clear_error (&local_error); camel_message_info_unref (minfo); - g_set_error (error, CAMEL_IMAPX_ERROR, 1, "envelope: expecting '('"); + g_set_error (error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "envelope: expecting '('"); return NULL; } @@ -1262,7 +1288,7 @@ imapx_parse_envelope (CamelIMAPXInputStr if (tok != ')') { g_clear_error (&local_error); camel_message_info_unref (minfo); - g_set_error (error, CAMEL_IMAPX_ERROR, 1, "expecting ')'"); + g_set_error (error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "expecting ')'"); return NULL; } @@ -1297,7 +1323,7 @@ imapx_parse_body (CamelIMAPXInputStream stream, &token, &len, cancellable, &local_error); if (tok != '(') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "body: expecting '('"); return NULL; } @@ -1533,7 +1559,7 @@ imapx_parse_section (CamelIMAPXInputStre if (tok != '[') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "section: expecting '['"); return NULL; } @@ -1548,7 +1574,7 @@ imapx_parse_section (CamelIMAPXInputStre camel_imapx_input_stream_ungettoken (stream, tok, token, len); } else { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "section: expecting token"); return NULL; } @@ -1569,7 +1595,7 @@ imapx_parse_section (CamelIMAPXInputStre /* ?do something? */ } else if (tok != ')') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "section: header fields: expecting string"); g_free (section); return NULL; @@ -1582,7 +1608,7 @@ imapx_parse_section (CamelIMAPXInputStre if (tok != ']') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "section: expecting ']'"); g_free (section); return NULL; @@ -1610,7 +1636,7 @@ imapx_parse_modseq (CamelIMAPXInputStrea if (tok != '(') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "fetch: expecting '('"); return 0; } @@ -1629,7 +1655,7 @@ imapx_parse_modseq (CamelIMAPXInputStrea if (tok != ')') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "fetch: expecting '('"); return 0; } @@ -1680,19 +1706,19 @@ imapx_dump_fetch (struct _fetch_info *fi if (finfo->body != NULL) { g_print ("Body content:\n"); data = g_bytes_get_data (finfo->body, &size); - fwrite (data, size, 1, stdout); + fwrite (data, sizeof (gchar), size, stdout); } if (finfo->text != NULL) { g_print ("Text content:\n"); data = g_bytes_get_data (finfo->text, &size); - fwrite (data, size, 1, stdout); + fwrite (data, sizeof (gchar), size, stdout); } if (finfo->header != NULL) { g_print ("Header content:\n"); data = g_bytes_get_data (finfo->header, &size); - fwrite (data, size, 1, stdout); + fwrite (data, sizeof (gchar), size, stdout); } if (finfo->minfo != NULL) { @@ -1786,7 +1812,7 @@ imapx_parse_fetch_body (CamelIMAPXInputS } g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "unknown body response"); return FALSE; @@ -1957,7 +1983,7 @@ imapx_parse_fetch_uid (CamelIMAPXInputSt if (tok != IMAPX_TOK_INT) { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "uid not integer"); return FALSE; } @@ -1988,7 +2014,7 @@ imapx_parse_fetch (CamelIMAPXInputStream if (tok != '(') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "fetch: expecting '('"); goto fail; } @@ -2056,7 +2082,7 @@ imapx_parse_fetch (CamelIMAPXInputStream default: g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "unknown body response"); break; } @@ -2073,7 +2099,7 @@ imapx_parse_fetch (CamelIMAPXInputStream if (tok != ')') { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "missing closing ')' on fetch response"); goto fail; } @@ -2094,7 +2120,7 @@ imapx_parse_uids (CamelIMAPXInputStream GError **error) { GArray *array; - guchar *token; + guchar *token = NULL; gchar **splits; guint len, str_len; gint tok, ii; @@ -2106,6 +2132,11 @@ imapx_parse_uids (CamelIMAPXInputStream if (tok < 0) return NULL; + if (!token) { + g_set_error (error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_IGNORE, "server response truncated"); + return NULL; + } + array = g_array_new (FALSE, FALSE, sizeof (guint32)); splits = g_strsplit ((gchar *) token, ",", -1); str_len = g_strv_length (splits); @@ -2181,6 +2212,7 @@ imapx_parse_status_copyuid (CamelIMAPXIn GArray *uids; guint64 number; gboolean success; + GError *local_error = NULL; success = camel_imapx_input_stream_number ( stream, &number, cancellable, error); @@ -2190,15 +2222,37 @@ imapx_parse_status_copyuid (CamelIMAPXIn sinfo->u.copyuid.uidvalidity = number; - uids = imapx_parse_uids (stream, cancellable, error); - if (uids == NULL) + uids = imapx_parse_uids (stream, cancellable, &local_error); + if (uids == NULL) { + /* Some broken servers can return truncated response, like: + B00083 OK [COPYUID 4154 ] COPY completed. + Just ignore such server error. + */ + if (g_error_matches (local_error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_IGNORE)) { + g_clear_error (&local_error); + return TRUE; + } + + if (local_error) + g_propagate_error (error, local_error); + return FALSE; + } sinfo->u.copyuid.uids = uids; uids = imapx_parse_uids (stream, cancellable, error); - if (uids == NULL) + if (uids == NULL) { + if (g_error_matches (local_error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_IGNORE)) { + g_clear_error (&local_error); + return TRUE; + } + + if (local_error) + g_propagate_error (error, local_error); + return FALSE; + } sinfo->u.copyuid.copied_uids = uids; @@ -2376,7 +2430,7 @@ imapx_parse_status (CamelIMAPXInputStrea break; default: g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "expecting OK/NO/BAD"); goto fail; } @@ -2486,7 +2540,7 @@ imapx_parse_status (CamelIMAPXInputStrea stream, &token, &len, cancellable, NULL); if (tok == '\n' || tok < 0) { g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "server response truncated"); goto fail; } @@ -2502,6 +2556,10 @@ imapx_parse_status (CamelIMAPXInputStrea if (!success) goto fail; + if (sinfo->text) { + g_strstrip (sinfo->text); + } + goto exit; fail: @@ -2915,7 +2973,7 @@ camel_imapx_parse_quota (CamelIMAPXInput break; default: g_set_error ( - error, CAMEL_IMAPX_ERROR, 1, + error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "quota_response: expecting '('"); goto fail; } @@ -3228,3 +3286,18 @@ imapx_get_temp_uid (void) return res; } + +gboolean +imapx_util_all_is_ascii (const gchar *str) +{ + gint ii; + gboolean all_ascii = TRUE; + + g_return_val_if_fail (str != NULL, FALSE); + + for (ii = 0; str[ii] && all_ascii; ii++) { + all_ascii = str[ii] > 0; + } + + return all_ascii; +} diff -up evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.h.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.h --- evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.h.imapx-update-to-upstream 2014-10-31 15:25:33.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/camel-imapx-utils.h 2016-08-15 13:52:41.979976329 +0200 @@ -2,17 +2,18 @@ /* * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * - * This library is free software; you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . + * */ #ifndef CAMEL_IMAPX_UTILS_H @@ -25,6 +26,8 @@ G_BEGIN_DECLS +#define CamelHeaderAddress struct _camel_header_address + /* FIXME Split off a camel-imapx-types.h file with supplemental * enum/struct definitions and helper macros, so we don't * have these conflicting header dependencies. */ @@ -213,7 +216,7 @@ struct _CamelMessageContentInfo * imapx_parse_body_fields (CamelIMAPXInputStream *stream, GCancellable *cancellable, GError **error); -struct _camel_header_address * +CamelHeaderAddress * imapx_parse_address_list (CamelIMAPXInputStream *stream, GCancellable *cancellable, GError **error); @@ -392,6 +395,8 @@ gchar * imapx_path_to_physical (const const gchar *vpath); gchar * imapx_get_temp_uid (void); +gboolean imapx_util_all_is_ascii (const gchar *str); + G_END_DECLS #endif /* CAMEL_IMAPX_UTILS_H */ diff -up evolution-data-server-3.12.11/camel/providers/imapx/Makefile.am.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/Makefile.am --- evolution-data-server-3.12.11/camel/providers/imapx/Makefile.am.imapx-update-to-upstream 2014-04-23 14:37:03.000000000 +0200 +++ evolution-data-server-3.12.11/camel/providers/imapx/Makefile.am 2016-08-15 13:52:41.982976329 +0200 @@ -14,6 +14,7 @@ libcamelimapx_la_CPPFLAGS = \ -I$(top_builddir)/camel \ $(CAMEL_CFLAGS) \ $(GIO_UNIX_CFLAGS) \ + $(EVOLUTION_CALENDAR_CFLAGS) \ -DG_LOG_DOMAIN=\"camel-imapx\" \ $(CODE_COVERAGE_CFLAGS) \ $(NULL) @@ -63,6 +64,7 @@ libcamelimapx_la_LIBADD = \ $(top_builddir)/camel/libcamel-1.2.la \ $(CAMEL_LIBS) \ $(GIO_UNIX_LIBS) \ + $(EVOLUTION_CALENDAR_LIBS) \ $(NULL) libcamelimapx_la_LDFLAGS = -avoid-version -module $(NO_UNDEFINED) \ diff -up evolution-data-server-3.12.11/camel/providers/imapx/test-imapx.c.imapx-update-to-upstream evolution-data-server-3.12.11/camel/providers/imapx/test-imapx.c --- evolution-data-server-3.12.11/camel/providers/imapx/test-imapx.c.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/camel/providers/imapx/test-imapx.c 2016-08-15 13:52:41.983976329 +0200 @@ -2,17 +2,18 @@ /* * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * - * This library is free software; you can redistribute it and/or modify it + * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see . + * along with this library. If not, see . + * */ #include diff -up evolution-data-server-3.12.11/libedataserver/e-source-mail-account.c.imapx-update-to-upstream evolution-data-server-3.12.11/libedataserver/e-source-mail-account.c --- evolution-data-server-3.12.11/libedataserver/e-source-mail-account.c.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/libedataserver/e-source-mail-account.c 2016-08-15 13:52:41.983976329 +0200 @@ -47,11 +47,13 @@ struct _ESourceMailAccountPrivate { GMutex property_lock; gchar *identity_uid; + gboolean needs_initial_setup; }; enum { PROP_0, - PROP_IDENTITY_UID + PROP_IDENTITY_UID, + PROP_NEEDS_INITIAL_SETUP }; G_DEFINE_TYPE ( @@ -71,6 +73,12 @@ source_mail_account_set_property (GObjec E_SOURCE_MAIL_ACCOUNT (object), g_value_get_string (value)); return; + + case PROP_NEEDS_INITIAL_SETUP: + e_source_mail_account_set_needs_initial_setup ( + E_SOURCE_MAIL_ACCOUNT (object), + g_value_get_boolean (value)); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -89,6 +97,13 @@ source_mail_account_get_property (GObjec e_source_mail_account_dup_identity_uid ( E_SOURCE_MAIL_ACCOUNT (object))); return; + + case PROP_NEEDS_INITIAL_SETUP: + g_value_set_boolean ( + value, + e_source_mail_account_get_needs_initial_setup ( + E_SOURCE_MAIL_ACCOUNT (object))); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -137,6 +152,19 @@ e_source_mail_account_class_init (ESourc G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_NEEDS_INITIAL_SETUP, + g_param_spec_boolean ( + "needs-initial-setup", + "Needs Initial Setup", + "Whether the account needs to do an initial setup", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); } static void @@ -227,3 +255,43 @@ e_source_mail_account_set_identity_uid ( g_object_notify (G_OBJECT (extension), "identity-uid"); } +/** + * e_source_mail_account_get_needs_initial_setup: + * @extension: an #ESourceMailAccount + * + * Check whether the mail account needs to do its initial setup. + * + * Returns: %TRUE, when the account needs to run its initial setup + * + * Since: 3.12.11-25 (3.20) + **/ +gboolean +e_source_mail_account_get_needs_initial_setup (ESourceMailAccount *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_ACCOUNT (extension), FALSE); + + return extension->priv->needs_initial_setup; +} + +/** + * e_source_mail_account_set_needs_initial_setup: + * @extension: an #ESourceMailAccount + * @needs_initial_setup: value to set + * + * Sets whether the account needs to run its initial setup. + * + * Since: 3.12.11-25 (3.20) + **/ +void +e_source_mail_account_set_needs_initial_setup (ESourceMailAccount *extension, + gboolean needs_initial_setup) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_ACCOUNT (extension)); + + if ((extension->priv->needs_initial_setup ? 1 : 0) == (needs_initial_setup ? 1 : 0)) + return; + + extension->priv->needs_initial_setup = needs_initial_setup; + + g_object_notify (G_OBJECT (extension), "needs-initial-setup"); +} diff -up evolution-data-server-3.12.11/libedataserver/e-source-mail-account.h.imapx-update-to-upstream evolution-data-server-3.12.11/libedataserver/e-source-mail-account.h --- evolution-data-server-3.12.11/libedataserver/e-source-mail-account.h.imapx-update-to-upstream 2014-03-24 10:07:52.000000000 +0100 +++ evolution-data-server-3.12.11/libedataserver/e-source-mail-account.h 2016-08-15 13:52:41.983976329 +0200 @@ -85,6 +85,11 @@ gchar * e_source_mail_account_dup_ident void e_source_mail_account_set_identity_uid (ESourceMailAccount *extension, const gchar *identity_uid); +gboolean e_source_mail_account_get_needs_initial_setup + (ESourceMailAccount *extension); +void e_source_mail_account_set_needs_initial_setup + (ESourceMailAccount *extension, + gboolean needs_initial_setup); G_END_DECLS diff -up evolution-data-server-3.12.11/po/POTFILES.in.imapx-update-to-upstream evolution-data-server-3.12.11/po/POTFILES.in --- evolution-data-server-3.12.11/po/POTFILES.in.imapx-update-to-upstream 2014-11-04 13:57:25.000000000 +0100 +++ evolution-data-server-3.12.11/po/POTFILES.in 2016-08-15 13:52:41.984976329 +0200 @@ -134,6 +134,7 @@ camel/camel-vee-store.c camel/camel-vee-summary.c camel/camel-vtrash-folder.c camel/providers/imapx/camel-imapx-command.c +camel/providers/imapx/camel-imapx-conn-manager.c camel/providers/imapx/camel-imapx-folder.c camel/providers/imapx/camel-imapx-input-stream.c camel/providers/imapx/camel-imapx-provider.c