summaryrefslogtreecommitdiff
path: root/src/camel/providers/imapx/camel-imapx-job.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/camel/providers/imapx/camel-imapx-job.c')
-rw-r--r--src/camel/providers/imapx/camel-imapx-job.c544
1 files changed, 544 insertions, 0 deletions
diff --git a/src/camel/providers/imapx/camel-imapx-job.c b/src/camel/providers/imapx/camel-imapx-job.c
new file mode 100644
index 000000000..ed9c45ade
--- /dev/null
+++ b/src/camel/providers/imapx/camel-imapx-job.c
@@ -0,0 +1,544 @@
+/*
+ * camel-imapx-job.c
+ *
+ * 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evolution-data-server-config.h"
+
+#include <string.h>
+
+#include "camel-imapx-job.h"
+
+G_LOCK_DEFINE_STATIC (get_kind_name_funcs);
+static GSList *get_kind_name_funcs = NULL;
+
+const gchar *
+camel_imapx_job_get_kind_name (guint32 job_kind)
+{
+ GSList *link;
+
+ 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;
+ }
+
+ G_LOCK (get_kind_name_funcs);
+
+ for (link = get_kind_name_funcs; link; link = g_slist_next (link)) {
+ CamelIMAPXJobGetKindNameFunc get_kind_name = link->data;
+
+ if (get_kind_name) {
+ const gchar *name = get_kind_name (job_kind);
+
+ if (name) {
+ G_UNLOCK (get_kind_name_funcs);
+ return name;
+ }
+ }
+ }
+
+ G_UNLOCK (get_kind_name_funcs);
+
+ if (job_kind == CAMEL_IMAPX_JOB_LAST)
+ return "LAST";
+
+ return "???";
+}
+
+void
+camel_imapx_job_register_get_kind_name_func (CamelIMAPXJobGetKindNameFunc get_kind_name)
+{
+ g_return_if_fail (get_kind_name != NULL);
+
+ G_LOCK (get_kind_name_funcs);
+
+ 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);
+
+ G_UNLOCK (get_kind_name_funcs);
+}
+
+void
+camel_imapx_job_unregister_get_kind_name_func (CamelIMAPXJobGetKindNameFunc get_kind_name)
+{
+ g_return_if_fail (get_kind_name != NULL);
+
+ G_LOCK (get_kind_name_funcs);
+
+ 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);
+
+ G_UNLOCK (get_kind_name_funcs);
+}
+
+struct _CamelIMAPXJob {
+ volatile gint ref_count;
+
+ guint32 job_kind;
+ CamelIMAPXMailbox *mailbox;
+
+ CamelIMAPXJobRunSyncFunc run_sync;
+ CamelIMAPXJobMatchesFunc matches;
+ CamelIMAPXJobCopyResultFunc copy_result;
+
+ /* Extra job-specific data. */
+ gpointer user_data;
+ GDestroyNotify destroy_user_data;
+
+ gboolean result_is_set;
+ gboolean result_success;
+ gpointer result_data;
+ GError *result_error;
+ GDestroyNotify destroy_result_data;
+
+ GCond done_cond;
+ GMutex done_mutex;
+ gboolean is_done;
+
+ GCancellable *abort_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_cond_init (&job->done_cond);
+ g_mutex_init (&job->done_mutex);
+
+ return job;
+}
+
+CamelIMAPXJob *
+camel_imapx_job_ref (CamelIMAPXJob *job)
+{
+ g_return_val_if_fail (job != NULL, NULL);
+
+ g_atomic_int_inc (&job->ref_count);
+
+ return job;
+}
+
+void
+camel_imapx_job_unref (CamelIMAPXJob *job)
+{
+ g_return_if_fail (job != NULL);
+
+ if (g_atomic_int_dec_and_test (&job->ref_count)) {
+ if (job->destroy_user_data)
+ job->destroy_user_data (job->user_data);
+
+ if (job->result_is_set && job->destroy_result_data)
+ job->destroy_result_data (job->result_data);
+
+ 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);
+
+ job->ref_count = 0xdeadbeef;
+
+ g_free (job);
+ }
+}
+
+guint32
+camel_imapx_job_get_kind (CamelIMAPXJob *job)
+{
+ g_return_val_if_fail (job != NULL, CAMEL_IMAPX_JOB_UNKNOWN);
+
+ return job->job_kind;
+}
+
+CamelIMAPXMailbox *
+camel_imapx_job_get_mailbox (CamelIMAPXJob *job)
+{
+ g_return_val_if_fail (job != NULL, NULL);
+
+ return job->mailbox;
+}
+
+gpointer
+camel_imapx_job_get_user_data (CamelIMAPXJob *job)
+{
+ g_return_val_if_fail (job != NULL, NULL);
+
+ return job->user_data;
+}
+
+void
+camel_imapx_job_set_user_data (CamelIMAPXJob *job,
+ gpointer user_data,
+ GDestroyNotify destroy_user_data)
+{
+ g_return_if_fail (job != NULL);
+
+ if (job->destroy_user_data)
+ job->destroy_user_data (job->user_data);
+
+ job->user_data = user_data;
+ job->destroy_user_data = destroy_user_data;
+}
+
+gboolean
+camel_imapx_job_was_cancelled (CamelIMAPXJob *job)
+{
+ g_return_val_if_fail (job != NULL, FALSE);
+
+ if (!job->result_is_set)
+ return FALSE;
+
+ return g_error_matches (job->result_error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+}
+
+void
+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);
+ }
+
+ 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_copy_result (CamelIMAPXJob *job,
+ gboolean *out_success,
+ gpointer *out_result,
+ GError **out_error,
+ GDestroyNotify *out_destroy_result)
+{
+ g_return_val_if_fail (job != NULL, FALSE);
+
+ if (!job->result_is_set)
+ return FALSE;
+
+ if (out_success)
+ *out_success = job->result_success;
+
+ if (out_result) {
+ *out_result = NULL;
+
+ if (job->copy_result) {
+ job->copy_result (job, job->result_data, out_result);
+ } else if (job->result_data) {
+ g_warn_if_reached ();
+ }
+ }
+
+ if (out_error) {
+ g_warn_if_fail (*out_error == NULL);
+
+ if (job->result_error)
+ *out_error = g_error_copy (job->result_error);
+ }
+
+ if (out_destroy_result)
+ *out_destroy_result = job->destroy_result_data;
+
+ return TRUE;
+}
+
+/* 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)
+{
+ g_return_val_if_fail (job != NULL, FALSE);
+
+ 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);
+ }
+
+ job->result_data = NULL;
+ g_clear_error (&job->result_error);
+
+ job->result_is_set = FALSE;
+
+ return TRUE;
+}
+
+gboolean
+camel_imapx_job_matches (CamelIMAPXJob *job,
+ CamelIMAPXJob *other_job)
+{
+ g_return_val_if_fail (job != NULL, FALSE);
+ g_return_val_if_fail (other_job != NULL, FALSE);
+
+ if (job->job_kind != other_job->job_kind)
+ return FALSE;
+
+ if (job->mailbox != other_job->mailbox)
+ return FALSE;
+
+ if (job->matches)
+ return job->matches (job, other_job);
+
+ return TRUE;
+}
+
+static void
+imapx_job_cancelled_cb (GCancellable *cancellable,
+ CamelIMAPXJob *job)
+{
+ camel_imapx_job_abort (job);
+}
+
+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));
+
+ camel_operation_push_message (job_cancellable, "%s", message);
+}
+
+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));
+
+ camel_operation_pop_message (job_cancellable);
+}
+
+static void
+imapx_job_progress_cb (CamelOperation *operation,
+ gint percent,
+ GCancellable *job_cancellable)
+{
+ g_return_if_fail (CAMEL_IS_OPERATION (operation));
+ g_return_if_fail (CAMEL_IS_OPERATION (job_cancellable));
+
+ camel_operation_progress (job_cancellable, percent);
+}
+
+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);
+ }
+
+ success = job->run_sync (job, server, job->abort_cancellable, &local_error);
+
+ 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);
+ }
+
+ if (local_error)
+ g_propagate_error (error, local_error);
+
+ return success;
+}
+
+void
+camel_imapx_job_done (CamelIMAPXJob *job)
+{
+ g_return_if_fail (job != NULL);
+
+ g_mutex_lock (&job->done_mutex);
+ job->is_done = TRUE;
+ g_cond_broadcast (&job->done_cond);
+ g_mutex_unlock (&job->done_mutex);
+}
+
+void
+camel_imapx_job_abort (CamelIMAPXJob *job)
+{
+ g_return_if_fail (job != NULL);
+
+ g_cancellable_cancel (job->abort_cancellable);
+}
+
+static void
+camel_imapx_job_wait_cancelled_cb (GCancellable *cancellable,
+ gpointer user_data)
+{
+ CamelIMAPXJob *job = user_data;
+
+ g_return_if_fail (job != NULL);
+
+ g_mutex_lock (&job->done_mutex);
+ g_cond_broadcast (&job->done_cond);
+ g_mutex_unlock (&job->done_mutex);
+}
+
+void
+camel_imapx_job_wait_sync (CamelIMAPXJob *job,
+ GCancellable *cancellable)
+{
+ gulong handler_id = 0;
+
+ g_return_if_fail (job != NULL);
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ if (cancellable)
+ handler_id = g_cancellable_connect (cancellable, G_CALLBACK (camel_imapx_job_wait_cancelled_cb), job, NULL);
+
+ 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);
+
+ if (handler_id)
+ g_cancellable_disconnect (cancellable, handler_id);
+}