[evolution-data-server] Modernize ECalBackend's public API.



commit 91749f78251580a5a6d72a9d77a706e37a1512b2
Author: Matthew Barnes <mbarnes redhat com>
Date:   Tue Mar 26 11:57:47 2013 -0400

    Modernize ECalBackend's public API.
    
    This leaves ECalBackend and EDataCal engaging in a somewhat ridiculous
    kind of ping-pong game for the moment, but the desired public API (in as
    much as a backend has a public API) is starting to take shape.
    
    With the scaffolding now in place, we can begin converting backends to
    use modern GIO conventions one method at a time, and eventually remove
    all the "respond" functions in EDataCal.

 calendar/libedata-cal/e-cal-backend.c              | 3411 +++++++++++++++++---
 calendar/libedata-cal/e-cal-backend.h              |  272 ++-
 calendar/libedata-cal/e-data-cal.c                 | 2140 +++++++------
 calendar/libedata-cal/e-data-cal.h                 |   28 +-
 configure.ac                                       |    2 +-
 .../libedata-cal/libedata-cal-sections.txt         |   32 +-
 6 files changed, 4405 insertions(+), 1480 deletions(-)
---
diff --git a/calendar/libedata-cal/e-cal-backend.c b/calendar/libedata-cal/e-cal-backend.c
index 5e31a6b..45b8f81 100644
--- a/calendar/libedata-cal/e-cal-backend.c
+++ b/calendar/libedata-cal/e-cal-backend.c
@@ -1,24 +1,19 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* Evolution calendar - generic backend class
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- * Authors: Federico Mena-Quintero <federico ximian com>
- *          JP Rosevear <jpr ximian com>
- *          Rodrigo Moya <rodrigo ximian com>
+/*
+ * e-cal-backend.c
  *
  * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU Lesser General Public
- * License as published by the Free Software Foundation.
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
  *
  * This program 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.
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
  *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
 #include <config.h>
@@ -35,9 +30,10 @@
 #define EDC_ERROR(_code)       e_data_cal_create_error (_code, NULL)
 #define EDC_NOT_OPENED_ERROR   e_data_cal_create_error (NotOpened, NULL)
 
+typedef struct _AsyncContext AsyncContext;
+typedef struct _DispatchNode DispatchNode;
 typedef struct _SignalClosure SignalClosure;
 
-/* Private part of the CalBackend structure */
 struct _ECalBackendPrivate {
        ESourceRegistry *registry;
 
@@ -56,6 +52,47 @@ struct _ECalBackendPrivate {
 
        GHashTable *zone_cache;
        GMutex zone_cache_lock;
+
+       GMutex operation_lock;
+       GHashTable *operation_ids;
+       GQueue pending_operations;
+       guint32 next_operation_id;
+       GSimpleAsyncResult *blocked;
+};
+
+struct _AsyncContext {
+       /* Inputs */
+       gchar *uid;
+       gchar *rid;
+       gchar *alarm_uid;
+       gchar *calobj;
+       gchar *query;
+       gchar *tzid;
+       gchar *tzobject;
+       ECalObjModType mod;
+       const gchar *prop_name;
+       time_t start;
+       time_t end;
+       GSList *compid_list;
+       GSList *string_list;
+
+       /* Outputs */
+       GQueue result_queue;
+
+       /* One of these should point to result_queue
+        * so any leftover resources can be released. */
+       GQueue *object_queue;
+       GQueue *string_queue;
+};
+
+struct _DispatchNode {
+       /* This is the dispatch function
+        * that invokes the class method. */
+       GSimpleAsyncThreadFunc dispatch_func;
+       gboolean blocking_operation;
+
+       GSimpleAsyncResult *simple;
+       GCancellable *cancellable;
 };
 
 struct _SignalClosure {
@@ -63,7 +100,6 @@ struct _SignalClosure {
        icaltimezone *cached_zone;
 };
 
-/* Property IDs */
 enum {
        PROP_0,
        PROP_CACHE_DIR,
@@ -92,6 +128,47 @@ G_DEFINE_TYPE_WITH_CODE (
                e_cal_backend_timezone_cache_init))
 
 static void
+async_context_free (AsyncContext *async_context)
+{
+       GQueue *queue;
+
+       g_free (async_context->uid);
+       g_free (async_context->rid);
+       g_free (async_context->alarm_uid);
+       g_free (async_context->calobj);
+       g_free (async_context->query);
+       g_free (async_context->tzid);
+       g_free (async_context->tzobject);
+
+       g_slist_free_full (
+               async_context->compid_list,
+               (GDestroyNotify) e_cal_component_free_id);
+
+       g_slist_free_full (
+               async_context->string_list,
+               (GDestroyNotify) g_free);
+
+       queue = async_context->object_queue;
+       while (queue != NULL && !g_queue_is_empty (queue))
+               g_object_unref (g_queue_pop_head (queue));
+
+       queue = async_context->string_queue;
+       while (queue != NULL && !g_queue_is_empty (queue))
+               g_free (g_queue_pop_head (queue));
+
+       g_slice_free (AsyncContext, async_context);
+}
+
+static void
+dispatch_node_free (DispatchNode *dispatch_node)
+{
+       g_clear_object (&dispatch_node->simple);
+       g_clear_object (&dispatch_node->cancellable);
+
+       g_slice_free (DispatchNode, dispatch_node);
+}
+
+static void
 signal_closure_free (SignalClosure *signal_closure)
 {
        g_weak_ref_set (&signal_closure->backend, NULL);
@@ -109,6 +186,143 @@ cal_backend_free_zone (icaltimezone *zone)
 }
 
 static void
+cal_backend_push_operation (ECalBackend *backend,
+                            GSimpleAsyncResult *simple,
+                            GCancellable *cancellable,
+                            gboolean blocking_operation,
+                            GSimpleAsyncThreadFunc dispatch_func)
+{
+       DispatchNode *node;
+
+       g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
+       g_return_if_fail (dispatch_func != NULL);
+
+       g_mutex_lock (&backend->priv->operation_lock);
+
+       node = g_slice_new0 (DispatchNode);
+       node->dispatch_func = dispatch_func;
+       node->blocking_operation = blocking_operation;
+       node->simple = g_object_ref (simple);
+
+       if (G_IS_CANCELLABLE (cancellable))
+               node->cancellable = g_object_ref (cancellable);
+
+       g_queue_push_tail (&backend->priv->pending_operations, node);
+
+       g_mutex_unlock (&backend->priv->operation_lock);
+}
+
+static gboolean
+cal_backend_dispatch_thread (GIOSchedulerJob *job,
+                             GCancellable *cancellable,
+                             gpointer user_data)
+{
+       DispatchNode *node = user_data;
+       GError *error = NULL;
+
+       if (g_cancellable_set_error_if_cancelled (cancellable, &error)) {
+               g_simple_async_result_take_error (node->simple, error);
+               g_simple_async_result_complete_in_idle (node->simple);
+       } else {
+               GAsyncResult *result;
+               GObject *source_object;
+
+               result = G_ASYNC_RESULT (node->simple);
+               source_object = g_async_result_get_source_object (result);
+               node->dispatch_func (node->simple, source_object, cancellable);
+               g_object_unref (source_object);
+       }
+
+       return FALSE;
+}
+
+static gboolean
+cal_backend_dispatch_next_operation (ECalBackend *backend)
+{
+       DispatchNode *node;
+
+       g_mutex_lock (&backend->priv->operation_lock);
+
+       /* We can't dispatch additional operations
+        * while a blocking operation is in progress. */
+       if (backend->priv->blocked != NULL) {
+               g_mutex_unlock (&backend->priv->operation_lock);
+               return FALSE;
+       }
+
+       /* Pop the next DispatchNode off the queue. */
+       node = g_queue_pop_head (&backend->priv->pending_operations);
+       if (node == NULL) {
+               g_mutex_unlock (&backend->priv->operation_lock);
+               return FALSE;
+       }
+
+       /* If this a blocking operation, block any
+        * further dispatching until this finishes. */
+       if (node->blocking_operation)
+               backend->priv->blocked = g_object_ref (node->simple);
+
+       g_mutex_unlock (&backend->priv->operation_lock);
+
+       g_io_scheduler_push_job (
+               cal_backend_dispatch_thread,
+               node, (GDestroyNotify) dispatch_node_free,
+               G_PRIORITY_DEFAULT,
+               node->cancellable);
+
+       return TRUE;
+}
+
+static guint32
+cal_backend_stash_operation (ECalBackend *backend,
+                             GSimpleAsyncResult *simple)
+{
+       guint32 opid;
+
+       g_mutex_lock (&backend->priv->operation_lock);
+
+       if (backend->priv->next_operation_id == 0)
+               backend->priv->next_operation_id = 1;
+
+       opid = backend->priv->next_operation_id++;
+
+       g_hash_table_insert (
+               backend->priv->operation_ids,
+               GUINT_TO_POINTER (opid),
+               g_object_ref (simple));
+
+       g_mutex_unlock (&backend->priv->operation_lock);
+
+       return opid;
+}
+
+static GSimpleAsyncResult *
+cal_backend_claim_operation (ECalBackend *backend,
+                             guint32 opid)
+{
+       GSimpleAsyncResult *simple;
+
+       g_return_val_if_fail (opid > 0, NULL);
+
+       g_mutex_lock (&backend->priv->operation_lock);
+
+       simple = g_hash_table_lookup (
+               backend->priv->operation_ids,
+               GUINT_TO_POINTER (opid));
+
+       if (simple != NULL) {
+               /* Steal the hash table's reference. */
+               g_hash_table_steal (
+                       backend->priv->operation_ids,
+                       GUINT_TO_POINTER (opid));
+       }
+
+       g_mutex_unlock (&backend->priv->operation_lock);
+
+       return simple;
+}
+
+static void
 cal_backend_set_default_cache_dir (ECalBackend *backend)
 {
        ESource *source;
@@ -333,6 +547,13 @@ cal_backend_dispose (GObject *object)
        g_clear_object (&priv->registry);
        g_clear_object (&priv->data_cal);
 
+       g_hash_table_remove_all (priv->operation_ids);
+
+       while (!g_queue_is_empty (&priv->pending_operations))
+               g_object_unref (g_queue_pop_head (&priv->pending_operations));
+
+       g_clear_object (&priv->blocked);
+
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_cal_backend_parent_class)->dispose (object);
 }
@@ -352,6 +573,9 @@ cal_backend_finalize (GObject *object)
 
        g_free (priv->cache_dir);
 
+       g_mutex_clear (&priv->operation_lock);
+       g_hash_table_destroy (priv->operation_ids);
+
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_cal_backend_parent_class)->finalize (object);
 }
@@ -651,6 +875,14 @@ e_cal_backend_init (ECalBackend *backend)
 
        backend->priv->zone_cache = zone_cache;
        g_mutex_init (&backend->priv->zone_cache_lock);
+
+       g_mutex_init (&backend->priv->operation_lock);
+
+       backend->priv->operation_ids = g_hash_table_new_full (
+               (GHashFunc) g_direct_hash,
+               (GEqualFunc) g_direct_equal,
+               (GDestroyNotify) NULL,
+               (GDestroyNotify) g_object_unref);
 }
 
 /**
@@ -700,7 +932,7 @@ e_cal_backend_ref_data_cal (ECalBackend *backend)
 }
 
 /**
- * e_cal_backend_set_data_book:
+ * e_cal_backend_set_data_cal:
  * @backend: an #ECalBackend
  * @data_cal: an #EDataCal
  *
@@ -933,42 +1165,191 @@ e_cal_backend_create_cache_filename (ECalBackend *backend,
                                      const gchar *filename,
                                      gint fileindex)
 {
+       const gchar *cache_dir;
+
        g_return_val_if_fail (backend != NULL, NULL);
        g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
 
-       return e_filename_mkdir_encoded (e_cal_backend_get_cache_dir (backend), uid, filename, fileindex);
+       cache_dir = e_cal_backend_get_cache_dir (backend);
+
+       return e_filename_mkdir_encoded (cache_dir, uid, filename, fileindex);
+}
+
+/**
+ * e_cal_backend_get_backend_property_sync:
+ * @backend: an #ECalBackend
+ * @prop_name: a backend property name
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Obtains the value of the backend property named @prop_name.
+ *
+ * Despite appearances, this function does not actually block.  So the
+ * @cancellable can safely be %NULL.  If can, however, return an error
+ * if @prop_name is not recognized.
+ *
+ * The returned string must be freed with g_free() when finished with it.
+ *
+ * Returns: the value for @prop_name
+ *
+ * Since: 3.10
+ **/
+gchar *
+e_cal_backend_get_backend_property_sync (ECalBackend *backend,
+                                         const gchar *prop_name,
+                                         GCancellable *cancellable,
+                                         GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gchar *prop_value;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
+       g_return_val_if_fail (prop_name != NULL, NULL);
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_get_backend_property (
+               backend, prop_name, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       prop_value = e_cal_backend_get_backend_property_finish (
+               backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return prop_value;
+}
+
+/* Helper for e_cal_backend_get_backend_property() */
+static void
+cal_backend_get_backend_property_thread (GSimpleAsyncResult *simple,
+                                         GObject *source_object,
+                                         GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+       AsyncContext *async_context;
+       guint32 opid;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->get_backend_property != NULL);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       opid = cal_backend_stash_operation (backend, simple);
+
+       class->get_backend_property (
+               backend, data_cal, opid, cancellable,
+               async_context->prop_name);
+
+       g_object_unref (data_cal);
 }
 
 /**
  * e_cal_backend_get_backend_property:
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @prop_name: property name to get value of; cannot be NULL
+ * @prop_name: a backend property name
+ * @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
  *
- * Calls the get_backend_property method on the given backend.
- * This might be finished with e_data_cal_respond_get_backend_property().
- * Default implementation takes care of common properties and returns
- * an 'unsupported' error for any unknown properties. The subclass may
- * always call this default implementation for properties which fetching
- * it doesn't overwrite.
+ * Asynchronously obtains the value of the backend property named @prop_name.
  *
- * Since: 3.2
+ * Despite appearances, e_cal_backend_get_backend_property_sync() does not
+ * actually block, and is more convenient than this function.  This function
+ * exists for the moment merely to invoke the class method and collect the
+ * result from #EDataCal.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_cal_backend_get_backend_property_finish() to get the result of
+ * the operation.
+ *
+ * Since: 3.10
  **/
 void
 e_cal_backend_get_backend_property (ECalBackend *backend,
-                                    EDataCal *cal,
-                                    guint32 opid,
+                                    const gchar *prop_name,
                                     GCancellable *cancellable,
-                                    const gchar *prop_name)
+                                    GAsyncReadyCallback callback,
+                                    gpointer user_data)
 {
-       g_return_if_fail (backend != NULL);
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
        g_return_if_fail (prop_name != NULL);
-       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_backend_property != NULL);
 
-       (* E_CAL_BACKEND_GET_CLASS (backend)->get_backend_property) (backend, cal, opid, cancellable, 
prop_name);
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->prop_name = g_intern_string (prop_name);
+       async_context->string_queue = &async_context->result_queue;
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_cal_backend_get_backend_property);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               cal_backend_get_backend_property_thread);
+
+       cal_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_cal_backend_get_backend_property_finish:
+ * @backend: an #ECalBackend
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_cal_backend_get_backend_property().
+ *
+ * The returned string must be freed with g_free() when finished with it.
+ *
+ * Returns: the requested property value
+ *
+ * Since: 3.10
+ **/
+gchar *
+e_cal_backend_get_backend_property_finish (ECalBackend *backend,
+                                           GAsyncResult *result,
+                                           GError **error)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       gchar *prop_value;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_get_backend_property), NULL);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (g_simple_async_result_propagate_error (simple, error))
+               return NULL;
+
+       prop_value = g_queue_pop_head (&async_context->result_queue);
+       g_return_val_if_fail (prop_value != NULL, NULL);
+
+       g_warn_if_fail (g_queue_is_empty (&async_context->result_queue));
+
+       return prop_value;
 }
 
 /**
@@ -1070,492 +1451,2692 @@ e_cal_backend_list_views (ECalBackend *backend)
 }
 
 /**
- * e_cal_backend_open:
+ * e_cal_backend_open_sync:
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @only_if_exists: Whether the calendar should be opened only if it already
- * exists.  If FALSE, a new calendar will be created when the specified @uri
- * does not exist.
- *
- * Opens a calendar backend with data from a calendar stored at the specified URI.
- * This might be finished with e_data_cal_respond_open() or e_cal_backend_respond_opened(),
- * though the overall opening phase finishes only after call
- * of e_cal_backend_notify_opened() after which call the backend
- * is either fully opened (including authentication against (remote)
- * server/storage) or an error was encountered during this opening phase.
- * 'opened' and 'opening' properties are updated automatically.
- * The backend refuses all other operations until the opening phase is finished.
- *
- * The e_cal_backend_notify_opened() is called either from this function
- * or from e_cal_backend_authenticate_user(), or after necessary steps
- * initiated by these two functions.
- *
- * The opening phase usually works like this:
- * 1) client requests open for the backend
- * 2) server receives this request and calls e_cal_backend_open() - the opening phase begun
- * 3) either the backend is opened during this call, and notifies client
- *    with e_cal_backend_notify_opened() about that. This is usually
- *    for local backends; their opening phase is finished
- * 4) or the backend requires authentication, thus it notifies client
- *    about that with e_cal_backend_notify_auth_required() and is
- *    waiting for credentials, which will be received from client
- *    by e_cal_backend_authenticate_user() call. Backend's opening
- *    phase is still running in this case, thus it doesn't call
- *    e_cal_backend_notify_opened() within e_cal_backend_open() call.
- * 5) when backend receives credentials in e_cal_backend_authenticate_user()
- *    then it tries to authenticate against a server/storage with them
- *    and only after it knows result of the authentication, whether user
- *    was or wasn't authenticated, it notifies client with the result
- *    by e_cal_backend_notify_opened() and it's opening phase is
- *    finished now. If there was no error returned then the backend is
- *    considered opened, otherwise it's considered closed. Use AuthenticationFailed
- *    error when the given credentials were rejected by the server/store, which
- *    will result in a re-prompt on the client side, otherwise use AuthenticationRequired
- *    if there was anything wrong with the given credentials. Set error's
- *    message to a reason for a re-prompt, it'll be shown to a user.
- * 6) client checks error returned from e_cal_backend_notify_opened() and
- *    reprompts for a password if it was AuthenticationFailed. Otherwise
- *    considers backend opened based on the error presence (no error means success).
- *
- * In any case, the call of e_cal_backend_open() should be always finished
- * with e_data_cal_respond_open(), which has no influence on the opening phase,
- * or alternatively with e_cal_backend_respond_opened(). Never use authentication
- * errors in e_data_cal_respond_open() to notify the client the authentication is
- * required, there is e_cal_backend_notify_auth_required() for this.
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * "Opens" the @backend.  Opening a backend is something of an outdated
+ * concept, but the operation is hanging around for a little while longer.
+ * This usually involves some custom initialization logic, and testing of
+ * remote authentication if applicable.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
  **/
-void
-e_cal_backend_open (ECalBackend *backend,
-                    EDataCal *cal,
-                    guint32 opid,
-                    GCancellable *cancellable,
-                    gboolean only_if_exists)
+gboolean
+e_cal_backend_open_sync (ECalBackend *backend,
+                         GCancellable *cancellable,
+                         GError **error)
 {
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->open != NULL);
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_open (
+               backend, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_cal_backend_open_finish (backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_cal_backend_open() */
+static void
+cal_backend_open_thread (GSimpleAsyncResult *simple,
+                         GObject *source_object,
+                         GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->open != NULL);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
 
        if (e_cal_backend_is_opened (backend)) {
-               e_data_cal_respond_open (cal, opid, NULL);
+               g_simple_async_result_complete_in_idle (simple);
+
        } else {
-               (* E_CAL_BACKEND_GET_CLASS (backend)->open) (backend, cal, opid, cancellable, only_if_exists);
+               guint32 opid;
+
+               opid = cal_backend_stash_operation (backend, simple);
+
+               class->open (backend, data_cal, opid, cancellable, FALSE);
        }
+
+       g_object_unref (data_cal);
 }
 
 /**
- * e_cal_backend_refresh:
+ * e_cal_backend_open:
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
+ * @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
+ *
+ * Asynchronously "opens" the @backend.  Opening a backend is something of
+ * an outdated concept, but the operation is hanging around for a little
+ * while longer.  This usually involves some custom initialization logic,
+ * and testing of remote authentication if applicable.
  *
- * Refreshes the calendar being accessed by the given backend.
- * This might be finished with e_data_cal_respond_refresh(),
- * and it might be called as soon as possible; it doesn't mean
- * that the refreshing is done after calling that, the backend
- * is only notifying client whether it started the refresh process
- * or not.
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_cal_backend_open_finish() to get the result of the operation.
  *
- * Since: 2.30
+ * Since: 3.10
  **/
 void
-e_cal_backend_refresh (ECalBackend *backend,
-                       EDataCal *cal,
-                       guint32 opid,
-                       GCancellable *cancellable)
+e_cal_backend_open (ECalBackend *backend,
+                    GCancellable *cancellable,
+                    GAsyncReadyCallback callback,
+                    gpointer user_data)
 {
-       g_return_if_fail (backend != NULL);
+       GSimpleAsyncResult *simple;
+
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
 
-       if (!E_CAL_BACKEND_GET_CLASS (backend)->refresh)
-               e_data_cal_respond_refresh (cal, opid, EDC_ERROR (UnsupportedMethod));
-       else if (!e_cal_backend_is_opened (backend))
-               e_data_cal_respond_refresh (cal, opid, EDC_NOT_OPENED_ERROR);
-       else
-               (* E_CAL_BACKEND_GET_CLASS (backend)->refresh) (backend, cal, opid, cancellable);
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback,
+               user_data, e_cal_backend_open);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, TRUE,
+               cal_backend_open_thread);
+
+       cal_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
 }
 
 /**
- * e_cal_backend_get_object:
+ * e_cal_backend_open_finish:
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @uid: Unique identifier for a calendar object.
- * @rid: ID for the object's recurrence to get.
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_cal_backend_open().
  *
- * Queries a calendar backend for a calendar object based on its unique
- * identifier and its recurrence ID (if a recurrent appointment).
- * This might be finished with e_data_cal_respond_get_object().
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
  **/
-void
-e_cal_backend_get_object (ECalBackend *backend,
-                          EDataCal *cal,
-                          guint32 opid,
-                          GCancellable *cancellable,
-                          const gchar *uid,
-                          const gchar *rid)
+gboolean
+e_cal_backend_open_finish (ECalBackend *backend,
+                           GAsyncResult *result,
+                           GError **error)
 {
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (uid != NULL);
-       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_object != NULL);
-
-       if (!e_cal_backend_is_opened (backend))
-               e_data_cal_respond_get_object (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
-       else
-               (* E_CAL_BACKEND_GET_CLASS (backend)->get_object) (backend, cal, opid, cancellable, uid, rid);
+       GSimpleAsyncResult *simple;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_open), FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+
+       /* This operation blocks, so we need to let waiting operations
+        * through.  (FIXME Centralize this for any blocking operation.) */
+       g_mutex_lock (&backend->priv->operation_lock);
+       if (backend->priv->blocked == simple)
+               g_clear_object (&backend->priv->blocked);
+       g_mutex_unlock (&backend->priv->operation_lock);
+       while (cal_backend_dispatch_next_operation (backend))
+               ;
+
+       /* Assume success unless a GError is set. */
+       return !g_simple_async_result_propagate_error (simple, error);
 }
 
 /**
- * e_cal_backend_get_object_list:
+ * e_cal_backend_refresh_sync:
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @sexp: Expression to search for.
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Initiates a refresh for @backend, if the @backend supports refreshing.
+ * The actual refresh operation completes on its own time.  This function
+ * merely initiates the operation.
+ *
+ * If an error occrs while initiating the refresh, the function will set
+ * @error and return %FALSE.  If the @backend does not support refreshing,
+ * the function will set an %UnsupportedMethod error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
  *
- * Calls the get_object_list method on the given backend.
- * This might be finished with e_data_cal_respond_get_object_list().
+ * Since: 3.10
  **/
-void
-e_cal_backend_get_object_list (ECalBackend *backend,
-                               EDataCal *cal,
-                               guint32 opid,
-                               GCancellable *cancellable,
-                               const gchar *sexp)
+gboolean
+e_cal_backend_refresh_sync (ECalBackend *backend,
+                            GCancellable *cancellable,
+                            GError **error)
 {
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_object_list != NULL);
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
 
-       if (!e_cal_backend_is_opened (backend))
-               e_data_cal_respond_get_object_list (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
-       else
-               (* E_CAL_BACKEND_GET_CLASS (backend)->get_object_list) (backend, cal, opid, cancellable, 
sexp);
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_refresh (
+               backend, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_cal_backend_refresh_finish (backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_cal_backend_refresh() */
+static void
+cal_backend_refresh_thread (GSimpleAsyncResult *simple,
+                            GObject *source_object,
+                            GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
+
+       if (class->refresh == NULL) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       UnsupportedMethod,
+                       "%s", e_data_cal_status_to_string (
+                       UnsupportedMethod));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else if (!e_cal_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       NotOpened,
+                       "%s", e_data_cal_status_to_string (
+                       NotOpened));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = cal_backend_stash_operation (backend, simple);
+
+               class->refresh (backend, data_cal, opid, cancellable);
+       }
+
+       g_object_unref (data_cal);
 }
 
 /**
- * e_cal_backend_get_free_busy:
+ * e_cal_backend_refresh:
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @users: List of users to get free/busy information for.
- * @start: Start time for query.
- * @end: End time for query.
+ * @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
+ *
+ * Asynchronously initiates a refresh for @backend, if the @backend supports
+ * refreshing.  The actual refresh operation completes on its own time.  This
+ * function, along with e_cal_backend_refresh_finish(), merely initiates the
+ * operation.
  *
- * Gets a free/busy object for the given time interval. Client side is
- * notified about free/busy objects throug e_data_cal_report_free_busy_data().
- * This might be finished with e_data_cal_respond_get_free_busy().
+ * Once the refresh is initiated, @callback will be called.  You can then
+ * call e_cal-backend_refresh_finish() to get the result of the initiation.
+ *
+ * Since: 3.10
  **/
 void
-e_cal_backend_get_free_busy (ECalBackend *backend,
-                             EDataCal *cal,
-                             guint32 opid,
-                             GCancellable *cancellable,
-                             const GSList *users,
-                             time_t start,
-                             time_t end)
+e_cal_backend_refresh (ECalBackend *backend,
+                       GCancellable *cancellable,
+                       GAsyncReadyCallback callback,
+                       gpointer user_data)
 {
-       g_return_if_fail (backend != NULL);
+       GSimpleAsyncResult *simple;
+
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback,
+               user_data, e_cal_backend_refresh);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               cal_backend_refresh_thread);
+
+       cal_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_cal_backend_refresh_finish:
+ * @backend: an #ECalBackend
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the refresh initiation started with e_cal_backend_refresh().
+ *
+ * If an error occurred while initiating the refresh, the function will set
+ * @error and return %FALSE.  If the @backend does not support refreshing,
+ * the function will set an %UnsupportedMethod error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_refresh_finish (ECalBackend *backend,
+                              GAsyncResult *result,
+                              GError **error)
+{
+       GSimpleAsyncResult *simple;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_refresh), FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+
+       /* Assume success unless a GError is set. */
+       return !g_simple_async_result_propagate_error (simple, error);
+}
+
+/**
+ * e_cal_backend_get_objects_sync:
+ * @backend: an #ECalBackend
+ * @uid: a unique ID for an iCalendar object
+ * @rid: a recurrence ID, or %NULL
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Obtains an #ECalComponent by its @uid and, optionally, @rid.
+ *
+ * The returned #ECalComponent is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * If an error occurs, the function will set @error and return %NULL.
+ *
+ * Returns: an #ECalComponent, or %NULL
+ *
+ * Since: 3.10
+ **/
+ECalComponent *
+e_cal_backend_get_object_sync (ECalBackend *backend,
+                               const gchar *uid,
+                               const gchar *rid,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       ECalComponent *component;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
+       g_return_val_if_fail (uid != NULL, NULL);
+       /* rid can be NULL */
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_get_object (
+               backend, uid, rid, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       component = e_cal_backend_get_object_finish (backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return component;
+}
+
+/* Helper for e_cal_backend_get_object() */
+static void
+cal_backend_get_object_thread (GSimpleAsyncResult *simple,
+                               GObject *source_object,
+                               GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+       AsyncContext *async_context;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->get_object != NULL);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_cal_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       NotOpened,
+                       "%s", e_data_cal_status_to_string (
+                       NotOpened));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = cal_backend_stash_operation (backend, simple);
+
+               class->get_object (
+                       backend, data_cal, opid, cancellable,
+                       async_context->uid,
+                       async_context->rid);
+       }
+
+       g_object_unref (data_cal);
+}
+
+/**
+ * e_cal_backend_get_object:
+ * @backend: an #ECalBackend
+ * @uid: a unique ID for an iCalendar object
+ * @rid: a recurrence ID, or %NULL
+ * @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
+ *
+ * Asynchronously obtains an #ECalComponent by its @uid and, optionally, @rid.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_cal_backend_get_object_finish() to get the result of the operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_cal_backend_get_object (ECalBackend *backend,
+                          const gchar *uid,
+                          const gchar *rid,
+                          GCancellable *cancellable,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (uid != NULL);
+       /* rid can be NULL */
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->uid = g_strdup (uid);
+       async_context->rid = g_strdup (rid);
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_cal_backend_get_object);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               cal_backend_get_object_thread);
+
+       cal_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_cal_backend_get_object_finish:
+ * @backend: an #ECalBackend
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_cal_backend_get_object().
+ *
+ * The returned #ECalComponent is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * If an error occurs, the function will set @error and return %NULL.
+ *
+ * Returns: an #ECalComponent, or %NULL
+ *
+ * Since: 3.10
+ **/
+ECalComponent *
+e_cal_backend_get_object_finish (ECalBackend *backend,
+                                 GAsyncResult *result,
+                                 GError **error)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       ECalComponent *component;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_get_object), FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (g_simple_async_result_propagate_error (simple, error))
+               return FALSE;
+
+       component = g_queue_pop_head (&async_context->result_queue);
+       g_return_val_if_fail (E_IS_CAL_COMPONENT (component), NULL);
+
+       g_warn_if_fail (g_queue_is_empty (&async_context->result_queue));
+
+       return component;
+}
+
+/**
+ * e_cal_backend_get_object_list_sync:
+ * @backend: an #ECalBackend
+ * @query: a search query in S-expression format
+ * @out_objects: a #GQueue in which to deposit results
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Obtains a set of #ECalComponent instances which satisfy the criteria
+ * specified in @query, and deposits them in @out_objects.
+ *
+ * The returned #ECalComponent instances are referenced for thread-safety
+ * and must be unreferenced with g_object_unref() when finished with them.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ * Note that an empty result set does not necessarily imply an error.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_get_object_list_sync (ECalBackend *backend,
+                                    const gchar *query,
+                                    GQueue *out_objects,
+                                    GCancellable *cancellable,
+                                    GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+       g_return_val_if_fail (query != NULL, FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_get_object_list (
+               backend, query, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_cal_backend_get_object_list_finish (
+               backend, result, out_objects, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_cal_backend_get_object_list() */
+static void
+cal_backend_get_object_list_thread (GSimpleAsyncResult *simple,
+                                    GObject *source_object,
+                                    GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+       AsyncContext *async_context;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->get_object_list != NULL);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_cal_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       NotOpened,
+                       "%s", e_data_cal_status_to_string (
+                       NotOpened));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = cal_backend_stash_operation (backend, simple);
+
+               class->get_object_list (
+                       backend, data_cal, opid, cancellable,
+                       async_context->query);
+       }
+
+       g_object_unref (data_cal);
+}
+
+/**
+ * e_cal_backend_get_object_list:
+ * @backend: an #ECalBackend
+ * @query: a search query in S-expression format
+ * @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
+ *
+ * Asynchronously obtains a set of #ECalComponent instances which satisfy
+ * the criteria specified in @query.
+ *
+ * When the operation in finished, @callback will be called.  You can then
+ * call e_cal_backend_get_object_list_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_cal_backend_get_object_list (ECalBackend *backend,
+                               const gchar *query,
+                               GCancellable *cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (query != NULL);
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->query = g_strdup (query);
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_cal_backend_get_object_list);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               cal_backend_get_object_list_thread);
+
+       cal_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_cal_backend_get_object_list_finish:
+ * @backend: an #ECalBackend
+ * @result: a #GAsyncResult
+ * @out_objects: a #GQueue in which to deposit results
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_cal_backend_get_object_list().
+ *
+ * The matching #ECalComponent instances are deposited in @out_objects.
+ * The returned #ECalComponent instances are referenced for thread-safety
+ * and must be unreferenced with g_object_unref() when finished with them.
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ * Note that an empty result set does not necessarily imply an error.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_get_object_list_finish (ECalBackend *backend,
+                                      GAsyncResult *result,
+                                      GQueue *out_objects,
+                                      GError **error)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_get_object_list), FALSE);
+       g_return_val_if_fail (out_objects != NULL, FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (g_simple_async_result_propagate_error (simple, error))
+               return FALSE;
+
+       e_queue_transfer (&async_context->result_queue, out_objects);
+
+       return TRUE;
+}
+
+/**
+ * e_cal_backend_get_free_busy_sync:
+ * @backend: an #ECalBackend
+ * @start: start time
+ * @end: end time
+ * @users: a %NULL-terminated array of user strings
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Obtains a free/busy object for the list of @users in the time interval
+ * between @start and @end.  The free/busy results are returned through the
+ * e_data_cal_report_free_busy_data() function rather than directly through
+ * this function.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure.
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_get_free_busy_sync (ECalBackend *backend,
+                                  time_t start,
+                                  time_t end,
+                                  const gchar * const *users,
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+       g_return_val_if_fail (users != NULL, FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_get_free_busy (
+               backend, start, end, users, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_cal_backend_get_free_busy_finish (
+               backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+static void
+cal_backend_get_free_busy_thread (GSimpleAsyncResult *simple,
+                                  GObject *source_object,
+                                  GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+       AsyncContext *async_context;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->get_free_busy != NULL);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_cal_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       NotOpened,
+                       "%s", e_data_cal_status_to_string (
+                       NotOpened));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = cal_backend_stash_operation (backend, simple);
+
+               class->get_free_busy (
+                       backend, data_cal, opid, cancellable,
+                       async_context->string_list,
+                       async_context->start,
+                       async_context->end);
+       }
+
+       g_object_unref (data_cal);
+}
+
+/**
+ * e_cal_backend_get_free_busy:
+ * @backend: an #ECalBackend
+ * @start: start time
+ * @end: end time
+ * @users: a %NULL-terminated array of user strings
+ * @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
+ *
+ * Asynchronously obtains a free/busy object for the list of @users in the
+ * time interval between @start and @end.
+ *
+ * When the operation is finished, @callback will be called.  You can
+ * then call e_cal_backend_get_free_busy_finish() to get the result of
+ * the operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_cal_backend_get_free_busy (ECalBackend *backend,
+                             time_t start,
+                             time_t end,
+                             const gchar * const *users,
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       GSList *list = NULL;
+       gint ii;
+
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
        g_return_if_fail (start != -1 && end != -1);
        g_return_if_fail (start <= end);
-       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_free_busy != NULL);
+       g_return_if_fail (users != NULL);
+
+       for (ii = 0; users[ii] != NULL; ii++)
+               list = g_slist_prepend (list, g_strdup (users[ii]));
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->start = start;
+       async_context->end = end;
+       async_context->string_list = g_slist_reverse (list);
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_cal_backend_get_free_busy);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               cal_backend_get_free_busy_thread);
+
+       cal_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_cal_backend_get_free_busy_finish:
+ * @backend: an #ECalBackend
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_cal_backend_get_free_busy().
+ *
+ * The free/busy results are returned through the
+ * e_data_cal_report_free_busy_data() function rather than directly through
+ * this function.
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_get_free_busy_finish (ECalBackend *backend,
+                                    GAsyncResult *result,
+                                    GError **error)
+{
+       GSimpleAsyncResult *simple;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_get_free_busy), FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+
+       /* Assume success unless a GError is set. */
+       return !g_simple_async_result_propagate_error (simple, error);
+}
+
+/**
+ * e_cal_backend_create_objects_sync:
+ * @backend: an #ECalBackend
+ * @calobjs: a %NULL-terminated array of iCalendar strings
+ * @out_uids: a #GQueue in which to deposit results
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Creates one or more new iCalendar objects from @calobjs, and deposits
+ * the unique ID string for each newly-created object in @out_uids.
+ *
+ * Free the returned ID strings with g_free() when finished with them.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_create_objects_sync (ECalBackend *backend,
+                                   const gchar * const *calobjs,
+                                   GQueue *out_uids,
+                                   GCancellable *cancellable,
+                                   GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+       g_return_val_if_fail (calobjs != NULL, FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_create_objects (
+               backend, calobjs, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_cal_backend_create_objects_finish (
+               backend, result, out_uids, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_cal_backend_create_objects() */
+static void
+cal_backend_create_objects_thread (GSimpleAsyncResult *simple,
+                                   GObject *source_object,
+                                   GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+       AsyncContext *async_context;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (class->create_objects == NULL) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       UnsupportedMethod,
+                       "%s", e_data_cal_status_to_string (
+                       UnsupportedMethod));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else if (!e_cal_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       NotOpened,
+                       "%s", e_data_cal_status_to_string (
+                       NotOpened));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = cal_backend_stash_operation (backend, simple);
+
+               class->create_objects (
+                       backend, data_cal, opid, cancellable,
+                       async_context->string_list);
+       }
+
+       g_object_unref (data_cal);
+}
+
+/**
+ * e_cal_backend_create_objects:
+ * @backend: an #ECalBackend
+ * @calobjs: a %NULL-terminated array of iCalendar strings
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisifed
+ * @user_data: data to pass to the callback function
+ *
+ * Asynchronously creates one or more new iCalendar objects from @calobjs.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_cal_backend_create_objects_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_cal_backend_create_objects (ECalBackend *backend,
+                              const gchar * const *calobjs,
+                              GCancellable *cancellable,
+                              GAsyncReadyCallback callback,
+                              gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       GSList *list = NULL;
+       gint ii;
+
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (calobjs != NULL);
+
+       for (ii = 0; calobjs[ii] != NULL; ii++)
+               list = g_slist_prepend (list, g_strdup (calobjs[ii]));
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->string_list = g_slist_reverse (list);
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_cal_backend_create_objects);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               cal_backend_create_objects_thread);
+
+       cal_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_cal_backend_create_objects_finish:
+ * @backend: an #ECalBackend
+ * @result: a #GAsyncResult
+ * @out_uids: a #GQueue in which to deposit results
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_cal_backend_create_objects().
+ *
+ * A unique ID string for each newly-created object is deposited in @out_uids.
+ * Free the returned ID strings with g_free() when finished with them.
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_create_objects_finish (ECalBackend *backend,
+                                     GAsyncResult *result,
+                                     GQueue *out_uids,
+                                     GError **error)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       GQueue *string_queue;
+       GQueue *component_queue;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_create_objects), FALSE);
+       g_return_val_if_fail (out_uids != NULL, FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (g_simple_async_result_propagate_error (simple, error))
+               return FALSE;
+
+       /* XXX Packing GQueues in a GQueue is pretty awkward, but until
+        *     the backend methods can be updated I'm trying to make do
+        *     with a single data container for all kinds of results. */
+
+       string_queue = g_queue_pop_head (&async_context->result_queue);
+       component_queue = g_queue_pop_head (&async_context->result_queue);
+
+       g_warn_if_fail (g_queue_is_empty (&async_context->result_queue));
+
+       g_return_val_if_fail (string_queue != NULL, FALSE);
+       g_return_val_if_fail (component_queue != NULL, FALSE);
+
+       e_queue_transfer (string_queue, out_uids);
+
+       while (!g_queue_is_empty (component_queue)) {
+               ECalComponent *component;
+
+               component = g_queue_pop_head (component_queue);
+               e_cal_backend_notify_component_created (backend, component);
+               g_object_unref (component);
+       }
+
+       g_queue_free (string_queue);
+       g_queue_free (component_queue);
+
+       return TRUE;
+}
+
+/**
+ * e_cal_backend_modify_objects:
+ * @backend: an #ECalBackend
+ * @calobjs: a %NULL-terminated array of iCalendar strings
+ * @mod: modification type for recurrences
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Modifies one or more iCalendar objects according to @calobjs and @mod.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_modify_objects_sync (ECalBackend *backend,
+                                   const gchar * const *calobjs,
+                                   ECalObjModType mod,
+                                   GCancellable *cancellable,
+                                   GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+       g_return_val_if_fail (calobjs != NULL, FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_modify_objects (
+               backend, calobjs, mod, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_cal_backend_modify_objects_finish (
+               backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_cal_backend_modify_objects() */
+static void
+cal_backend_modify_objects_thread (GSimpleAsyncResult *simple,
+                                   GObject *source_object,
+                                   GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+       AsyncContext *async_context;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (class->modify_objects == NULL) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       UnsupportedMethod,
+                       "%s", e_data_cal_status_to_string (
+                       UnsupportedMethod));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else if (!e_cal_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       NotOpened,
+                       "%s", e_data_cal_status_to_string (
+                       NotOpened));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = cal_backend_stash_operation (backend, simple);
+
+               class->modify_objects (
+                       backend, data_cal, opid, cancellable,
+                       async_context->string_list,
+                       async_context->mod);
+       }
+
+       g_object_unref (data_cal);
+}
+
+/**
+ * e_cal_backend_modify_objects:
+ * @backend: an #ECalBackend
+ * @calobjs: a %NULL-terminated array of iCalendar strings
+ * @mod: modification type for recurrences
+ * @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
+ *
+ * Asynchronously modifies one or more iCalendar objects according to
+ * @calobjs and @mod.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_cal_backend_modify_objects_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_cal_backend_modify_objects (ECalBackend *backend,
+                              const gchar * const *calobjs,
+                              ECalObjModType mod,
+                              GCancellable *cancellable,
+                              GAsyncReadyCallback callback,
+                              gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       GSList *list = NULL;
+       gint ii;
+
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (calobjs != NULL);
+
+       for (ii = 0; calobjs[ii] != NULL; ii++)
+               list = g_slist_prepend (list, g_strdup (calobjs[ii]));
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->string_list = g_slist_reverse (list);
+       async_context->mod = mod;
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_cal_backend_modify_objects);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               cal_backend_modify_objects_thread);
+
+       cal_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_cal_backend_modify_objects_finish:
+ * @backend: an #ECalBackend
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_cal_backend_modify_objects().
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_modify_objects_finish (ECalBackend *backend,
+                                     GAsyncResult *result,
+                                     GError **error)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       GQueue *old_component_queue;
+       GQueue *new_component_queue;
+       guint length, ii;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_modify_objects), FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (g_simple_async_result_propagate_error (simple, error))
+               return FALSE;
+
+       /* XXX Packing GQueues in a GQueue is pretty awkward, but until
+        *     the backend methods can be updated I'm trying to make do
+        *     with a single data container for all kinds of results. */
+
+       old_component_queue = g_queue_pop_head (&async_context->result_queue);
+       new_component_queue = g_queue_pop_head (&async_context->result_queue);
+
+       g_warn_if_fail (g_queue_is_empty (&async_context->result_queue));
+
+       g_return_val_if_fail (old_component_queue != NULL, FALSE);
+       g_return_val_if_fail (new_component_queue != NULL, FALSE);
+
+       length = MIN (
+               g_queue_get_length (old_component_queue),
+               g_queue_get_length (new_component_queue));
+
+       for (ii = 0; ii < length; ii++) {
+               ECalComponent *old_component;
+               ECalComponent *new_component;
+
+               old_component = g_queue_pop_head (old_component_queue);
+               new_component = g_queue_pop_head (new_component_queue);
+
+               e_cal_backend_notify_component_modified (
+                       backend, old_component, new_component);
+
+               g_object_unref (old_component);
+               g_object_unref (new_component);
+       }
+
+       g_warn_if_fail (g_queue_is_empty (old_component_queue));
+       g_queue_free (old_component_queue);
+
+       g_warn_if_fail (g_queue_is_empty (new_component_queue));
+       g_queue_free (new_component_queue);
+
+       return TRUE;
+}
+
+/**
+ * e_cal_backend_remove_objects_sync:
+ * @backend: an #ECalBackend
+ * @component_ids: a #GList of #ECalComponentId structs
+ * @mod: modification type for recurrences
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Removes one or more iCalendar objects according to @component_ids and @mod.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_remove_objects_sync (ECalBackend *backend,
+                                   GList *component_ids,
+                                   ECalObjModType mod,
+                                   GCancellable *cancellable,
+                                   GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+       g_return_val_if_fail (component_ids != NULL, FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_remove_objects (
+               backend, component_ids, mod, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_cal_backend_remove_objects_finish (
+               backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_cal_backend_remove_objects() */
+static void
+cal_backend_remove_objects_thread (GSimpleAsyncResult *simple,
+                                   GObject *source_object,
+                                   GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+       AsyncContext *async_context;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->remove_objects != NULL);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_cal_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       NotOpened,
+                       "%s", e_data_cal_status_to_string (
+                       NotOpened));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = cal_backend_stash_operation (backend, simple);
+
+               class->remove_objects (
+                       backend, data_cal, opid, cancellable,
+                       async_context->compid_list,
+                       async_context->mod);
+       }
+
+       g_object_unref (data_cal);
+}
+
+/**
+ * e_cal_backend_remove_objects:
+ * @backend: an #ECalBackend
+ * @component_ids: a #GList of #ECalComponentId structs
+ * @mod: modification type for recurrences
+ * @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
+ *
+ * Asynchronously removes one or more iCalendar objects according to
+ * @component_ids and @mod.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_cal_backend_remove_objects_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_cal_backend_remove_objects (ECalBackend *backend,
+                              GList *component_ids,
+                              ECalObjModType mod,
+                              GCancellable *cancellable,
+                              GAsyncReadyCallback callback,
+                              gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       GSList *list = NULL;
+
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (component_ids != NULL);
+
+       while (component_ids != NULL) {
+               ECalComponentId *id = component_ids->data;
+               list = g_slist_prepend (list, e_cal_component_id_copy (id));
+               component_ids = g_list_next (component_ids);
+       }
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->compid_list = g_slist_reverse (list);
+       async_context->mod = mod;
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_cal_backend_remove_objects);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               cal_backend_remove_objects_thread);
+
+       cal_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_cal_backend_remove_objects_finish:
+ * @backend: an #ECalBackend
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_cal_backend_remove_objects().
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_remove_objects_finish (ECalBackend *backend,
+                                     GAsyncResult *result,
+                                     GError **error)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       GQueue *component_id_queue;
+       GQueue *old_component_queue;
+       GQueue *new_component_queue;
+       guint length, ii;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_remove_objects), FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (g_simple_async_result_propagate_error (simple, error))
+               return FALSE;
+
+       /* XXX Packing GQueues in a GQueue is pretty awkward, but until
+        *     the backend methods can be updated I'm trying to make do
+        *     with a single data container for all kinds of results. */
+
+       component_id_queue = g_queue_pop_head (&async_context->result_queue);
+       old_component_queue = g_queue_pop_head (&async_context->result_queue);
+       new_component_queue = g_queue_pop_head (&async_context->result_queue);
+
+       g_warn_if_fail (g_queue_is_empty (&async_context->result_queue));
+
+       g_return_val_if_fail (component_id_queue != NULL, FALSE);
+       g_return_val_if_fail (old_component_queue != NULL, FALSE);
+       /* new_component_queue may be NULL */
+
+       length = MIN (
+               g_queue_get_length (component_id_queue),
+               g_queue_get_length (old_component_queue));
+
+       for (ii = 0; ii < length; ii++) {
+               ECalComponentId *component_id;
+               ECalComponent *old_component;
+               ECalComponent *new_component = NULL;
+
+               component_id = g_queue_pop_head (component_id_queue);
+               old_component = g_queue_pop_head (old_component_queue);
+               if (new_component_queue != NULL)
+                       new_component = g_queue_pop_head (new_component_queue);
+
+               e_cal_backend_notify_component_removed (
+                       backend, component_id, old_component, new_component);
+
+               e_cal_component_free_id (component_id);
+               g_clear_object (&old_component);
+               g_clear_object (&new_component);
+       }
+
+       g_warn_if_fail (g_queue_is_empty (component_id_queue));
+       g_queue_free (component_id_queue);
+
+       g_warn_if_fail (g_queue_is_empty (old_component_queue));
+       g_queue_free (old_component_queue);
+
+       if (new_component_queue != NULL) {
+               g_warn_if_fail (g_queue_is_empty (new_component_queue));
+               g_queue_free (new_component_queue);
+       }
+
+       return TRUE;
+}
+
+/**
+ * e_cal_backend_receive_objects_sync:
+ * @backend: an #ECalBackend
+ * @calobj: an iCalendar string
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Receives the set of iCalendar objects specified by @calobj.  This is used
+ * for iTIP confirmation and cancellation messages for scheduled meetings.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_receive_objects_sync (ECalBackend *backend,
+                                    const gchar *calobj,
+                                    GCancellable *cancellable,
+                                    GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+       g_return_val_if_fail (calobj != NULL, FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_receive_objects (
+               backend, calobj, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_cal_backend_receive_objects_finish (
+               backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_cal_backend_receive_objects() */
+static void
+cal_backend_receive_objects_thread (GSimpleAsyncResult *simple,
+                                    GObject *source_object,
+                                    GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+       AsyncContext *async_context;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->receive_objects != NULL);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_cal_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       NotOpened,
+                       "%s", e_data_cal_status_to_string (
+                       NotOpened));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = cal_backend_stash_operation (backend, simple);
+
+               class->receive_objects (
+                       backend, data_cal, opid, cancellable,
+                       async_context->calobj);
+       }
+
+       g_object_unref (data_cal);
+}
+
+/**
+ * e_cal_backend_receive_objects:
+ * @backend: an #ECalBackend
+ * @calobj: an iCalendar string
+ * @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
+ *
+ * Asynchronously receives the set of iCalendar objects specified by
+ * @calobj.  This is used for iTIP confirmation and cancellation messages
+ * for scheduled meetings.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_cal_backend_receive_objects_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_cal_backend_receive_objects (ECalBackend *backend,
+                               const gchar *calobj,
+                               GCancellable *cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (calobj != NULL);
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->calobj = g_strdup (calobj);
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_cal_backend_receive_objects);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               cal_backend_receive_objects_thread);
+
+       cal_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_cal_backend_receive_objects_finish:
+ * @backend: an #ECalBackend
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_cal_backend_receive_objects().
+ *
+ * If an error occurred, the function will set @error and erturn %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_receive_objects_finish (ECalBackend *backend,
+                                      GAsyncResult *result,
+                                      GError **error)
+{
+       GSimpleAsyncResult *simple;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_receive_objects), FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+
+       /* Assume success unless a GError is set. */
+       return !g_simple_async_result_propagate_error (simple, error);
+}
+
+/**
+ * e_cal_backend_send_objects_sync:
+ * @backend: an #ECalBackend
+ * @calobj: an iCalendar string
+ * @out_users: a #GQueue in which to deposit results
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Sends meeting information in @calobj.  The @backend may modify @calobj
+ * and send meeting information only to particular users.  The function
+ * returns the sent #ECalComponent and deposits the list of users the
+ * meeting information was sent to in @out_users.
+ *
+ * The returned #ECalComponent is referenced for thread-safety and must
+ * be unrefenced with g_object_unref() when finished with it.
+ *
+ * If an error occurs, the function will set @error and return %NULL.
+ *
+ * Returns: an #ECalComponent, or %NULL
+ *
+ * Since: 3.10
+ **/
+ECalComponent *
+e_cal_backend_send_objects_sync (ECalBackend *backend,
+                                 const gchar *calobj,
+                                 GQueue *out_users,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       ECalComponent *component;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
+       g_return_val_if_fail (calobj != NULL, NULL);
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_send_objects (
+               backend, calobj, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       component = e_cal_backend_send_objects_finish (
+               backend, result, out_users, error);
+
+       e_async_closure_free (closure);
+
+       return component;
+}
+
+/* Helper for e_cal_backend_send_objects() */
+static void
+cal_backend_send_objects_thread (GSimpleAsyncResult *simple,
+                                 GObject *source_object,
+                                 GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+       AsyncContext *async_context;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->send_objects != NULL);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_cal_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       NotOpened,
+                       "%s", e_data_cal_status_to_string (
+                       NotOpened));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = cal_backend_stash_operation (backend, simple);
+
+               class->send_objects (
+                       backend, data_cal, opid, cancellable,
+                       async_context->calobj);
+       }
+
+       g_object_unref (data_cal);
+}
+
+/**
+ * e_cal_backend_send_objects:
+ * @backend: an #ECalBackend
+ * @calobj: an iCalendar string
+ * @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
+ *
+ * Asynchronously sends meeting information in @calobj.  The @backend may
+ * modify @calobj and send meeting information only to particular users.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_cal_backend_send_objects_finish() to get the result of the operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_cal_backend_send_objects (ECalBackend *backend,
+                            const gchar *calobj,
+                            GCancellable *cancellable,
+                            GAsyncReadyCallback callback,
+                            gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (calobj != NULL);
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->calobj = g_strdup (calobj);
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_cal_backend_send_objects);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               cal_backend_send_objects_thread);
+
+       cal_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_cal_backend_send_objects_finish:
+ * @backend: an #ECalBackend
+ * @result: a #GAsyncResult
+ * @out_users: a #GQueue in which to deposit results
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_cal_backend_send_objects().
+ *
+ * The function returns the sent #ECalComponent and deposits the list of
+ * users the meeting information was sent to in @out_users.
+ *
+ * The returned #ECalComponent is referenced for thread-safety and must
+ * be unreferenced with g_object_unref() when finished with it.
+ *
+ * If an error occurs, the function will set @error and return %NULL.
+ *
+ * Returns: an #ECalComponent, or %NULL
+ *
+ * Since: 3.10
+ **/
+ECalComponent *
+e_cal_backend_send_objects_finish (ECalBackend *backend,
+                                   GAsyncResult *result,
+                                   GQueue *out_users,
+                                   GError **error)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       ECalComponent *component;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_send_objects), NULL);
+       g_return_val_if_fail (out_users != NULL, NULL);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (g_simple_async_result_propagate_error (simple, error))
+               return NULL;
+
+       component = g_queue_pop_head (&async_context->result_queue);
+       g_return_val_if_fail (E_IS_CAL_COMPONENT (component), NULL);
+
+       e_queue_transfer (&async_context->result_queue, out_users);
+
+       return component;
+}
+
+/**
+ * e_cal_backend_get_attachment_uris_sync:
+ * @backend: an #ECalBackend
+ * @uid: a unique ID for an iCalendar object
+ * @rid: a recurrence ID, or %NULL
+ * @out_attachment_uris: a #GQueue in which to deposit results
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Inspects the iCalendar object specified by @uid and, optionally, @rid
+ * for attachments and deposits a URI string for each attachment in
+ * @out_attachment_uris.  Free the returned strings with g_free() when
+ * finished with them.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ * Note that an empty result set does not necessarily imply an error.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_get_attachment_uris_sync (ECalBackend *backend,
+                                        const gchar *uid,
+                                        const gchar *rid,
+                                        GQueue *out_attachment_uris,
+                                        GCancellable *cancellable,
+                                        GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+       /* rid can be NULL */
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_get_attachment_uris (
+               backend, uid, rid, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_cal_backend_get_attachment_uris_finish (
+               backend, result, out_attachment_uris, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_cal_backend_get_attachment_uris() */
+static void
+cal_backend_get_attachment_uris_thread (GSimpleAsyncResult *simple,
+                                        GObject *source_object,
+                                        GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+       AsyncContext *async_context;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->get_attachment_uris != NULL);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_cal_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       NotOpened,
+                       "%s", e_data_cal_status_to_string (
+                       NotOpened));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = cal_backend_stash_operation (backend, simple);
+
+               class->get_attachment_uris (
+                       backend, data_cal, opid, cancellable,
+                       async_context->uid,
+                       async_context->rid);
+       }
+
+       g_object_unref (data_cal);
+}
+
+/**
+ * e_cal_backend_get_attachment_uris:
+ * @backend: an #ECalBackend
+ * @uid: a unique ID for an iCalendar object
+ * @rid: a recurrence ID, or %NULL
+ * @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
+ *
+ * Asynchronously inspects the iCalendar object specified by @uid and,
+ * optionally, @rid for attachments.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_cal_backend_get_attachment_uris_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_cal_backend_get_attachment_uris (ECalBackend *backend,
+                                   const gchar *uid,
+                                   const gchar *rid,
+                                   GCancellable *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (uid != NULL);
+       /* rid is optional */
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->uid = g_strdup (uid);
+       async_context->rid = g_strdup (rid);
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_cal_backend_get_attachment_uris);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               cal_backend_get_attachment_uris_thread);
+
+       cal_backend_dispatch_next_operation (backend);
 
-       if (!e_cal_backend_is_opened (backend))
-               e_data_cal_respond_get_free_busy (cal, opid, EDC_NOT_OPENED_ERROR);
-       else
-               (* E_CAL_BACKEND_GET_CLASS (backend)->get_free_busy) (backend, cal, opid, cancellable, users, 
start, end);
+       g_object_unref (simple);
 }
 
 /**
- * e_cal_backend_create_objects:
+ * e_cal_backend_get_attachment_uris_finish:
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @calobjs: The objects to create (list of gchar *).
+ * @result: a #GAsyncResult
+ * @out_attachment_uris: a #GQueue in which to deposit results
+ * @error: return location for a #GError, or %NULL
  *
- * Calls the create_object method on the given backend.
- * This might be finished with e_data_cal_respond_create_objects().
+ * Finishes the operation started with e_cal_backend_get_attachment_uris().
  *
- * Since: 3.6
+ * The requested attachment URI strings are deposited in @out_attachment_uris.
+ * Free the returned strings with g_free() when finished with them.
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ * Note that an empty result set does not necessarily imply an error.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
  **/
-void
-e_cal_backend_create_objects (ECalBackend *backend,
-                              EDataCal *cal,
-                              guint32 opid,
-                              GCancellable *cancellable,
-                              const GSList *calobjs)
+gboolean
+e_cal_backend_get_attachment_uris_finish (ECalBackend *backend,
+                                          GAsyncResult *result,
+                                          GQueue *out_attachment_uris,
+                                          GError **error)
 {
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (calobjs != NULL);
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_get_attachment_uris), FALSE);
+       g_return_val_if_fail (out_attachment_uris != NULL, FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (g_simple_async_result_propagate_error (simple, error))
+               return FALSE;
 
-       if (!E_CAL_BACKEND_GET_CLASS (backend)->create_objects)
-               e_data_cal_respond_create_objects (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
-       else if (!e_cal_backend_is_opened (backend))
-               e_data_cal_respond_create_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
-       else
-               (* E_CAL_BACKEND_GET_CLASS (backend)->create_objects) (backend, cal, opid, cancellable, 
calobjs);
+       e_queue_transfer (&async_context->result_queue, out_attachment_uris);
+
+       return TRUE;
 }
 
 /**
- * e_cal_backend_modify_objects:
+ * e_cal_backend_discard_alarm_sync:
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @calobjs: Objects to be modified (list of gchar *).
- * @mod: Type of modification.
+ * @uid: a unique ID for an iCalendar object
+ * @rid: a recurrence ID, or %NULL
+ * @alarm_uid: a unique ID for an iCalendar VALARM object
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
  *
- * Calls the modify_objects method on the given backend.
- * This might be finished with e_data_cal_respond_modify_objects().
+ * Discards the VALARM object with a unique ID of @alarm_uid from the
+ * iCalendar object identified by @uid and, optionally, @rid.
  *
- * Since: 3.6
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
  **/
-void
-e_cal_backend_modify_objects (ECalBackend *backend,
-                              EDataCal *cal,
-                              guint32 opid,
-                              GCancellable *cancellable,
-                              const GSList *calobjs,
-                              ECalObjModType mod)
+gboolean
+e_cal_backend_discard_alarm_sync (ECalBackend *backend,
+                                  const gchar *uid,
+                                  const gchar *rid,
+                                  const gchar *alarm_uid,
+                                  GCancellable *cancellable,
+                                  GError **error)
 {
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (calobjs != NULL);
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+       /* rid can be NULL */
+       g_return_val_if_fail (alarm_uid != NULL, FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_discard_alarm (
+               backend, uid, rid, alarm_uid, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_cal_backend_discard_alarm_finish (
+               backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_cal_backend_discard_alarm() */
+static void
+cal_backend_discard_alarm_thread (GSimpleAsyncResult *simple,
+                                  GObject *source_object,
+                                  GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+       AsyncContext *async_context;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (class->discard_alarm == NULL) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       NotSupported,
+                       "%s", e_data_cal_status_to_string (
+                       NotSupported));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else if (!e_cal_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       NotOpened,
+                       "%s", e_data_cal_status_to_string (
+                       NotOpened));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = cal_backend_stash_operation (backend, simple);
+
+               class->discard_alarm (
+                       backend, data_cal, opid, cancellable,
+                       async_context->uid,
+                       async_context->rid,
+                       async_context->alarm_uid);
+       }
 
-       if (!E_CAL_BACKEND_GET_CLASS (backend)->modify_objects)
-               e_data_cal_respond_modify_objects (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
-       else if (!e_cal_backend_is_opened (backend))
-               e_data_cal_respond_modify_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
-       else
-               (* E_CAL_BACKEND_GET_CLASS (backend)->modify_objects) (backend, cal, opid, cancellable, 
calobjs, mod);
+       g_object_unref (data_cal);
 }
 
 /**
- * e_cal_backend_remove_objects:
+ * e_cal_backend_discard_alarm:
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @ids: List of #ECalComponentId objects identifying the objects to remove
- * @mod: Type of removal.
+ * @uid: a unique ID for an iCalendar object
+ * @rid: a recurrence ID, or %NULL
+ * @alarm_uid: a unique ID for an iCalendar VALARM object
+ * @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
  *
- * Removes objects in a calendar backend.  The backend will notify all of its
- * clients about the change.
- * This might be finished with e_data_cal_respond_remove_objects().
+ * Asynchronously discards the VALARM object with a unique ID of @alarm_uid
+ * from the iCalendar object identified by @uid and, optionally, @rid.
  *
- * Since: 3.6
+ * When the operation is finished, @callback will be called.  You can
+ * then call e_cal_backend_discard_alarm_finish() to get the result of
+ * the operation.
+ *
+ * Since: 3.10
  **/
 void
-e_cal_backend_remove_objects (ECalBackend *backend,
-                              EDataCal *cal,
-                              guint32 opid,
-                              GCancellable *cancellable,
-                              const GSList *ids,
-                              ECalObjModType mod)
+e_cal_backend_discard_alarm (ECalBackend *backend,
+                             const gchar *uid,
+                             const gchar *rid,
+                             const gchar *alarm_uid,
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
 {
-       g_return_if_fail (backend != NULL);
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (ids != NULL);
-       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->remove_objects != NULL);
+       g_return_if_fail (uid != NULL);
+       /* rid can be NULL */
+       g_return_if_fail (alarm_uid != NULL);
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->uid = g_strdup (uid);
+       async_context->rid = g_strdup (rid);
+       async_context->alarm_uid = g_strdup (alarm_uid);
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_cal_backend_discard_alarm);
 
-       if (!e_cal_backend_is_opened (backend))
-               e_data_cal_respond_remove_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL, NULL);
-       else
-               (* E_CAL_BACKEND_GET_CLASS (backend)->remove_objects) (backend, cal, opid, cancellable, ids, 
mod);
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               cal_backend_discard_alarm_thread);
+
+       cal_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
 }
 
 /**
- * e_cal_backend_receive_objects:
+ * e_cal_backend_discard_alarm_finish:
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @calobj: iCalendar object.
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_cal_backend_discard_alarm().
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
  *
- * Calls the receive_objects method on the given backend.
- * This might be finished with e_data_cal_respond_receive_objects().
+ * Since: 3.10
  **/
-void
-e_cal_backend_receive_objects (ECalBackend *backend,
-                               EDataCal *cal,
-                               guint32 opid,
-                               GCancellable *cancellable,
-                               const gchar *calobj)
+gboolean
+e_cal_backend_discard_alarm_finish (ECalBackend *backend,
+                                    GAsyncResult *result,
+                                    GError **error)
 {
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (calobj != NULL);
-       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->receive_objects != NULL);
+       GSimpleAsyncResult *simple;
 
-       if (!e_cal_backend_is_opened (backend))
-               e_data_cal_respond_receive_objects (cal, opid, EDC_NOT_OPENED_ERROR);
-       else
-               (* E_CAL_BACKEND_GET_CLASS (backend)->receive_objects) (backend, cal, opid, cancellable, 
calobj);
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_discard_alarm), FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+
+       /* Assume success unless a GError is set. */
+       return !g_simple_async_result_propagate_error (simple, error);
 }
 
 /**
- * e_cal_backend_send_objects:
+ * e_cal_backend_get_timezone_sync:
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @calobj: iCalendar object to be sent.
+ * @tzid: a unique ID for an iCalendar VTIMEZONE object
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Obtains the VTIMEZONE object identified by @tzid.  Free the returned
+ * string with g_free() when finished with it.
  *
- * Calls the send_objects method on the given backend.
- * This might be finished with e_data_cal_respond_send_objects().
+ * If an error occurs, the function will set @error and return %NULL.
+ *
+ * Returns: an iCalendar string, or %NULL
+ *
+ * Since: 3.10
  **/
-void
-e_cal_backend_send_objects (ECalBackend *backend,
-                            EDataCal *cal,
-                            guint32 opid,
-                            GCancellable *cancellable,
-                            const gchar *calobj)
+gchar *
+e_cal_backend_get_timezone_sync (ECalBackend *backend,
+                                 const gchar *tzid,
+                                 GCancellable *cancellable,
+                                 GError **error)
 {
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (calobj != NULL);
-       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->send_objects != NULL);
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gchar *tzobject;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
+       g_return_val_if_fail (tzid != NULL, NULL);
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_get_timezone (
+               backend, tzid, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       tzobject = e_cal_backend_get_timezone_finish (
+               backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return tzobject;
+}
+
+/* Helper for e_cal_backend_get_timezone() */
+static void
+cal_backend_get_timezone_thread (GSimpleAsyncResult *simple,
+                                 GObject *source_object,
+                                 GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+       AsyncContext *async_context;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->get_timezone != NULL);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
 
-       if (!e_cal_backend_is_opened (backend))
-               e_data_cal_respond_send_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
-       else
-               (* E_CAL_BACKEND_GET_CLASS (backend)->send_objects) (backend, cal, opid, cancellable, calobj);
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_cal_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       NotOpened,
+                       "%s", e_data_cal_status_to_string (
+                       NotOpened));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = cal_backend_stash_operation (backend, simple);
+
+               class->get_timezone (
+                       backend, data_cal, opid, cancellable,
+                       async_context->tzid);
+       }
+
+       g_object_unref (data_cal);
 }
 
 /**
- * e_cal_backend_get_attachment_uris:
+ * e_cal_backend_get_timezone:
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @uid: Unique identifier for a calendar object.
- * @rid: ID for the object's recurrence to get.
+ * @tzid: a unique ID for an iCalendar VTIMEZONE object
+ * @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
  *
- * Queries a calendar backend for attachments present in a calendar object based
- * on its unique identifier and its recurrence ID (if a recurrent appointment).
- * This might be finished with e_data_cal_respond_get_attachment_uris().
+ * Asynchronously obtains the VTIMEZONE object identified by @tzid.
  *
- * Since: 3.2
+ * When the operation is finished, @callback will be called.  You can
+ * then call e_cal_backend_get_timezone_finish() to get the result of
+ * the operation.
+ *
+ * Since: 3.10
  **/
 void
-e_cal_backend_get_attachment_uris (ECalBackend *backend,
-                                   EDataCal *cal,
-                                   guint32 opid,
-                                   GCancellable *cancellable,
-                                   const gchar *uid,
-                                   const gchar *rid)
+e_cal_backend_get_timezone (ECalBackend *backend,
+                            const gchar *tzid,
+                            GCancellable *cancellable,
+                            GAsyncReadyCallback callback,
+                            gpointer user_data)
 {
-       g_return_if_fail (backend != NULL);
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (uid != NULL);
-       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_attachment_uris != NULL);
+       g_return_if_fail (tzid != NULL);
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->tzid = g_strdup (tzid);
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_cal_backend_get_timezone);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
 
-       if (!e_cal_backend_is_opened (backend))
-               e_data_cal_respond_get_attachment_uris (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
-       else
-               (* E_CAL_BACKEND_GET_CLASS (backend)->get_attachment_uris) (backend, cal, opid, cancellable, 
uid, rid);
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               cal_backend_get_timezone_thread);
+
+       cal_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
 }
 
 /**
- * e_cal_backend_discard_alarm:
+ * e_cal_backend_get_timezone_finish:
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @uid: Unique identifier for a calendar object.
- * @rid: ID for the object's recurrence to discard alarm in.
- * @auid: Unique identifier of the alarm itself.
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_cal_backend_get_timezone().
+ *
+ * Free the returned string with g_free() when finished with it.
+ *
+ * If an error occurred, the function will set @error and return %NULL.
+ *
+ * Returns: an iCalendar string, or %NULL
  *
- * Discards alarm @auid from the object identified by @uid and @rid.
- * This might be finished with e_data_cal_respond_discard_alarm().
- * Default implementation of this method returns Not Supported error.
+ * Since: 3.10
  **/
-void
-e_cal_backend_discard_alarm (ECalBackend *backend,
-                             EDataCal *cal,
-                             guint32 opid,
-                             GCancellable *cancellable,
-                             const gchar *uid,
-                             const gchar *rid,
-                             const gchar *auid)
+gchar *
+e_cal_backend_get_timezone_finish (ECalBackend *backend,
+                                   GAsyncResult *result,
+                                   GError **error)
 {
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (uid != NULL);
-       g_return_if_fail (auid != NULL);
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       gchar *tzobject;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_get_timezone), FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
 
-       if (!E_CAL_BACKEND_GET_CLASS (backend)->discard_alarm)
-               e_data_cal_respond_discard_alarm (cal, opid, e_data_cal_create_error (NotSupported, NULL));
-       else if (!e_cal_backend_is_opened (backend))
-               e_data_cal_respond_discard_alarm (cal, opid, EDC_NOT_OPENED_ERROR);
-       else
-               (* E_CAL_BACKEND_GET_CLASS (backend)->discard_alarm) (backend, cal, opid, cancellable, uid, 
rid, auid);
+       if (g_simple_async_result_propagate_error (simple, error))
+               return FALSE;
+
+       tzobject = g_queue_pop_head (&async_context->result_queue);
+       g_return_val_if_fail (tzobject != NULL, NULL);
+
+       g_warn_if_fail (g_queue_is_empty (&async_context->result_queue));
+
+       return tzobject;
 }
 
 /**
- * e_cal_backend_get_timezone:
+ * e_cal_backend_add_timezone_sync:
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @tzid: Unique identifier of a VTIMEZONE object. Note that this must not be
- * NULL.
+ * @tzobject: an iCalendar VTIMEZONE string
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
  *
- * Returns the icaltimezone* corresponding to the TZID, or NULL if the TZID
- * can't be found.
- * This might be finished with e_data_cal_respond_get_timezone().
+ * Adds the timezone described by @tzobject to @backend.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
  **/
-void
-e_cal_backend_get_timezone (ECalBackend *backend,
-                            EDataCal *cal,
-                            guint32 opid,
-                            GCancellable *cancellable,
-                            const gchar *tzid)
+gboolean
+e_cal_backend_add_timezone_sync (ECalBackend *backend,
+                                 const gchar *tzobject,
+                                 GCancellable *cancellable,
+                                 GError **error)
 {
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (tzid != NULL);
-       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_timezone != NULL);
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+       g_return_val_if_fail (tzobject != NULL, FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_cal_backend_add_timezone (
+               backend, tzobject, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
 
-       if (!e_cal_backend_is_opened (backend))
-               e_data_cal_respond_get_timezone (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
-       else
-               (* E_CAL_BACKEND_GET_CLASS (backend)->get_timezone) (backend, cal, opid, cancellable, tzid);
+       success = e_cal_backend_add_timezone_finish (
+               backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_cal_backend_add_timezone() */
+static void
+cal_backend_add_timezone_thread (GSimpleAsyncResult *simple,
+                                 GObject *source_object,
+                                 GCancellable *cancellable)
+{
+       ECalBackend *backend;
+       ECalBackendClass *class;
+       EDataCal *data_cal;
+       AsyncContext *async_context;
+
+       backend = E_CAL_BACKEND (source_object);
+
+       class = E_CAL_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->add_timezone != NULL);
+
+       data_cal = e_cal_backend_ref_data_cal (backend);
+       g_return_if_fail (data_cal != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_cal_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_CAL_ERROR,
+                       NotOpened,
+                       "%s", e_data_cal_status_to_string (
+                       NotOpened));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = cal_backend_stash_operation (backend, simple);
+
+               class->add_timezone (
+                       backend, data_cal, opid, cancellable,
+                       async_context->tzobject);
+       }
+
+       g_object_unref (data_cal);
 }
 
 /**
  * e_cal_backend_add_timezone
  * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @tzobject: The timezone object, in a string.
+ * @tzobject: an iCalendar VTIMEZONE string
+ * @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
  *
- * Add a timezone object to the given backend.
- * This might be finished with e_data_cal_respond_add_timezone().
+ * Asynchronously adds the timezone described by @tzobject to @backend.
+ *
+ * When the operation is finished, @callback will be called.  You can
+ * then call e_cal_backend_add_timezone_finish() to get the result of
+ * the operation.
+ *
+ * Since: 3.10
  **/
 void
 e_cal_backend_add_timezone (ECalBackend *backend,
-                            EDataCal *cal,
-                            guint32 opid,
+                            const gchar *tzobject,
                             GCancellable *cancellable,
-                            const gchar *tzobject)
+                            GAsyncReadyCallback callback,
+                            gpointer user_data)
 {
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
        g_return_if_fail (tzobject != NULL);
-       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->add_timezone != NULL);
 
-       if (!e_cal_backend_is_opened (backend))
-               e_data_cal_respond_add_timezone (cal, opid, EDC_NOT_OPENED_ERROR);
-       else
-               (* E_CAL_BACKEND_GET_CLASS (backend)->add_timezone) (backend, cal, opid, cancellable, 
tzobject);
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->tzobject = g_strdup (tzobject);
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_cal_backend_add_timezone);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       cal_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               cal_backend_add_timezone_thread);
+
+       cal_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_cal_backend_add_timezone_finish:
+ * @backend: an #ECalBackend
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_cal_backend_add_timezone().
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_cal_backend_add_timezone_finish (ECalBackend *backend,
+                                   GAsyncResult *result,
+                                   GError **error)
+{
+       GSimpleAsyncResult *simple;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_cal_backend_add_timezone), FALSE);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+
+       /* Assume success unless a GError is set. */
+       return !g_simple_async_result_propagate_error (simple, error);
 }
 
 /**
@@ -1852,3 +4433,51 @@ e_cal_backend_empty_cache (ECalBackend *backend,
 
        e_file_cache_thaw_changes (E_FILE_CACHE (cache));
 }
+
+/**
+ * e_cal_backend_prepare_for_completion:
+ * @backend: an #ECalBackend
+ * @opid: an operation ID given to #EDataCal
+ * @result_queue: return location for a #GQueue, or %NULL
+ *
+ * Obtains the #GSimpleAsyncResult for @opid and sets @result_queue as a
+ * place to deposit results prior to completing the #GSimpleAsyncResult.
+ *
+ * <note>
+ *   <para>
+ *     This is a temporary function to serve #EDataCal's "respond"
+ *     functions until they can be removed.  Nothing else should be
+ *     calling this function.
+ *   </para>
+ * </note>
+ *
+ * Returns: (transfer full): a #GSimpleAsyncResult
+ *
+ * Since: 3.10
+ **/
+GSimpleAsyncResult *
+e_cal_backend_prepare_for_completion (ECalBackend *backend,
+                                      guint32 opid,
+                                      GQueue **result_queue)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
+       g_return_val_if_fail (opid > 0, NULL);
+
+       simple = cal_backend_claim_operation (backend, opid);
+       g_return_val_if_fail (simple != NULL, NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (result_queue != NULL) {
+               if (async_context != NULL)
+                       *result_queue = &async_context->result_queue;
+               else
+                       *result_queue = NULL;
+       }
+
+       return simple;
+}
+
diff --git a/calendar/libedata-cal/e-cal-backend.h b/calendar/libedata-cal/e-cal-backend.h
index 358b4c2..7779f58 100644
--- a/calendar/libedata-cal/e-cal-backend.h
+++ b/calendar/libedata-cal/e-cal-backend.h
@@ -1,23 +1,19 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* Evolution calendar - generic backend class
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- * Authors: Federico Mena-Quintero <federico ximian com>
- *          Rodrigo Moya <rodrigo ximian com>
+/*
+ * e-cal-backend.h
  *
  * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU Lesser General Public
- * License as published by the Free Software Foundation.
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
  *
  * This program 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.
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
  *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
 #if !defined (__LIBEDATA_CAL_H_INSIDE__) && !defined (LIBEDATA_CAL_COMPILATION)
@@ -275,91 +271,229 @@ void             e_cal_backend_remove_view       (ECalBackend *backend,
                                                 EDataCalView *view);
 GList *                e_cal_backend_list_views        (ECalBackend *backend);
 
+gchar *                e_cal_backend_get_backend_property_sync
+                                               (ECalBackend *backend,
+                                                const gchar *prop_name,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_cal_backend_get_backend_property
                                                (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
+                                                const gchar *prop_name,
                                                 GCancellable *cancellable,
-                                                const gchar *prop_name);
-
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gchar *                e_cal_backend_get_backend_property_finish
+                                               (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+gboolean       e_cal_backend_open_sync         (ECalBackend *backend,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_cal_backend_open              (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
                                                 GCancellable *cancellable,
-                                                gboolean only_if_exists);
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_cal_backend_open_finish       (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+gboolean       e_cal_backend_refresh_sync      (ECalBackend *backend,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_cal_backend_refresh           (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
-                                                GCancellable *cancellable);
-void           e_cal_backend_get_object        (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
                                                 GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_cal_backend_refresh_finish    (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+ECalComponent *        e_cal_backend_get_object_sync   (ECalBackend *backend,
                                                 const gchar *uid,
-                                                const gchar *rid);
+                                                const gchar *rid,
+                                                GCancellable *cancellable,
+                                                GError **error);
+void           e_cal_backend_get_object        (ECalBackend *backend,
+                                                const gchar *uid,
+                                                const gchar *rid,
+                                                GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+ECalComponent *        e_cal_backend_get_object_finish (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+gboolean       e_cal_backend_get_object_list_sync
+                                               (ECalBackend *backend,
+                                                const gchar *query,
+                                                GQueue *out_objects,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_cal_backend_get_object_list   (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
+                                                const gchar *query,
                                                 GCancellable *cancellable,
-                                                const gchar *sexp);
-void           e_cal_backend_get_free_busy     (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_cal_backend_get_object_list_finish
+                                               (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GQueue *out_objects,
+                                                GError **error);
+gboolean       e_cal_backend_get_free_busy_sync
+                                               (ECalBackend *backend,
+                                                time_t start,
+                                                time_t end,
+                                                const gchar * const *users,
                                                 GCancellable *cancellable,
-                                                const GSList *users,
+                                                GError **error);
+void           e_cal_backend_get_free_busy     (ECalBackend *backend,
                                                 time_t start,
-                                                time_t end);
+                                                time_t end,
+                                                const gchar * const *users,
+                                                GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_cal_backend_get_free_busy_finish
+                                               (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+gboolean       e_cal_backend_create_objects_sync
+                                               (ECalBackend *backend,
+                                                const gchar * const *calobjs,
+                                                GQueue *out_uids,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_cal_backend_create_objects    (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
+                                                const gchar * const *calobjs,
                                                 GCancellable *cancellable,
-                                                const GSList *calobjs);
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_cal_backend_create_objects_finish
+                                               (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GQueue *out_uids,
+                                                GError **error);
+gboolean       e_cal_backend_modify_objects_sync
+                                               (ECalBackend *backend,
+                                                const gchar * const *calobjs,
+                                                ECalObjModType mod,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_cal_backend_modify_objects    (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
+                                                const gchar * const *calobjs,
+                                                ECalObjModType mod,
                                                 GCancellable *cancellable,
-                                                const GSList *calobjs,
-                                                ECalObjModType mod);
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_cal_backend_modify_objects_finish
+                                               (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+gboolean       e_cal_backend_remove_objects_sync
+                                               (ECalBackend *backend,
+                                                GList *component_ids,
+                                                ECalObjModType mod,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_cal_backend_remove_objects    (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
+                                                GList *component_ids,
+                                                ECalObjModType mod,
                                                 GCancellable *cancellable,
-                                                const GSList *ids,
-                                                ECalObjModType mod);
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_cal_backend_remove_objects_finish
+                                               (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+gboolean       e_cal_backend_receive_objects_sync
+                                               (ECalBackend *backend,
+                                                const gchar *calobj,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_cal_backend_receive_objects   (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
+                                                const gchar *calobj,
                                                 GCancellable *cancellable,
-                                                const gchar *calobj);
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_cal_backend_receive_objects_finish
+                                               (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+ECalComponent *        e_cal_backend_send_objects_sync (ECalBackend *backend,
+                                                const gchar *calobj,
+                                                GQueue *out_users,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_cal_backend_send_objects      (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
+                                                const gchar *calobj,
                                                 GCancellable *cancellable,
-                                                const gchar *calobj);
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+ECalComponent *        e_cal_backend_send_objects_finish
+                                               (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GQueue *out_users,
+                                                GError **error);
+gboolean       e_cal_backend_get_attachment_uris_sync
+                                               (ECalBackend *backend,
+                                                const gchar *uid,
+                                                const gchar *rid,
+                                                GQueue *out_attachment_uris,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_cal_backend_get_attachment_uris
                                                (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
+                                                const gchar *uid,
+                                                const gchar *rid,
                                                 GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_cal_backend_get_attachment_uris_finish
+                                               (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GQueue *out_attachment_uris,
+                                                GError **error);
+gboolean       e_cal_backend_discard_alarm_sync
+                                               (ECalBackend *backend,
                                                 const gchar *uid,
-                                                const gchar *rid);
-void           e_cal_backend_discard_alarm     (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
+                                                const gchar *rid,
+                                                const gchar *alarm_uid,
                                                 GCancellable *cancellable,
+                                                GError **error);
+void           e_cal_backend_discard_alarm     (ECalBackend *backend,
                                                 const gchar *uid,
                                                 const gchar *rid,
-                                                const gchar *auid);
+                                                const gchar *alarm_uid,
+                                                GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_cal_backend_discard_alarm_finish
+                                               (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+gchar *                e_cal_backend_get_timezone_sync (ECalBackend *backend,
+                                                const gchar *tzid,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_cal_backend_get_timezone      (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
+                                                const gchar *tzid,
                                                 GCancellable *cancellable,
-                                                const gchar *tzid);
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gchar *                e_cal_backend_get_timezone_finish
+                                               (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+gboolean       e_cal_backend_add_timezone_sync (ECalBackend *backend,
+                                                const gchar *tzobject,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_cal_backend_add_timezone      (ECalBackend *backend,
-                                                EDataCal *cal,
-                                                guint32 opid,
+                                                const gchar *tzobject,
                                                 GCancellable *cancellable,
-                                                const gchar *tzobject);
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_cal_backend_add_timezone_finish
+                                               (ECalBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
 void           e_cal_backend_start_view        (ECalBackend *backend,
                                                 EDataCalView *view);
 void           e_cal_backend_stop_view         (ECalBackend *backend,
@@ -392,6 +526,12 @@ void               e_cal_backend_empty_cache       (ECalBackend *backend,
 void           e_cal_backend_set_is_removed    (ECalBackend *backend,
                                                 gboolean is_removed);
 
+GSimpleAsyncResult *
+               e_cal_backend_prepare_for_completion
+                                               (ECalBackend *backend,
+                                                guint opid,
+                                                GQueue **result_queue);
+
 G_END_DECLS
 
 #endif /* E_CAL_BACKEND_H */
diff --git a/calendar/libedata-cal/e-data-cal.c b/calendar/libedata-cal/e-data-cal.c
index 7b79e7a..a570c7f 100644
--- a/calendar/libedata-cal/e-data-cal.c
+++ b/calendar/libedata-cal/e-data-cal.c
@@ -1,25 +1,19 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* Evolution calendar client interface object
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- * Copyright (C) 2009 Intel Corporation
- *
- * Authors: Federico Mena-Quintero <federico ximian com>
- *          Rodrigo Moya <rodrigo ximian com>
- *          Ross Burton <ross linux intel com>
+/*
+ * e-data-cal.c
  *
  * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU Lesser General Public
- * License as published by the Free Software Foundation.
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
  *
  * This program 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.
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
  *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -46,6 +40,8 @@
 #define EDC_ERROR(_code) e_data_cal_create_error (_code, NULL)
 #define EDC_ERROR_EX(_code, _msg) e_data_cal_create_error (_code, _msg)
 
+typedef struct _AsyncContext AsyncContext;
+
 struct _EDataCalPrivate {
        GDBusConnection *connection;
        EDBusCalendar *dbus_interface;
@@ -54,14 +50,16 @@ struct _EDataCalPrivate {
 
        gboolean opened;
 
-       GRecMutex pending_ops_lock;
-       GHashTable *pending_ops; /* opid -> OperationData */
+       GMutex sender_lock;
+       GHashTable *sender_table;
+};
 
-       /* Operations are queued while an
-        * open operation is in progress. */
-       GMutex open_lock;
-       guint32 open_opid;
-       GQueue open_queue;
+struct _AsyncContext {
+       EDataCal *data_cal;
+       EDBusCalendar *interface;
+       GDBusMethodInvocation *invocation;
+       GCancellable *cancellable;
+       guint watcher_id;
 };
 
 enum {
@@ -71,90 +69,6 @@ enum {
        PROP_OBJECT_PATH
 };
 
-static EOperationPool *ops_pool = NULL;
-
-typedef enum {
-       OP_OPEN,
-       OP_REFRESH,
-       OP_GET_BACKEND_PROPERTY,
-       OP_GET_OBJECT,
-       OP_GET_OBJECT_LIST,
-       OP_GET_FREE_BUSY,
-       OP_CREATE_OBJECTS,
-       OP_MODIFY_OBJECTS,
-       OP_REMOVE_OBJECTS,
-       OP_RECEIVE_OBJECTS,
-       OP_SEND_OBJECTS,
-       OP_GET_ATTACHMENT_URIS,
-       OP_DISCARD_ALARM,
-       OP_GET_VIEW,
-       OP_GET_TIMEZONE,
-       OP_ADD_TIMEZONE,
-       OP_CLOSE
-} OperationID;
-
-typedef struct {
-       volatile gint ref_count;
-
-       OperationID op;
-       guint32 id; /* operation id */
-       EDataCal *cal;
-       ECalBackend *backend;
-       GCancellable *cancellable;
-       GDBusMethodInvocation *invocation;
-       const gchar *sender; /* owned by invocation */
-       guint watcher_id;
-
-       union {
-               /* OP_GET_OBJECT */
-               /* OP_GET_ATTACHMENT_URIS */
-               struct _ur {
-                       gchar *uid;
-                       gchar *rid;
-               } ur;
-               /* OP_DISCARD_ALARM */
-               struct _ura {
-                       gchar *uid;
-                       gchar *rid;
-                       gchar *auid;
-               } ura;
-               /* OP_GET_OBJECT_LIST */
-               /* OP_GET_VIEW */
-               gchar *sexp;
-               /* OP_GET_FREE_BUSY */
-               struct _free_busy {
-                       time_t start, end;
-                       GSList *users;
-               } fb;
-               /* OP_CREATE_OBJECTS */
-               GSList *calobjs;
-               /* OP_RECEIVE_OBJECTS */
-               /* OP_SEND_OBJECTS */
-               struct _co {
-                       gchar *calobj;
-               } co;
-               /* OP_MODIFY_OBJECTS */
-               struct _mo {
-                       GSList *calobjs;
-                       ECalObjModType mod;
-               } mo;
-               /* OP_REMOVE_OBJECTS */
-               struct _ro {
-                       GSList *ids;
-                       ECalObjModType mod;
-               } ro;
-               /* OP_GET_TIMEZONE */
-               gchar *tzid;
-               /* OP_ADD_TIMEZONE */
-               gchar *tzobject;
-               /* OP_GET_BACKEND_PROPERTY */
-               const gchar *prop_name;
-
-               /* OP_REFRESH */
-               /* OP_CLOSE */
-       } d;
-} OperationData;
-
 /* Forward Declarations */
 static void    e_data_cal_initable_init        (GInitableIface *interface);
 
@@ -166,205 +80,131 @@ G_DEFINE_TYPE_WITH_CODE (
                G_TYPE_INITABLE,
                e_data_cal_initable_init))
 
-/* Function to get a new EDataCalView path, used by get_view below */
-static gchar *
-construct_calview_path (void)
-{
-       static guint counter = 1;
-       return g_strdup_printf ("/org/gnome/evolution/dataserver/CalendarView/%d/%d", getpid (), counter++);
-}
-
-static OperationData *
-op_ref (OperationData *data)
-{
-       g_return_val_if_fail (data != NULL, data);
-       g_return_val_if_fail (data->ref_count > 0, data);
-
-       g_atomic_int_inc (&data->ref_count);
-
-       return data;
-}
-
 static void
-op_sender_vanished_cb (GDBusConnection *connection,
-                       const gchar *sender,
-                       GCancellable *cancellable)
+sender_vanished_cb (GDBusConnection *connection,
+                    const gchar *sender,
+                    GCancellable *cancellable)
 {
        g_cancellable_cancel (cancellable);
 }
 
-static OperationData *
-op_new (OperationID op,
-        EDataCal *cal,
-        ECalBackend *backend,
-        GDBusMethodInvocation *invocation)
+static void
+sender_table_insert (EDataCal *data_cal,
+                     const gchar *sender,
+                     GCancellable *cancellable)
 {
-       OperationData *data;
-
-       data = g_slice_new0 (OperationData);
-       data->ref_count = 1;
-       data->op = op;
-       data->id = e_operation_pool_reserve_opid (ops_pool);
-       data->cal = g_object_ref (cal);
-       data->backend = g_object_ref (backend);
-       data->cancellable = g_cancellable_new ();
+       GHashTable *sender_table;
+       GPtrArray *array;
 
-       /* This is optional so we can fake client requests. */
-       if (invocation != NULL) {
-               GDBusConnection *connection;
+       g_return_if_fail (sender != NULL);
 
-               data->invocation = g_object_ref (invocation);
-               data->sender = g_dbus_method_invocation_get_sender (invocation);
+       g_mutex_lock (&data_cal->priv->sender_lock);
 
-               connection = e_data_cal_get_connection (cal);
+       sender_table = data_cal->priv->sender_table;
+       array = g_hash_table_lookup (sender_table, sender);
 
-               data->watcher_id = g_bus_watch_name_on_connection (
-                       connection, data->sender,
-                       G_BUS_NAME_WATCHER_FLAGS_NONE,
-                       (GBusNameAppearedCallback) NULL,
-                       (GBusNameVanishedCallback) op_sender_vanished_cb,
-                       g_object_ref (data->cancellable),
+       if (array == NULL) {
+               array = g_ptr_array_new_with_free_func (
                        (GDestroyNotify) g_object_unref);
+               g_hash_table_insert (
+                       sender_table, g_strdup (sender), array);
        }
 
-       g_rec_mutex_lock (&cal->priv->pending_ops_lock);
-       g_hash_table_insert (
-               cal->priv->pending_ops,
-               GUINT_TO_POINTER (data->id),
-               op_ref (data));
-       g_rec_mutex_unlock (&cal->priv->pending_ops_lock);
+       g_ptr_array_add (array, g_object_ref (cancellable));
 
-       return data;
+       g_mutex_unlock (&data_cal->priv->sender_lock);
 }
 
-static void
-op_unref (OperationData *data)
-{
-       g_return_if_fail (data != NULL);
-       g_return_if_fail (data->ref_count > 0);
-
-       if (g_atomic_int_dec_and_test (&data->ref_count)) {
-
-               switch (data->op) {
-                       case OP_GET_OBJECT:
-                       case OP_GET_ATTACHMENT_URIS:
-                               g_free (data->d.ur.uid);
-                               g_free (data->d.ur.rid);
-                               break;
-                       case OP_DISCARD_ALARM:
-                               g_free (data->d.ura.uid);
-                               g_free (data->d.ura.rid);
-                               g_free (data->d.ura.auid);
-                               break;
-                       case OP_GET_OBJECT_LIST:
-                       case OP_GET_VIEW:
-                               g_free (data->d.sexp);
-                               break;
-                       case OP_GET_FREE_BUSY:
-                               g_slist_free_full (
-                                       data->d.fb.users,
-                                       (GDestroyNotify) g_free);
-                               break;
-                       case OP_CREATE_OBJECTS:
-                               g_slist_free_full (
-                                       data->d.calobjs,
-                                       (GDestroyNotify) g_free);
-                               break;
-                       case OP_RECEIVE_OBJECTS:
-                       case OP_SEND_OBJECTS:
-                               g_free (data->d.co.calobj);
-                               break;
-                       case OP_MODIFY_OBJECTS:
-                               g_slist_free_full (
-                                       data->d.mo.calobjs,
-                                       (GDestroyNotify) g_free);
-                               break;
-                       case OP_REMOVE_OBJECTS:
-                               g_slist_free_full (
-                                       data->d.ro.ids, (GDestroyNotify)
-                                       e_cal_component_free_id);
-                               break;
-                       case OP_GET_TIMEZONE:
-                               g_free (data->d.tzid);
-                               break;
-                       case OP_ADD_TIMEZONE:
-                               g_free (data->d.tzobject);
-                               break;
-                       default:
-                               break;
-               }
+static gboolean
+sender_table_remove (EDataCal *data_cal,
+                     const gchar *sender,
+                     GCancellable *cancellable)
+{
+       GHashTable *sender_table;
+       GPtrArray *array;
+       gboolean removed = FALSE;
 
-               g_object_unref (data->cal);
-               g_object_unref (data->backend);
-               g_object_unref (data->cancellable);
+       g_return_val_if_fail (sender != NULL, FALSE);
 
-               if (data->invocation != NULL)
-                       g_object_unref (data->invocation);
+       g_mutex_lock (&data_cal->priv->sender_lock);
 
-               if (data->watcher_id > 0)
-                       g_bus_unwatch_name (data->watcher_id);
+       sender_table = data_cal->priv->sender_table;
+       array = g_hash_table_lookup (sender_table, sender);
 
-               g_slice_free (OperationData, data);
+       if (array != NULL) {
+               removed = g_ptr_array_remove_fast (array, cancellable);
+
+               if (array->len == 0)
+                       g_hash_table_remove (sender_table, sender);
        }
+
+       g_mutex_unlock (&data_cal->priv->sender_lock);
+
+       return removed;
 }
 
-static void
-op_dispatch (EDataCal *cal,
-             OperationData *data)
+static AsyncContext *
+async_context_new (EDataCal *data_cal,
+                   GDBusMethodInvocation *invocation)
 {
-       g_mutex_lock (&cal->priv->open_lock);
+       AsyncContext *async_context;
+       EDBusCalendar *interface;
 
-       /* If an open operation is currently in progress, queue this
-        * operation to be dispatched when the open operation finishes. */
-       if (cal->priv->open_opid > 0) {
-               g_queue_push_tail (&cal->priv->open_queue, data);
-       } else {
-               if (data->op == OP_OPEN)
-                       cal->priv->open_opid = data->id;
-               e_operation_pool_push (ops_pool, data);
-       }
+       interface = data_cal->priv->dbus_interface;
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->data_cal = g_object_ref (data_cal);
+       async_context->interface = g_object_ref (interface);
+       async_context->invocation = g_object_ref (invocation);
+       async_context->cancellable = g_cancellable_new ();
+
+       async_context->watcher_id = g_bus_watch_name_on_connection (
+               g_dbus_method_invocation_get_connection (invocation),
+               g_dbus_method_invocation_get_sender (invocation),
+               G_BUS_NAME_WATCHER_FLAGS_NONE,
+               (GBusNameAppearedCallback) NULL,
+               (GBusNameVanishedCallback) sender_vanished_cb,
+               g_object_ref (async_context->cancellable),
+               (GDestroyNotify) g_object_ref);
+
+       sender_table_insert (
+               async_context->data_cal,
+               g_dbus_method_invocation_get_sender (invocation),
+               async_context->cancellable);
 
-       g_mutex_unlock (&cal->priv->open_lock);
+       return async_context;
 }
 
-static OperationData *
-op_claim (EDataCal *cal,
-          guint32 opid)
+static void
+async_context_free (AsyncContext *async_context)
 {
-       OperationData *data;
+       sender_table_remove (
+               async_context->data_cal,
+               g_dbus_method_invocation_get_sender (
+                       async_context->invocation),
+               async_context->cancellable);
 
-       g_return_val_if_fail (E_IS_DATA_CAL (cal), NULL);
+       g_clear_object (&async_context->data_cal);
+       g_clear_object (&async_context->interface);
+       g_clear_object (&async_context->invocation);
+       g_clear_object (&async_context->cancellable);
 
-       e_operation_pool_release_opid (ops_pool, opid);
-
-       g_rec_mutex_lock (&cal->priv->pending_ops_lock);
-       data = g_hash_table_lookup (
-               cal->priv->pending_ops,
-               GUINT_TO_POINTER (opid));
-       if (data != NULL) {
-               /* Steal the hash table's reference. */
-               g_hash_table_steal (
-                       cal->priv->pending_ops,
-                       GUINT_TO_POINTER (opid));
-       }
-       g_rec_mutex_unlock (&cal->priv->pending_ops_lock);
+       if (async_context->watcher_id > 0)
+               g_bus_unwatch_name (async_context->watcher_id);
 
-       return data;
+       g_slice_free (AsyncContext, async_context);
 }
 
-static void
-op_complete (EDataCal *cal,
-             guint32 opid)
+static gchar *
+construct_calview_path (void)
 {
-       g_return_if_fail (E_IS_DATA_CAL (cal));
+       static volatile gint counter = 1;
 
-       e_operation_pool_release_opid (ops_pool, opid);
+       g_atomic_int_inc (&counter);
 
-       g_rec_mutex_lock (&cal->priv->pending_ops_lock);
-       g_hash_table_remove (
-               cal->priv->pending_ops,
-               GUINT_TO_POINTER (opid));
-       g_rec_mutex_unlock (&cal->priv->pending_ops_lock);
+       return g_strdup_printf (
+               "/org/gnome/evolution/dataserver/CalendarView/%d/%d",
+               getpid (), counter);
 }
 
 static void
@@ -494,218 +334,6 @@ data_cal_convert_to_client_error (GError *error)
        }
 }
 
-static void
-cancel_operations_for_sender (EDataCal *cal,
-                              const gchar *sender)
-{
-       GHashTableIter iter;
-       gpointer value;
-
-       g_return_if_fail (sender != NULL);
-
-       g_rec_mutex_lock (&cal->priv->pending_ops_lock);
-
-       g_hash_table_iter_init (&iter, cal->priv->pending_ops);
-       while (g_hash_table_iter_next (&iter, NULL, &value)) {
-               OperationData *op = value;
-
-               if (op->sender != NULL && g_str_equal (sender, op->sender))
-                       g_cancellable_cancel (op->cancellable);
-       }
-
-       g_rec_mutex_unlock (&cal->priv->pending_ops_lock);
-}
-
-static void
-operation_thread (gpointer data,
-                  gpointer user_data)
-{
-       OperationData *op = data;
-
-       switch (op->op) {
-       case OP_OPEN:
-               e_cal_backend_open (
-                       op->backend, op->cal, op->id,
-                       op->cancellable, FALSE);
-               break;
-
-       case OP_REFRESH:
-               e_cal_backend_refresh (
-                       op->backend, op->cal, op->id, op->cancellable);
-               break;
-
-       case OP_GET_BACKEND_PROPERTY:
-               e_cal_backend_get_backend_property (
-                       op->backend, op->cal, op->id,
-                       op->cancellable, op->d.prop_name);
-               break;
-
-       case OP_GET_OBJECT:
-               e_cal_backend_get_object (
-                       op->backend, op->cal, op->id,
-                       op->cancellable,
-                       op->d.ur.uid,
-                       op->d.ur.rid && *op->d.ur.rid ? op->d.ur.rid : NULL);
-               break;
-
-       case OP_GET_OBJECT_LIST:
-               e_cal_backend_get_object_list (
-                       op->backend, op->cal, op->id,
-                       op->cancellable, op->d.sexp);
-               break;
-
-       case OP_GET_FREE_BUSY:
-               e_cal_backend_get_free_busy (
-                       op->backend, op->cal, op->id,
-                       op->cancellable,
-                       op->d.fb.users,
-                       op->d.fb.start,
-                       op->d.fb.end);
-               break;
-
-       case OP_CREATE_OBJECTS:
-               e_cal_backend_create_objects (
-                       op->backend, op->cal, op->id,
-                       op->cancellable, op->d.calobjs);
-               break;
-
-       case OP_MODIFY_OBJECTS:
-               e_cal_backend_modify_objects (
-                       op->backend, op->cal, op->id,
-                       op->cancellable,
-                       op->d.mo.calobjs,
-                       op->d.mo.mod);
-               break;
-
-       case OP_REMOVE_OBJECTS:
-               e_cal_backend_remove_objects (
-                       op->backend, op->cal, op->id,
-                       op->cancellable,
-                       op->d.ro.ids,
-                       op->d.ro.mod);
-               break;
-
-       case OP_RECEIVE_OBJECTS:
-               e_cal_backend_receive_objects (
-                       op->backend, op->cal, op->id,
-                       op->cancellable, op->d.co.calobj);
-               break;
-
-       case OP_SEND_OBJECTS:
-               e_cal_backend_send_objects (
-                       op->backend, op->cal, op->id,
-                       op->cancellable, op->d.co.calobj);
-               break;
-
-       case OP_GET_ATTACHMENT_URIS:
-               e_cal_backend_get_attachment_uris (
-                       op->backend, op->cal, op->id,
-                       op->cancellable,
-                       op->d.ur.uid,
-                       op->d.ur.rid && *op->d.ur.rid ? op->d.ur.rid : NULL);
-               break;
-
-       case OP_DISCARD_ALARM:
-               e_cal_backend_discard_alarm (
-                       op->backend, op->cal, op->id,
-                       op->cancellable,
-                       op->d.ura.uid,
-                       op->d.ura.rid && *op->d.ura.rid ? op->d.ura.rid : NULL,
-                       op->d.ura.auid);
-               break;
-
-       case OP_GET_VIEW:
-               if (op->d.sexp) {
-                       EDataCalView *view;
-                       ECalBackendSExp *obj_sexp;
-                       GDBusConnection *connection;
-                       gchar *object_path;
-                       GError *error = NULL;
-
-                       /* we handle this entirely here, since it doesn't require any
-                        * backend involvement now that we have e_cal_view_start to
-                        * actually kick off the search. */
-
-                       obj_sexp = e_cal_backend_sexp_new (op->d.sexp);
-                       if (!obj_sexp) {
-                               g_dbus_method_invocation_return_error_literal (
-                                       op->invocation,
-                                       E_CLIENT_ERROR,
-                                       E_CLIENT_ERROR_INVALID_QUERY,
-                                       _("Invalid query"));
-
-                               op_complete (op->cal, op->id);
-                               break;
-                       }
-
-                       object_path = construct_calview_path ();
-                       connection = e_data_cal_get_connection (op->cal);
-
-                       view = e_data_cal_view_new (
-                               op->backend, obj_sexp,
-                               connection, object_path, &error);
-
-                       g_object_unref (obj_sexp);
-
-                       /* Sanity check. */
-                       g_return_if_fail (
-                               ((view != NULL) && (error == NULL)) ||
-                               ((view == NULL) && (error != NULL)));
-
-                       if (error != NULL) {
-                               /* Translators: This is a prefix to a detailed error message *
- */
-                               g_prefix_error (&error, "%s", _("Invalid query: "));
-                               data_cal_convert_to_client_error (error);
-                               g_dbus_method_invocation_take_error (
-                                       op->invocation, error);
-
-                               op_complete (op->cal, op->id);
-                               g_free (object_path);
-                               break;
-                       }
-
-                       e_cal_backend_add_view (op->backend, view);
-
-                       e_dbus_calendar_complete_get_view (
-                               op->cal->priv->dbus_interface,
-                               op->invocation,
-                               object_path);
-
-                       op_complete (op->cal, op->id);
-
-                       g_object_unref (view);
-                       g_free (object_path);
-               }
-               break;
-
-       case OP_GET_TIMEZONE:
-               e_cal_backend_get_timezone (
-                       op->backend, op->cal, op->id,
-                       op->cancellable, op->d.tzid);
-               break;
-
-       case OP_ADD_TIMEZONE:
-               e_cal_backend_add_timezone (
-                       op->backend, op->cal, op->id,
-                       op->cancellable, op->d.tzobject);
-               break;
-
-       case OP_CLOSE:
-               if (op->sender != NULL)
-                       cancel_operations_for_sender (op->cal, op->sender);
-
-               e_dbus_calendar_complete_close (
-                       op->cal->priv->dbus_interface,
-                       op->invocation);
-
-               op_complete (op->cal, op->id);
-               break;
-       }
-
-       op_unref (op);
-}
-
 /* Create the EDataCal error quark */
 GQuark
 e_data_cal_error_quark (void)
@@ -855,171 +483,399 @@ e_data_cal_create_error_fmt (EDataCalCallStatus status,
        return error;
 }
 
+static void
+data_cal_complete_open_cb (GObject *source_object,
+                           GAsyncResult *result,
+                           gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GError *error = NULL;
+
+       e_cal_backend_open_finish (
+               E_CAL_BACKEND (source_object), result, &error);
+
+       if (error == NULL) {
+               e_dbus_calendar_complete_open (
+                       async_context->interface,
+                       async_context->invocation);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
+}
+
 static gboolean
 data_cal_handle_open_cb (EDBusCalendar *interface,
                          GDBusMethodInvocation *invocation,
-                         EDataCal *cal)
+                         EDataCal *data_cal)
 {
-       OperationData *op;
        ECalBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_cal_ref_backend (cal);
+       backend = e_data_cal_ref_backend (data_cal);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_OPEN, cal, backend, invocation);
+       async_context = async_context_new (data_cal, invocation);
 
-       op_dispatch (cal, op);
+       e_cal_backend_open (
+               backend,
+               async_context->cancellable,
+               data_cal_complete_open_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_cal_complete_refresh_cb (GObject *source_object,
+                              GAsyncResult *result,
+                              gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GError *error = NULL;
+
+       e_cal_backend_refresh_finish (
+               E_CAL_BACKEND (source_object), result, &error);
+
+       if (error == NULL) {
+               e_dbus_calendar_complete_refresh (
+                       async_context->interface,
+                       async_context->invocation);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
+}
+
 static gboolean
 data_cal_handle_refresh_cb (EDBusCalendar *interface,
                             GDBusMethodInvocation *invocation,
-                            EDataCal *cal)
+                            EDataCal *data_cal)
 {
-       OperationData *op;
        ECalBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_cal_ref_backend (cal);
+       backend = e_data_cal_ref_backend (data_cal);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_REFRESH, cal, backend, invocation);
+       async_context = async_context_new (data_cal, invocation);
 
-       op_dispatch (cal, op);
+       e_cal_backend_refresh (
+               backend,
+               async_context->cancellable,
+               data_cal_complete_refresh_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_cal_complete_get_object_cb (GObject *source_object,
+                                 GAsyncResult *result,
+                                 gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       ECalComponent *component;
+       GError *error = NULL;
+
+       component = e_cal_backend_get_object_finish (
+               E_CAL_BACKEND (source_object), result, &error);
+
+       /* Sanity check. */
+       g_return_if_fail (
+               ((component != NULL) && (error == NULL)) ||
+               ((component == NULL) && (error != NULL)));
+
+       if (error == NULL) {
+               gchar *string;
+               gchar *utf8_string;
+
+               string = e_cal_component_get_as_string (component);
+               utf8_string = e_util_utf8_make_valid (string);
+
+               e_dbus_calendar_complete_get_object (
+                       async_context->interface,
+                       async_context->invocation,
+                       utf8_string);
+
+               g_free (utf8_string);
+               g_free (string);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
+}
+
 static gboolean
 data_cal_handle_get_object_cb (EDBusCalendar *interface,
                                GDBusMethodInvocation *invocation,
                                const gchar *in_uid,
                                const gchar *in_rid,
-                               EDataCal *cal)
+                               EDataCal *data_cal)
 {
-       OperationData *op;
        ECalBackend *backend;
-
-       backend = e_data_cal_ref_backend (cal);
-       g_return_val_if_fail (backend != NULL, FALSE);
+       AsyncContext *async_context;
 
        /* Recurrence ID is optional.  Its omission is denoted
         * via D-Bus by an emptry string.  Convert it to NULL. */
        if (in_rid != NULL && *in_rid == '\0')
                in_rid = NULL;
 
-       op = op_new (OP_GET_OBJECT, cal, backend, invocation);
-       op->d.ur.uid = g_strdup (in_uid);
-       op->d.ur.rid = g_strdup (in_rid);
+       backend = e_data_cal_ref_backend (data_cal);
+       g_return_val_if_fail (backend != NULL, FALSE);
+
+       async_context = async_context_new (data_cal, invocation);
 
-       op_dispatch (cal, op);
+       e_cal_backend_get_object (
+               backend,
+               in_uid, in_rid,
+               async_context->cancellable,
+               data_cal_complete_get_object_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_cal_complete_get_object_list_cb (GObject *source_object,
+                                      GAsyncResult *result,
+                                      gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GQueue queue = G_QUEUE_INIT;
+       GError *error = NULL;
+
+       e_cal_backend_get_object_list_finish (
+               E_CAL_BACKEND (source_object), result, &queue, &error);
+
+       if (error == NULL) {
+               gchar **strv;
+               gint ii = 0;
+
+               strv = g_new0 (gchar *, queue.length + 1);
+
+               while (!g_queue_is_empty (&queue)) {
+                       ECalComponent *component;
+                       gchar *string;
+
+                       component = g_queue_pop_head (&queue);
+
+                       string = e_cal_component_get_as_string (component);
+                       strv[ii++] = e_util_utf8_make_valid (string);
+                       g_free (string);
+
+                       g_object_unref (component);
+               }
+
+               e_dbus_calendar_complete_get_object_list (
+                       async_context->interface,
+                       async_context->invocation,
+                       (const gchar * const *) strv);
+
+               g_strfreev (strv);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
+}
+
 static gboolean
 data_cal_handle_get_object_list_cb (EDBusCalendar *interface,
                                     GDBusMethodInvocation *invocation,
-                                    const gchar *in_sexp,
-                                    EDataCal *cal)
+                                    const gchar *in_query,
+                                    EDataCal *data_cal)
 {
-       OperationData *op;
        ECalBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_cal_ref_backend (cal);
+       backend = e_data_cal_ref_backend (data_cal);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_GET_OBJECT_LIST, cal, backend, invocation);
-       op->d.sexp = g_strdup (in_sexp);
+       async_context = async_context_new (data_cal, invocation);
 
-       op_dispatch (cal, op);
+       e_cal_backend_get_object_list (
+               backend,
+               in_query,
+               async_context->cancellable,
+               data_cal_complete_get_object_list_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_cal_complete_get_free_busy_cb (GObject *source_object,
+                                    GAsyncResult *result,
+                                    gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GError *error = NULL;
+
+       e_cal_backend_get_free_busy_finish (
+               E_CAL_BACKEND (source_object), result, &error);
+
+       if (error == NULL) {
+               e_dbus_calendar_complete_get_free_busy (
+                       async_context->interface,
+                       async_context->invocation);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
+}
+
 static gboolean
 data_cal_handle_get_free_busy_cb (EDBusCalendar *interface,
                                   GDBusMethodInvocation *invocation,
                                   gint64 in_start,
                                   gint64 in_end,
-                                  const gchar **in_users,
-                                  EDataCal *cal)
+                                  const gchar * const *in_users,
+                                  EDataCal *data_cal)
 {
-       OperationData *op;
        ECalBackend *backend;
-       GSList *tmp = NULL;
-       gint ii;
+       AsyncContext *async_context;
 
-       backend = e_data_cal_ref_backend (cal);
+       backend = e_data_cal_ref_backend (data_cal);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       for (ii = 0; in_users[ii] != NULL; ii++)
-               tmp = g_slist_prepend (tmp, g_strdup (in_users[ii]));
+       async_context = async_context_new (data_cal, invocation);
 
-       op = op_new (OP_GET_FREE_BUSY, cal, backend, invocation);
-       op->d.fb.start = (time_t) in_start;
-       op->d.fb.end = (time_t) in_end;
-       op->d.fb.users = g_slist_reverse (tmp);
-
-       op_dispatch (cal, op);
+       e_cal_backend_get_free_busy (
+               backend,
+               (time_t) in_start,
+               (time_t) in_end,
+               in_users,
+               async_context->cancellable,
+               data_cal_complete_get_free_busy_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_cal_complete_create_objects_cb (GObject *source_object,
+                                     GAsyncResult *result,
+                                     gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GQueue queue = G_QUEUE_INIT;
+       GError *error = NULL;
+
+       e_cal_backend_create_objects_finish (
+               E_CAL_BACKEND (source_object), result, &queue, &error);
+
+       if (error == NULL) {
+               gchar **strv;
+               gint ii = 0;
+
+               strv = g_new0 (gchar *, queue.length + 1);
+
+               while (!g_queue_is_empty (&queue)) {
+                       gchar *uid;
+
+                       uid = g_queue_pop_head (&queue);
+                       strv[ii++] = e_util_utf8_make_valid (uid);
+                       g_free (uid);
+               }
+
+               e_dbus_calendar_complete_create_objects (
+                       async_context->interface,
+                       async_context->invocation,
+                       (const gchar * const *) strv);
+
+               g_strfreev (strv);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
+}
+
 static gboolean
 data_cal_handle_create_objects_cb (EDBusCalendar *interface,
                                    GDBusMethodInvocation *invocation,
-                                   const gchar **in_calobjs,
-                                   EDataCal *cal)
+                                   const gchar * const *in_calobjs,
+                                   EDataCal *data_cal)
 {
-       OperationData *op;
        ECalBackend *backend;
-       GSList *tmp = NULL;
-       gint ii;
+       AsyncContext *async_context;
 
-       backend = e_data_cal_ref_backend (cal);
+       backend = e_data_cal_ref_backend (data_cal);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       for (ii = 0; in_calobjs[ii] != NULL; ii++)
-               tmp = g_slist_prepend (tmp, g_strdup (in_calobjs[ii]));
+       async_context = async_context_new (data_cal, invocation);
 
-       op = op_new (OP_CREATE_OBJECTS, cal, backend, invocation);
-       op->d.calobjs = g_slist_reverse (tmp);
-
-       op_dispatch (cal, op);
+       e_cal_backend_create_objects (
+               backend,
+               in_calobjs,
+               async_context->cancellable,
+               data_cal_complete_create_objects_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_cal_complete_modify_objects_cb (GObject *source_object,
+                                     GAsyncResult *result,
+                                     gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GError *error = NULL;
+
+       e_cal_backend_modify_objects_finish (
+               E_CAL_BACKEND (source_object), result, &error);
+
+       if (error == NULL) {
+               e_dbus_calendar_complete_modify_objects (
+                       async_context->interface,
+                       async_context->invocation);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
+}
+
 static gboolean
 data_cal_handle_modify_objects_cb (EDBusCalendar *interface,
                                    GDBusMethodInvocation *invocation,
-                                   const gchar **in_ics_objects,
+                                   const gchar * const *in_ics_objects,
                                    const gchar *in_mod_type,
-                                   EDataCal *cal)
+                                   EDataCal *data_cal)
 {
+       ECalBackend *backend;
+       AsyncContext *async_context;
        GFlagsClass *flags_class;
        ECalObjModType mod = 0;
-       OperationData *op;
-       ECalBackend *backend;
-       GSList *tmp = NULL;
        gchar **flags_strv;
        gint ii;
 
-       backend = e_data_cal_ref_backend (cal);
+       backend = e_data_cal_ref_backend (data_cal);
        g_return_val_if_fail (backend != NULL, FALSE);
 
        flags_class = g_type_class_ref (E_TYPE_CAL_OBJ_MOD_TYPE);
@@ -1040,36 +896,59 @@ data_cal_handle_modify_objects_cb (EDBusCalendar *interface,
        g_strfreev (flags_strv);
        g_type_class_unref (flags_class);
 
-       for (ii = 0; in_ics_objects[ii] != NULL; ii++)
-               tmp = g_slist_prepend (tmp, g_strdup (in_ics_objects[ii]));
+       async_context = async_context_new (data_cal, invocation);
 
-       op = op_new (OP_MODIFY_OBJECTS, cal, backend, invocation);
-       op->d.mo.calobjs = g_slist_reverse (tmp);
-       op->d.mo.mod = mod;
-
-       op_dispatch (cal, op);
+       e_cal_backend_modify_objects (
+               backend,
+               in_ics_objects, mod,
+               async_context->cancellable,
+               data_cal_complete_modify_objects_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_cal_complete_remove_objects_cb (GObject *source_object,
+                                     GAsyncResult *result,
+                                     gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GError *error = NULL;
+
+       e_cal_backend_remove_objects_finish (
+               E_CAL_BACKEND (source_object), result, &error);
+
+       if (error == NULL) {
+               e_dbus_calendar_complete_remove_objects (
+                       async_context->interface,
+                       async_context->invocation);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
+}
+
 static gboolean
 data_cal_handle_remove_objects_cb (EDBusCalendar *interface,
                                    GDBusMethodInvocation *invocation,
                                    GVariant *in_uid_rid_array,
                                    const gchar *in_mod_type,
-                                   EDataCal *cal)
+                                   EDataCal *data_cal)
 {
+       ECalBackend *backend;
+       AsyncContext *async_context;
        GFlagsClass *flags_class;
        ECalObjModType mod = 0;
-       OperationData *op;
-       ECalBackend *backend;
-       GSList *tmp = NULL;
+       GQueue component_ids = G_QUEUE_INIT;
        gchar **flags_strv;
        gsize n_children, ii;
 
-       backend = e_data_cal_ref_backend (cal);
+       backend = e_data_cal_ref_backend (data_cal);
        g_return_val_if_fail (backend != NULL, FALSE);
 
        flags_class = g_type_class_ref (E_TYPE_CAL_OBJ_MOD_TYPE);
@@ -1114,91 +993,249 @@ data_cal_handle_remove_objects_cb (EDBusCalendar *interface,
                        id->rid = NULL;
                }
 
-               tmp = g_slist_prepend (tmp, id);
+               g_queue_push_tail (&component_ids, id);
        }
 
-       op = op_new (OP_REMOVE_OBJECTS, cal, backend, invocation);
-       op->d.ro.ids = g_slist_reverse (tmp);
-       op->d.ro.mod = mod;
+       async_context = async_context_new (data_cal, invocation);
+
+       e_cal_backend_remove_objects (
+               backend,
+               component_ids.head, mod,
+               async_context->cancellable,
+               data_cal_complete_remove_objects_cb,
+               async_context);
 
-       op_dispatch (cal, op);
+       while (!g_queue_is_empty (&component_ids))
+               e_cal_component_free_id (g_queue_pop_head (&component_ids));
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_cal_complete_receive_objects_cb (GObject *source_object,
+                                      GAsyncResult *result,
+                                      gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GError *error = NULL;
+
+       e_cal_backend_receive_objects_finish (
+               E_CAL_BACKEND (source_object), result, &error);
+
+       if (error == NULL) {
+               e_dbus_calendar_complete_receive_objects (
+                       async_context->interface,
+                       async_context->invocation);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
+}
+
 static gboolean
 data_cal_handle_receive_objects_cb (EDBusCalendar *interface,
                                     GDBusMethodInvocation *invocation,
                                     const gchar *in_calobj,
-                                    EDataCal *cal)
+                                    EDataCal *data_cal)
 {
-       OperationData *op;
        ECalBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_cal_ref_backend (cal);
+       backend = e_data_cal_ref_backend (data_cal);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_RECEIVE_OBJECTS, cal, backend, invocation);
-       op->d.co.calobj = g_strdup (in_calobj);
+       async_context = async_context_new (data_cal, invocation);
 
-       op_dispatch (cal, op);
+       e_cal_backend_receive_objects (
+               backend,
+               in_calobj,
+               async_context->cancellable,
+               data_cal_complete_receive_objects_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_cal_complete_send_objects_cb (GObject *source_object,
+                                   GAsyncResult *result,
+                                   gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       ECalComponent *component;
+       GQueue queue = G_QUEUE_INIT;
+       GError *error = NULL;
+
+       component = e_cal_backend_send_objects_finish (
+               E_CAL_BACKEND (source_object), result, &queue, &error);
+
+       /* Sanity check. */
+       g_return_if_fail (
+               ((component != NULL) && (error == NULL)) ||
+               ((component == NULL) && (error != NULL)));
+
+       if (component != NULL) {
+               gchar **strv;
+               gchar *string;
+               gchar *utf8_string;
+               gint ii = 0;
+
+               strv = g_new0 (gchar *, queue.length + 1);
+
+               while (!g_queue_is_empty (&queue)) {
+                       gchar *user;
+
+                       user = g_queue_pop_head (&queue);
+                       strv[ii++] = e_util_utf8_make_valid (user);
+                       g_free (user);
+               }
+
+               string = e_cal_component_get_as_string (component);
+               utf8_string = e_util_utf8_make_valid (string);
+
+               e_dbus_calendar_complete_send_objects (
+                       async_context->interface,
+                       async_context->invocation,
+                       (const gchar * const *) strv,
+                       utf8_string);
+
+               g_free (utf8_string);
+               g_free (string);
+
+               g_strfreev (strv);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
+}
+
 static gboolean
 data_cal_handle_send_objects_cb (EDBusCalendar *interface,
                                  GDBusMethodInvocation *invocation,
                                  const gchar *in_calobj,
-                                 EDataCal *cal)
+                                 EDataCal *data_cal)
 {
-       OperationData *op;
        ECalBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_cal_ref_backend (cal);
+       backend = e_data_cal_ref_backend (data_cal);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_SEND_OBJECTS, cal, backend, invocation);
-       op->d.co.calobj = g_strdup (in_calobj);
+       async_context = async_context_new (data_cal, invocation);
 
-       op_dispatch (cal, op);
+       e_cal_backend_send_objects (
+               backend,
+               in_calobj,
+               async_context->cancellable,
+               data_cal_complete_send_objects_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_cal_complete_get_attachment_uris_cb (GObject *source_object,
+                                          GAsyncResult *result,
+                                          gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GQueue queue = G_QUEUE_INIT;
+       GError *error = NULL;
+
+       e_cal_backend_get_attachment_uris_finish (
+               E_CAL_BACKEND (source_object), result, &queue, &error);
+
+       if (error == NULL) {
+               gchar **strv;
+               gint ii = 0;
+
+               strv = g_new0 (gchar *, queue.length + 1);
+
+               while (!g_queue_is_empty (&queue)) {
+                       gchar *uri;
+
+                       uri = g_queue_pop_head (&queue);
+                       strv[ii++] = e_util_utf8_make_valid (uri);
+                       g_free (uri);
+               }
+
+               e_dbus_calendar_complete_get_attachment_uris (
+                       async_context->interface,
+                       async_context->invocation,
+                       (const gchar * const *) strv);
+
+               g_strfreev (strv);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
+}
+
 static gboolean
 data_cal_handle_get_attachment_uris_cb (EDBusCalendar *interface,
                                         GDBusMethodInvocation *invocation,
                                         const gchar *in_uid,
                                         const gchar *in_rid,
-                                        EDataCal *cal)
+                                        EDataCal *data_cal)
 {
-       OperationData *op;
        ECalBackend *backend;
-
-       backend = e_data_cal_ref_backend (cal);
-       g_return_val_if_fail (backend != NULL, FALSE);
+       AsyncContext *async_context;
 
        /* Recurrence ID is optional.  Its omission is denoted
         * via D-Bus by an empty string.  Convert it to NULL. */
        if (in_rid != NULL && *in_rid == '\0')
                in_rid = NULL;
 
-       op = op_new (OP_GET_ATTACHMENT_URIS, cal, backend, invocation);
-       op->d.ur.uid = g_strdup (in_uid);
-       op->d.ur.rid = g_strdup (in_rid);
+       backend = e_data_cal_ref_backend (data_cal);
+       g_return_val_if_fail (backend != NULL, FALSE);
+
+       async_context = async_context_new (data_cal, invocation);
 
-       op_dispatch (cal, op);
+       e_cal_backend_get_attachment_uris (
+               backend,
+               in_uid, in_rid,
+               async_context->cancellable,
+               data_cal_complete_get_attachment_uris_cb,
+               async_context);
 
        g_object_unref (backend);
 
-       return TRUE;
+       return TRUE;
+}
+
+static void
+data_cal_complete_discard_alarm_cb (GObject *source_object,
+                                    GAsyncResult *result,
+                                    gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GError *error = NULL;
+
+       e_cal_backend_discard_alarm_finish (
+               E_CAL_BACKEND (source_object), result, &error);
+
+       if (error == NULL) {
+               e_dbus_calendar_complete_discard_alarm (
+                       async_context->interface,
+                       async_context->invocation);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
 }
 
 static gboolean
@@ -1207,25 +1244,27 @@ data_cal_handle_discard_alarm_cb (EDBusCalendar *interface,
                                   const gchar *in_uid,
                                   const gchar *in_rid,
                                   const gchar *in_alarm_uid,
-                                  EDataCal *cal)
+                                  EDataCal *data_cal)
 {
-       OperationData *op;
        ECalBackend *backend;
-
-       backend = e_data_cal_ref_backend (cal);
-       g_return_val_if_fail (backend != NULL, FALSE);
+       AsyncContext *async_context;
 
        /* Recurrence ID is optional.  Its omission is denoted
         * via D-Bus by an empty string.  Convert it to NULL. */
        if (in_rid != NULL && *in_rid == '\0')
                in_rid = NULL;
 
-       op = op_new (OP_DISCARD_ALARM, cal, backend, invocation);
-       op->d.ura.uid = g_strdup (in_uid);
-       op->d.ura.rid = g_strdup (in_rid);
-       op->d.ura.auid = g_strdup (in_alarm_uid);
+       backend = e_data_cal_ref_backend (data_cal);
+       g_return_val_if_fail (backend != NULL, FALSE);
+
+       async_context = async_context_new (data_cal, invocation);
 
-       op_dispatch (cal, op);
+       e_cal_backend_discard_alarm (
+               backend,
+               in_uid, in_rid, in_alarm_uid,
+               async_context->cancellable,
+               data_cal_complete_discard_alarm_cb,
+               async_context);
 
        g_object_unref (backend);
 
@@ -1235,64 +1274,163 @@ data_cal_handle_discard_alarm_cb (EDBusCalendar *interface,
 static gboolean
 data_cal_handle_get_view_cb (EDBusCalendar *interface,
                              GDBusMethodInvocation *invocation,
-                             const gchar *in_sexp,
-                             EDataCal *cal)
+                             const gchar *in_query,
+                             EDataCal *data_cal)
 {
-       OperationData *op;
        ECalBackend *backend;
+       EDataCalView *view;
+       ECalBackendSExp *sexp;
+       GDBusConnection *connection;
+       gchar *object_path;
+       GError *error = NULL;
 
-       backend = e_data_cal_ref_backend (cal);
+       backend = e_data_cal_ref_backend (data_cal);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_GET_VIEW, cal, backend, invocation);
-       op->d.sexp = g_strdup (in_sexp);
+       sexp = e_cal_backend_sexp_new (in_query);
+       if (sexp == NULL) {
+               g_dbus_method_invocation_return_error_literal (
+                       invocation,
+                       E_CLIENT_ERROR,
+                       E_CLIENT_ERROR_INVALID_QUERY,
+                       _("Invalid query"));
+               g_object_unref (backend);
+               return TRUE;
+       }
+
+       object_path = construct_calview_path ();
+       connection = g_dbus_method_invocation_get_connection (invocation);
+
+       view = e_data_cal_view_new (
+               backend, sexp, connection, object_path, &error);
+
+       g_object_unref (sexp);
+
+       /* Sanity check. */
+       g_return_val_if_fail (
+               ((view != NULL) && (error == NULL)) ||
+               ((view == NULL) && (error != NULL)), FALSE);
+
+       if (view != NULL) {
+               e_dbus_calendar_complete_get_view (
+                       interface, invocation, object_path);
+               e_cal_backend_add_view (backend, view);
+               g_object_unref (view);
+       } else {
+               data_cal_convert_to_client_error (error);
+               g_prefix_error (&error, "%s", _("Invalid query: "));
+               g_dbus_method_invocation_take_error (invocation, error);
+       }
 
-       /* This operation is never queued. */
-       e_operation_pool_push (ops_pool, op);
+       g_free (object_path);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_cal_complete_get_timezone_cb (GObject *source_object,
+                                   GAsyncResult *result,
+                                   gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       gchar *tzobject;
+       GError *error = NULL;
+
+       /* XXX Should this return an ECalComponent instead? */
+       tzobject = e_cal_backend_get_timezone_finish (
+               E_CAL_BACKEND (source_object), result, &error);
+
+       /* Sanity check. */
+       g_return_if_fail (
+               ((tzobject != NULL) && (error == NULL)) ||
+               ((tzobject == NULL) && (error != NULL)));
+
+       if (tzobject != NULL) {
+               e_dbus_calendar_complete_get_timezone (
+                       async_context->interface,
+                       async_context->invocation,
+                       tzobject);
+
+               g_free (tzobject);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
+}
+
 static gboolean
 data_cal_handle_get_timezone_cb (EDBusCalendar *interface,
                                  GDBusMethodInvocation *invocation,
                                  const gchar *in_tzid,
-                                 EDataCal *cal)
+                                 EDataCal *data_cal)
 {
-       OperationData *op;
        ECalBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_cal_ref_backend (cal);
+       backend = e_data_cal_ref_backend (data_cal);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_GET_TIMEZONE, cal, backend, invocation);
-       op->d.tzid = g_strdup (in_tzid);
+       async_context = async_context_new (data_cal, invocation);
 
-       op_dispatch (cal, op);
+       e_cal_backend_get_timezone (
+               backend,
+               in_tzid,
+               async_context->cancellable,
+               data_cal_complete_get_timezone_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_cal_complete_add_timezone_cb (GObject *source_object,
+                                   GAsyncResult *result,
+                                   gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GError *error = NULL;
+
+       e_cal_backend_add_timezone_finish (
+               E_CAL_BACKEND (source_object), result, &error);
+
+       if (error == NULL) {
+               e_dbus_calendar_complete_add_timezone (
+                       async_context->interface,
+                       async_context->invocation);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
+}
+
 static gboolean
 data_cal_handle_add_timezone_cb (EDBusCalendar *interface,
                                  GDBusMethodInvocation *invocation,
                                  const gchar *in_tzobject,
-                                 EDataCal *cal)
+                                 EDataCal *data_cal)
 {
-       OperationData *op;
        ECalBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_cal_ref_backend (cal);
+       backend = e_data_cal_ref_backend (data_cal);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_ADD_TIMEZONE, cal, backend, invocation);
-       op->d.tzobject = g_strdup (in_tzobject);
+       async_context = async_context_new (data_cal, invocation);
 
-       op_dispatch (cal, op);
+       e_cal_backend_add_timezone (
+               backend,
+               in_tzobject,
+               async_context->cancellable,
+               data_cal_complete_add_timezone_cb,
+               async_context);
 
        g_object_unref (backend);
 
@@ -1302,20 +1440,14 @@ data_cal_handle_add_timezone_cb (EDBusCalendar *interface,
 static gboolean
 data_cal_handle_close_cb (EDBusCalendar *interface,
                           GDBusMethodInvocation *invocation,
-                          EDataCal *cal)
+                          EDataCal *data_cal)
 {
-       OperationData *op;
        ECalBackend *backend;
        const gchar *sender;
 
-       backend = e_data_cal_ref_backend (cal);
+       backend = e_data_cal_ref_backend (data_cal);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_CLOSE, cal, backend, invocation);
-
-       /* This operation is never queued. */
-       e_operation_pool_push (ops_pool, op);
-
        sender = g_dbus_method_invocation_get_sender (invocation);
        g_signal_emit_by_name (backend, "closed", sender);
 
@@ -1338,46 +1470,31 @@ e_data_cal_respond_open (EDataCal *cal,
                          guint32 opid,
                          GError *error)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_cal_backend_prepare_for_completion (backend, opid, NULL);
+       g_return_if_fail (simple != NULL);
 
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Cannot open calendar: "));
 
        cal->priv->opened = (error == NULL);
 
-       if (error == NULL) {
-               e_dbus_calendar_complete_open (
-                       cal->priv->dbus_interface,
-                       data->invocation);
-       } else {
+       if (error != NULL) {
                data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               g_simple_async_result_take_error (simple, error);
        }
 
-       op_unref (data);
+       g_simple_async_result_complete_in_idle (simple);
 
-       /* Dispatch any pending operations. */
-
-       g_mutex_lock (&cal->priv->open_lock);
-
-       if (opid == cal->priv->open_opid) {
-               OperationData *op;
-
-               cal->priv->open_opid = 0;
-
-               while (!g_queue_is_empty (&cal->priv->open_queue)) {
-                       op = g_queue_pop_head (&cal->priv->open_queue);
-                       e_operation_pool_push (ops_pool, op);
-               }
-       }
-
-       g_mutex_unlock (&cal->priv->open_lock);
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -1394,27 +1511,29 @@ e_data_cal_respond_refresh (EDataCal *cal,
                             guint32 opid,
                             GError *error)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_cal_backend_prepare_for_completion (backend, opid, NULL);
+       g_return_if_fail (simple != NULL);
 
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Cannot refresh calendar: "));
 
-       if (error == NULL) {
-               e_dbus_calendar_complete_refresh (
-                       cal->priv->dbus_interface,
-                       data->invocation);
-       } else {
+       if (error != NULL) {
                data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               g_simple_async_result_take_error (simple, error);
        }
 
-       op_unref (data);
+       g_simple_async_result_complete (simple);
+
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -1433,27 +1552,32 @@ e_data_cal_respond_get_backend_property (EDataCal *cal,
                                          GError *error,
                                          const gchar *prop_value)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
 
-       /* Translators: This is prefix to a detailed error message */
-       g_prefix_error (&error, "%s", _("Cannot retrieve backend property: "));
+       simple = e_cal_backend_prepare_for_completion (backend, opid, &queue);
+       g_return_if_fail (simple != NULL);
+       g_return_if_fail (queue != NULL);
 
        if (error == NULL) {
-               e_data_cal_report_backend_property_changed (
-                       cal, data->d.prop_name, prop_value);
+               /* Convert NULL to an empty string. */
+               if (prop_value == NULL)
+                       prop_value = "";
+               g_queue_push_tail (queue, g_strdup (prop_value));
        } else {
-               /* This should never happen, since all backend property
-                * requests now originate from our constructed() method. */
-               g_warning ("%s: %s", G_STRFUNC, error->message);
-               g_error_free (error);
+               g_simple_async_result_take_error (simple, error);
        }
 
-       op_unref (data);
+       g_simple_async_result_complete_in_idle (simple);
+
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -1472,34 +1596,44 @@ e_data_cal_respond_get_object (EDataCal *cal,
                                GError *error,
                                const gchar *object)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_cal_backend_prepare_for_completion (backend, opid, &queue);
+       g_return_if_fail (simple != NULL);
+       g_return_if_fail (queue != NULL);
 
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Cannot retrieve calendar object path: "));
 
        if (error == NULL) {
-               gchar *utf8_object;
-
-               utf8_object = e_util_utf8_make_valid (object);
-
-               e_dbus_calendar_complete_get_object (
-                       cal->priv->dbus_interface,
-                       data->invocation,
-                       utf8_object);
+               ECalComponent *component;
 
-               g_free (utf8_object);
+               component = e_cal_component_new_from_string (object);
+               if (component != NULL) {
+                       g_queue_push_tail (queue, component);
+               } else {
+                       g_simple_async_result_set_error (
+                               simple, E_CAL_CLIENT_ERROR,
+                               E_CAL_CLIENT_ERROR_INVALID_OBJECT,
+                               "%s", e_cal_client_error_to_string (
+                               E_CAL_CLIENT_ERROR_INVALID_OBJECT));
+               }
        } else {
                data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               g_simple_async_result_take_error (simple, error);
        }
 
-       op_unref (data);
+       g_simple_async_result_complete_in_idle (simple);
+
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -1518,42 +1652,48 @@ e_data_cal_respond_get_object_list (EDataCal *cal,
                                     GError *error,
                                     const GSList *objects)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_cal_backend_prepare_for_completion (backend, opid, &queue);
+       g_return_if_fail (simple != NULL);
+       g_return_if_fail (queue != NULL);
 
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Cannot retrieve calendar object list: "));
 
        if (error == NULL) {
-               gchar **strv;
-               guint length;
-               gint ii = 0;
+               GSList *list, *link;
 
-               length = g_slist_length ((GSList *) objects);
-               strv = g_new0 (gchar *, length + 1);
+               list = (GSList *) objects;
 
-               while (objects != NULL) {
-                       strv[ii++] = e_util_utf8_make_valid (objects->data);
-                       objects = g_slist_next (objects);
-               }
+               for (link = list; link != NULL; link = g_slist_next (link)) {
+                       ECalComponent *component;
+                       gchar *string = link->data;
 
-               e_dbus_calendar_complete_get_object_list (
-                       cal->priv->dbus_interface,
-                       data->invocation,
-                       (const gchar * const *) strv);
+                       component = e_cal_component_new_from_string (string);
+                       if (component != NULL) {
+                               g_queue_push_tail (
+                                       queue, g_object_ref (component));
+                               g_object_unref (component);
+                       }
+               }
 
-               g_strfreev (strv);
        } else {
                data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               g_simple_async_result_take_error (simple, error);
        }
 
-       op_unref (data);
+       g_simple_async_result_complete_in_idle (simple);
+
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -1571,27 +1711,29 @@ e_data_cal_respond_get_free_busy (EDataCal *cal,
                                   guint32 opid,
                                   GError *error)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_cal_backend_prepare_for_completion (backend, opid, NULL);
+       g_return_if_fail (simple != NULL);
 
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Cannot retrieve calendar free/busy list: "));
 
-       if (error == NULL) {
-               e_dbus_calendar_complete_get_free_busy (
-                       cal->priv->dbus_interface,
-                       data->invocation);
-       } else {
+       if (error != NULL) {
                data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               g_simple_async_result_take_error (simple, error);
        }
 
-       op_unref (data);
+       g_simple_async_result_complete_in_idle (simple);
+
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -1612,52 +1754,55 @@ e_data_cal_respond_create_objects (EDataCal *cal,
                                    const GSList *uids,
                                    GSList *new_components)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_cal_backend_prepare_for_completion (backend, opid, &queue);
+       g_return_if_fail (simple != NULL);
+       g_return_if_fail (queue != NULL);
 
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Cannot create calendar object: "));
 
        if (error == NULL) {
-               gchar **strv;
-               guint length;
-               gint ii = 0;
+               GQueue *inner_queue;
+               GSList *list, *link;
 
-               length = g_slist_length ((GSList *) uids);
-               strv = g_new0 (gchar *, length + 1);
+               inner_queue = g_queue_new ();
 
-               while (uids != NULL) {
-                       strv[ii++] = e_util_utf8_make_valid (uids->data);
-                       uids = g_slist_next ((GSList *) uids);
-               }
+               list = (GSList *) uids;
 
-               e_dbus_calendar_complete_create_objects (
-                       cal->priv->dbus_interface,
-                       data->invocation,
-                       (const gchar * const *) strv);
+               for (link = list; link != NULL; link = g_slist_next (link))
+                       g_queue_push_tail (inner_queue, g_strdup (link->data));
 
-               g_strfreev (strv);
+               g_queue_push_tail (queue, inner_queue);
 
-               while (new_components != NULL) {
-                       ECalComponent *component;
+               inner_queue = g_queue_new ();
 
-                       component = E_CAL_COMPONENT (new_components->data);
-                       e_cal_backend_notify_component_created (
-                               data->backend, component);
+               list = (GSList *) new_components;
+
+               for (link = list; link != NULL; link = g_slist_next (link))
+                       g_queue_push_tail (
+                               inner_queue,
+                               g_object_ref (link->data));
+
+               g_queue_push_tail (queue, inner_queue);
 
-                       new_components = g_slist_next (new_components);
-               }
        } else {
                data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               g_simple_async_result_take_error (simple, error);
        }
 
-       op_unref (data);
+       g_simple_async_result_complete_in_idle (simple);
+
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -1678,41 +1823,59 @@ e_data_cal_respond_modify_objects (EDataCal *cal,
                                    GSList *old_components,
                                    GSList *new_components)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_cal_backend_prepare_for_completion (backend, opid, &queue);
+       g_return_if_fail (simple != NULL);
+       g_return_if_fail (queue != NULL);
 
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Cannot modify calendar object: "));
 
        if (error == NULL) {
-               e_dbus_calendar_complete_modify_objects (
-                       cal->priv->dbus_interface,
-                       data->invocation);
+               GQueue *inner_queue;
+               GSList *list, *link;
 
-               while (old_components != NULL && new_components != NULL) {
-                       ECalComponent *old_component;
-                       ECalComponent *new_component;
+               /* FIXME Ugh, this is awkward... */
 
-                       old_component = E_CAL_COMPONENT (old_components->data);
-                       new_component = E_CAL_COMPONENT (new_components->data);
+               inner_queue = g_queue_new ();
 
-                       e_cal_backend_notify_component_modified (
-                               data->backend, old_component, new_component);
+               list = (GSList *) old_components;
+
+               for (link = list; link != NULL; link = g_slist_next (link))
+                       g_queue_push_tail (
+                               inner_queue,
+                               g_object_ref (link->data));
+
+               g_queue_push_tail (queue, inner_queue);
+
+               inner_queue = g_queue_new ();
+
+               list = (GSList *) new_components;
+
+               for (link = list; link != NULL; link = g_slist_next (link))
+                       g_queue_push_tail (
+                               inner_queue,
+                               g_object_ref (link->data));
+
+               g_queue_push_tail (queue, inner_queue);
 
-                       old_components = g_slist_next (old_components);
-                       new_components = g_slist_next (new_components);
-               }
        } else {
                data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               g_simple_async_result_take_error (simple, error);
        }
 
-       op_unref (data);
+       g_simple_async_result_complete_in_idle (simple);
+
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -1736,47 +1899,76 @@ e_data_cal_respond_remove_objects (EDataCal *cal,
                                   GSList *old_components,
                                   GSList *new_components)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_cal_backend_prepare_for_completion (backend, opid, &queue);
+       g_return_if_fail (simple != NULL);
+       g_return_if_fail (queue != NULL);
 
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Cannot remove calendar object: "));
 
        if (error == NULL) {
-               e_dbus_calendar_complete_remove_objects (
-                       cal->priv->dbus_interface,
-                       data->invocation);
+               GQueue *inner_queue;
+               GSList *list, *link;
+
+               /* FIXME Ugh, this is awkward... */
+
+               inner_queue = g_queue_new ();
+
+               list = (GSList *) ids;
+
+               for (link = list; link != NULL; link = g_slist_next (link))
+                       g_queue_push_tail (
+                               inner_queue,
+                               e_cal_component_id_copy (link->data));
+
+               g_queue_push_tail (queue, inner_queue);
 
-               while (ids != NULL && old_components != NULL) {
-                       ECalComponentId *id;
-                       ECalComponent *old_component;
-                       ECalComponent *new_component = NULL;
+               inner_queue = g_queue_new ();
 
-                       id = ids->data;
-                       old_component = E_CAL_COMPONENT (old_components->data);
-                       new_component = (new_components != NULL) ?
-                               E_CAL_COMPONENT (new_components->data) : NULL;
+               list = (GSList *) old_components;
 
-                       e_cal_backend_notify_component_removed (
-                               data->backend, id, old_component, new_component);
+               for (link = list; link != NULL; link = g_slist_next (link))
+                       g_queue_push_tail (
+                               inner_queue,
+                               g_object_ref (link->data));
 
-                       ids = g_slist_next ((GSList *) ids);
-                       old_components = g_slist_next (old_components);
+               g_queue_push_tail (queue, inner_queue);
 
-                       if (new_components != NULL)
-                               new_components = g_slist_next (new_components);
+               if (new_components != NULL) {
+                       inner_queue = g_queue_new ();
+
+                       list = (GSList *) new_components;
+
+                       /* XXX Careful here.  Apparently list elements
+                        *     can be NULL.  What a horrible API design. */
+                       for (link = list; link != NULL; link = g_slist_next (link)) {
+                               if (link->data != NULL)
+                                       g_object_ref (link->data);
+                                       g_queue_push_tail (
+                                               inner_queue, link->data);
+                       }
+
+                       g_queue_push_tail (queue, inner_queue);
                }
+
        } else {
                data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               g_simple_async_result_take_error (simple, error);
        }
 
-       op_unref (data);
+       g_simple_async_result_complete_in_idle (simple);
+
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -1793,27 +1985,29 @@ e_data_cal_respond_receive_objects (EDataCal *cal,
                                     guint32 opid,
                                     GError *error)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_cal_backend_prepare_for_completion (backend, opid, NULL);
+       g_return_if_fail (simple != NULL);
 
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Cannot receive calendar objects: "));
 
-       if (error == NULL) {
-               e_dbus_calendar_complete_receive_objects (
-                       cal->priv->dbus_interface,
-                       data->invocation);
-       } else {
+       if (error != NULL) {
                data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               g_simple_async_result_take_error (simple, error);
        }
 
-       op_unref (data);
+       g_simple_async_result_complete_in_idle (simple);
+
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -1834,48 +2028,45 @@ e_data_cal_respond_send_objects (EDataCal *cal,
                                  const GSList *users,
                                  const gchar *calobj)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_cal_backend_prepare_for_completion (backend, opid, &queue);
+       g_return_if_fail (simple != NULL);
+       g_return_if_fail (queue != NULL);
 
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Cannot send calendar objects: "));
 
        if (error == NULL) {
-               gchar *utf8_calobj;
-               gchar **strv;
-               guint length;
-               gint ii = 0;
-
-               length = g_slist_length ((GSList *) users);
-               strv = g_new0 (gchar *, length + 1);
-
-               while (users != NULL) {
-                       strv[ii++] = e_util_utf8_make_valid (users->data);
-                       users = g_slist_next ((GSList *) users);
-               }
+               ECalComponent *component;
+               GSList *list, *link;
 
-               utf8_calobj = e_util_utf8_make_valid (calobj);
+               component = e_cal_component_new_from_string (calobj);
+               g_return_if_fail (component != NULL);
+               g_queue_push_tail (queue, g_object_ref (component));
+               g_object_unref (component);
 
-               e_dbus_calendar_complete_send_objects (
-                       cal->priv->dbus_interface,
-                       data->invocation,
-                       (const gchar *const *) strv,
-                       utf8_calobj);
+               list = (GSList *) users;
 
-               g_free (utf8_calobj);
+               for (link = list; link != NULL; link = g_slist_next (link))
+                       g_queue_push_tail (queue, g_strdup (link->data));
 
-               g_strfreev (strv);
        } else {
                data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               g_simple_async_result_take_error (simple, error);
        }
 
-       op_unref (data);
+       g_simple_async_result_complete_in_idle (simple);
+
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -1894,40 +2085,38 @@ e_data_cal_respond_get_attachment_uris (EDataCal *cal,
                                         GError *error,
                                         const GSList *attachment_uris)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_cal_backend_prepare_for_completion (backend, opid, &queue);
+       g_return_if_fail (simple != NULL);
+       g_return_if_fail (queue != NULL);
 
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Could not retrieve attachment uris: "));
 
        if (error == NULL) {
-               gchar **strv;
-               guint length;
-               gint ii = 0;
-
-               length = g_slist_length ((GSList *) attachment_uris);
-               strv = g_new0 (gchar *, length + 1);
+               GSList *list, *link;
 
-               while (attachment_uris != NULL) {
-                       strv[ii++] = e_util_utf8_make_valid (attachment_uris->data);
-                       attachment_uris = g_slist_next ((GSList *) attachment_uris);
-               }
-
-               e_dbus_calendar_complete_get_attachment_uris (
-                       cal->priv->dbus_interface,
-                       data->invocation,
-                       (const gchar * const *) strv);
+               list = (GSList *) attachment_uris;
 
-               g_strfreev (strv);
+               for (link = list; link != NULL; link = g_slist_next (link))
+                       g_queue_push_tail (queue, g_strdup (link->data));
        } else {
                data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               g_simple_async_result_take_error (simple, error);
        }
+
+       g_simple_async_result_complete_in_idle (simple);
+
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -1944,73 +2133,29 @@ e_data_cal_respond_discard_alarm (EDataCal *cal,
                                   guint32 opid,
                                   GError *error)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_cal_backend_prepare_for_completion (backend, opid, NULL);
+       g_return_if_fail (simple != NULL);
 
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Could not discard reminder: "));
 
-       if (error == NULL) {
-               e_dbus_calendar_complete_discard_alarm (
-                       cal->priv->dbus_interface,
-                       data->invocation);
-       } else {
+       if (error != NULL) {
                data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               g_simple_async_result_take_error (simple, error);
        }
 
-       op_unref (data);
-}
-
-/**
- * e_data_cal_respond_get_view:
- * @cal: A calendar client interface.
- * @error: Operation error, if any, automatically freed if passed it.
- * @view_path: The new live view path.
- *
- * Notifies listeners of the completion of the get_view method call.
- *
- * Since: 3.2
- */
-void
-e_data_cal_respond_get_view (EDataCal *cal,
-                             guint32 opid,
-                             GError *error,
-                             const gchar *view_path)
-{
-       OperationData *data;
-
-       g_return_if_fail (E_IS_DATA_CAL (cal));
-
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
-
-       /* Translators: This is prefix to a detailed error message */
-       g_prefix_error (&error, "%s", _("Could not get calendar view path: "));
-
-       if (error == NULL) {
-               gchar *utf8_view_path;
-
-               utf8_view_path = e_util_utf8_make_valid (view_path);
-
-               e_dbus_calendar_complete_get_view (
-                       cal->priv->dbus_interface,
-                       data->invocation,
-                       utf8_view_path);
-
-               g_free (utf8_view_path);
-       } else {
-               data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
-       }
+       g_simple_async_result_complete_in_idle (simple);
 
-       op_unref (data);
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -2029,34 +2174,33 @@ e_data_cal_respond_get_timezone (EDataCal *cal,
                                  GError *error,
                                  const gchar *tzobject)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_cal_backend_prepare_for_completion (backend, opid, &queue);
+       g_return_if_fail (simple != NULL);
+       g_return_if_fail (queue != NULL);
 
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Could not retrieve calendar time zone: "));
 
        if (error == NULL) {
-               gchar *utf8_tz_object;
-
-               utf8_tz_object = e_util_utf8_make_valid (tzobject);
-
-               e_dbus_calendar_complete_get_timezone (
-                       cal->priv->dbus_interface,
-                       data->invocation,
-                       utf8_tz_object);
-
-               g_free (utf8_tz_object);
+               g_queue_push_tail (queue, g_strdup (tzobject));
        } else {
                data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               g_simple_async_result_take_error (simple, error);
        }
 
-       op_unref (data);
+       g_simple_async_result_complete_in_idle (simple);
+
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -2073,27 +2217,29 @@ e_data_cal_respond_add_timezone (EDataCal *cal,
                                  guint32 opid,
                                  GError *error)
 {
-       OperationData *data;
+       ECalBackend *backend;
+       GSimpleAsyncResult *simple;
 
        g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       data = op_claim (cal, opid);
-       g_return_if_fail (data != NULL);
+       backend = e_data_cal_ref_backend (cal);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_cal_backend_prepare_for_completion (backend, opid, NULL);
+       g_return_if_fail (simple != NULL);
 
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Could not add calendar time zone: "));
 
-       if (error == NULL) {
-               e_dbus_calendar_complete_add_timezone (
-                       cal->priv->dbus_interface,
-                       data->invocation);
-       } else {
+       if (error != NULL) {
                data_cal_convert_to_client_error (error);
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               g_simple_async_result_take_error (simple, error);
        }
 
-       op_unref (data);
+       g_simple_async_result_complete_in_idle (simple);
+
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -2294,6 +2440,8 @@ data_cal_dispose (GObject *object)
                priv->connection = NULL;
        }
 
+       g_hash_table_remove_all (priv->sender_table);
+
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_data_cal_parent_class)->dispose (object);
 }
@@ -2307,23 +2455,14 @@ data_cal_finalize (GObject *object)
 
        g_free (priv->object_path);
 
-       if (priv->pending_ops) {
-               g_hash_table_destroy (priv->pending_ops);
-               priv->pending_ops = NULL;
-       }
-
-       g_rec_mutex_clear (&priv->pending_ops_lock);
+       g_mutex_clear (&priv->sender_lock);
+       g_hash_table_destroy (priv->sender_table);
 
        if (priv->dbus_interface) {
                g_object_unref (priv->dbus_interface);
                priv->dbus_interface = NULL;
        }
 
-       g_mutex_clear (&priv->open_lock);
-
-       /* This should be empty now, else we leak memory. */
-       g_warn_if_fail (g_queue_is_empty (&priv->open_queue));
-
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_data_cal_parent_class)->finalize (object);
 }
@@ -2332,8 +2471,9 @@ static void
 data_cal_constructed (GObject *object)
 {
        EDataCal *cal = E_DATA_CAL (object);
-       OperationData *op;
        ECalBackend *backend;
+       const gchar *prop_name;
+       gchar *prop_value;
 
        /* Chain up to parent's constructed() method. */
        G_OBJECT_CLASS (e_data_cal_parent_class)->constructed (object);
@@ -2359,44 +2499,42 @@ data_cal_constructed (GObject *object)
                cal->priv->dbus_interface, "writable",
                G_BINDING_SYNC_CREATE);
 
-       /* XXX Initialize the rest of the properties by faking client
-        *     requests.  At present it's the only way to fish values
-        *     from ECalBackend's antiquated API. */
-
-       op = op_new (OP_GET_BACKEND_PROPERTY, cal, backend, NULL);
-       op->d.prop_name = CLIENT_BACKEND_PROPERTY_CAPABILITIES;
-       e_cal_backend_get_backend_property (
-               backend, cal, op->id,
-               op->cancellable, op->d.prop_name);
-       op_unref (op);
-
-       op = op_new (OP_GET_BACKEND_PROPERTY, cal, backend, NULL);
-       op->d.prop_name = CLIENT_BACKEND_PROPERTY_REVISION;
-       e_cal_backend_get_backend_property (
-               backend, cal, op->id,
-               op->cancellable, op->d.prop_name);
-       op_unref (op);
-
-       op = op_new (OP_GET_BACKEND_PROPERTY, cal, backend, NULL);
-       op->d.prop_name = CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS;
-       e_cal_backend_get_backend_property (
-               backend, cal, op->id,
-               op->cancellable, op->d.prop_name);
-       op_unref (op);
-
-       op = op_new (OP_GET_BACKEND_PROPERTY, cal, backend, NULL);
-       op->d.prop_name = CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS;
-       e_cal_backend_get_backend_property (
-               backend, cal, op->id,
-               op->cancellable, op->d.prop_name);
-       op_unref (op);
-
-       op = op_new (OP_GET_BACKEND_PROPERTY, cal, backend, NULL);
-       op->d.prop_name = CAL_BACKEND_PROPERTY_DEFAULT_OBJECT;
-       e_cal_backend_get_backend_property (
-               backend, cal, op->id,
-               op->cancellable, op->d.prop_name);
-       op_unref (op);
+       /* XXX Initialize the rest of the properties. */
+
+       prop_name = CLIENT_BACKEND_PROPERTY_CAPABILITIES;
+       prop_value = e_cal_backend_get_backend_property_sync (
+               backend, prop_name, NULL, NULL);
+       e_data_cal_report_backend_property_changed (
+               cal, prop_name, prop_value);
+       g_free (prop_value);
+
+       prop_name = CLIENT_BACKEND_PROPERTY_REVISION;
+       prop_value = e_cal_backend_get_backend_property_sync (
+               backend, prop_name, NULL, NULL);
+       e_data_cal_report_backend_property_changed (
+               cal, prop_name, prop_value);
+       g_free (prop_value);
+
+       prop_name = CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS;
+       prop_value = e_cal_backend_get_backend_property_sync (
+               backend, prop_name, NULL, NULL);
+       e_data_cal_report_backend_property_changed (
+               cal, prop_name, prop_value);
+       g_free (prop_value);
+
+       prop_name = CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS;
+       prop_value = e_cal_backend_get_backend_property_sync (
+               backend, prop_name, NULL, NULL);
+       e_data_cal_report_backend_property_changed (
+               cal, prop_name, prop_value);
+       g_free (prop_value);
+
+       prop_name = CAL_BACKEND_PROPERTY_DEFAULT_OBJECT;
+       prop_value = e_cal_backend_get_backend_property_sync (
+               backend, prop_name, NULL, NULL);
+       e_data_cal_report_backend_property_changed (
+               cal, prop_name, prop_value);
+       g_free (prop_value);
 
        g_object_unref (backend);
 }
@@ -2468,9 +2606,6 @@ e_data_cal_class_init (EDataCalClass *class)
                        G_PARAM_READWRITE |
                        G_PARAM_CONSTRUCT_ONLY |
                        G_PARAM_STATIC_STRINGS));
-
-       if (!ops_pool)
-               ops_pool = e_operation_pool_new (10, operation_thread, NULL);
 }
 
 static void
@@ -2480,72 +2615,71 @@ e_data_cal_initable_init (GInitableIface *interface)
 }
 
 static void
-e_data_cal_init (EDataCal *ecal)
+e_data_cal_init (EDataCal *data_cal)
 {
        EDBusCalendar *dbus_interface;
 
-       ecal->priv = E_DATA_CAL_GET_PRIVATE (ecal);
+       data_cal->priv = E_DATA_CAL_GET_PRIVATE (data_cal);
 
        dbus_interface = e_dbus_calendar_skeleton_new ();
-       ecal->priv->dbus_interface = dbus_interface;
+       data_cal->priv->dbus_interface = dbus_interface;
 
-       ecal->priv->pending_ops = g_hash_table_new_full (
-               (GHashFunc) g_direct_hash,
-               (GEqualFunc) g_direct_equal,
-               (GDestroyNotify) NULL,
-               (GDestroyNotify) op_unref);
-       g_rec_mutex_init (&ecal->priv->pending_ops_lock);
+       g_mutex_init (&data_cal->priv->sender_lock);
 
-       g_mutex_init (&ecal->priv->open_lock);
+       data_cal->priv->sender_table = g_hash_table_new_full (
+               (GHashFunc) g_str_hash,
+               (GEqualFunc) g_str_equal,
+               (GDestroyNotify) g_free,
+               (GDestroyNotify) g_ptr_array_unref);
 
        g_signal_connect (
                dbus_interface, "handle-open",
-               G_CALLBACK (data_cal_handle_open_cb), ecal);
+               G_CALLBACK (data_cal_handle_open_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-refresh",
-               G_CALLBACK (data_cal_handle_refresh_cb), ecal);
+               G_CALLBACK (data_cal_handle_refresh_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-get-object",
-               G_CALLBACK (data_cal_handle_get_object_cb), ecal);
+               G_CALLBACK (data_cal_handle_get_object_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-get-object-list",
-               G_CALLBACK (data_cal_handle_get_object_list_cb), ecal);
+               G_CALLBACK (data_cal_handle_get_object_list_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-get-free-busy",
-               G_CALLBACK (data_cal_handle_get_free_busy_cb), ecal);
+               G_CALLBACK (data_cal_handle_get_free_busy_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-create-objects",
-               G_CALLBACK (data_cal_handle_create_objects_cb), ecal);
+               G_CALLBACK (data_cal_handle_create_objects_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-modify-objects",
-               G_CALLBACK (data_cal_handle_modify_objects_cb), ecal);
+               G_CALLBACK (data_cal_handle_modify_objects_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-remove-objects",
-               G_CALLBACK (data_cal_handle_remove_objects_cb), ecal);
+               G_CALLBACK (data_cal_handle_remove_objects_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-receive-objects",
-               G_CALLBACK (data_cal_handle_receive_objects_cb), ecal);
+               G_CALLBACK (data_cal_handle_receive_objects_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-send-objects",
-               G_CALLBACK (data_cal_handle_send_objects_cb), ecal);
+               G_CALLBACK (data_cal_handle_send_objects_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-get-attachment-uris",
-               G_CALLBACK (data_cal_handle_get_attachment_uris_cb), ecal);
+               G_CALLBACK (data_cal_handle_get_attachment_uris_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-discard-alarm",
-               G_CALLBACK (data_cal_handle_discard_alarm_cb), ecal);
+               G_CALLBACK (data_cal_handle_discard_alarm_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-get-view",
-               G_CALLBACK (data_cal_handle_get_view_cb), ecal);
+               G_CALLBACK (data_cal_handle_get_view_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-get-timezone",
-               G_CALLBACK (data_cal_handle_get_timezone_cb), ecal);
+               G_CALLBACK (data_cal_handle_get_timezone_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-add-timezone",
-               G_CALLBACK (data_cal_handle_add_timezone_cb), ecal);
+               G_CALLBACK (data_cal_handle_add_timezone_cb), data_cal);
        g_signal_connect (
                dbus_interface, "handle-close",
-               G_CALLBACK (data_cal_handle_close_cb), ecal);
+               G_CALLBACK (data_cal_handle_close_cb), data_cal);
 }
 
 /**
diff --git a/calendar/libedata-cal/e-data-cal.h b/calendar/libedata-cal/e-data-cal.h
index 117b96f..b3bb673 100644
--- a/calendar/libedata-cal/e-data-cal.h
+++ b/calendar/libedata-cal/e-data-cal.h
@@ -1,23 +1,19 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* Evolution calendar client interface object
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- * Authors: Federico Mena-Quintero <federico ximian com>
- *          Rodrigo Moya <rodrigo ximian com>
+/*
+ * e-data-cal.h
  *
  * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU Lesser General Public
- * License as published by the Free Software Foundation.
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
  *
  * This program 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.
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
  *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
 #if !defined (__LIBEDATA_CAL_H_INSIDE__) && !defined (LIBEDATA_CAL_COMPILATION)
@@ -239,10 +235,6 @@ void               e_data_cal_respond_discard_alarm
                                                (EDataCal *cal,
                                                 guint32 opid,
                                                 GError *error);
-void           e_data_cal_respond_get_view     (EDataCal *cal,
-                                                guint32 opid,
-                                                GError *error,
-                                                const gchar *view_path);
 void           e_data_cal_respond_get_timezone (EDataCal *cal,
                                                 guint32 opid,
                                                 GError *error,
diff --git a/configure.ac b/configure.ac
index 1e1ac78..21cfc57 100644
--- a/configure.ac
+++ b/configure.ac
@@ -114,7 +114,7 @@ LIBECAL_CURRENT=15
 LIBECAL_REVISION=0
 LIBECAL_AGE=0
 
-LIBEDATACAL_CURRENT=20
+LIBEDATACAL_CURRENT=21
 LIBEDATACAL_REVISION=0
 LIBEDATACAL_AGE=0
 
diff --git a/docs/reference/calendar/libedata-cal/libedata-cal-sections.txt 
b/docs/reference/calendar/libedata-cal/libedata-cal-sections.txt
index 2b34cfd..be44d2e 100644
--- a/docs/reference/calendar/libedata-cal/libedata-cal-sections.txt
+++ b/docs/reference/calendar/libedata-cal/libedata-cal-sections.txt
@@ -25,21 +25,51 @@ e_cal_backend_create_cache_filename
 e_cal_backend_add_view
 e_cal_backend_remove_view
 e_cal_backend_list_views
+e_cal_backend_get_backend_property_sync
 e_cal_backend_get_backend_property
+e_cal_backend_get_backend_property_finish
+e_cal_backend_open_sync
 e_cal_backend_open
+e_cal_backend_open_finish
+e_cal_backend_refresh_sync
 e_cal_backend_refresh
+e_cal_backend_refresh_finish
+e_cal_backend_get_object_sync
 e_cal_backend_get_object
+e_cal_backend_get_object_finish
+e_cal_backend_get_object_list_sync
 e_cal_backend_get_object_list
+e_cal_backend_get_object_list_finish
+e_cal_backend_get_free_busy_sync
 e_cal_backend_get_free_busy
+e_cal_backend_get_free_busy_finish
+e_cal_backend_create_objects_sync
 e_cal_backend_create_objects
+e_cal_backend_create_objects_finish
+e_cal_backend_modify_objects_sync
 e_cal_backend_modify_objects
+e_cal_backend_modify_objects_finish
+e_cal_backend_remove_objects_sync
 e_cal_backend_remove_objects
+e_cal_backend_remove_objects_finish
+e_cal_backend_receive_objects_sync
 e_cal_backend_receive_objects
+e_cal_backend_receive_objects_finish
+e_cal_backend_send_objects_sync
 e_cal_backend_send_objects
+e_cal_backend_send_objects_finish
+e_cal_backend_get_attachment_uris_sync
 e_cal_backend_get_attachment_uris
+e_cal_backend_get_attachment_uris_finish
+e_cal_backend_discard_alarm_sync
 e_cal_backend_discard_alarm
+e_cal_backend_discard_alarm_finish
+e_cal_backend_get_timezone_sync
 e_cal_backend_get_timezone
+e_cal_backend_get_timezone_finish
+e_cal_backend_add_timezone_sync
 e_cal_backend_add_timezone
+e_cal_backend_add_timezone_finish
 e_cal_backend_start_view
 e_cal_backend_stop_view
 e_cal_backend_notify_component_created
@@ -60,6 +90,7 @@ ECalBackendClass
 e_cal_backend_get_type
 <SUBSECTION Private>
 ECalBackendPrivate
+e_cal_backend_prepare_for_completion
 </SECTION>
 
 <SECTION>
@@ -247,7 +278,6 @@ e_data_cal_respond_receive_objects
 e_data_cal_respond_send_objects
 e_data_cal_respond_get_attachment_uris
 e_data_cal_respond_discard_alarm
-e_data_cal_respond_get_view
 e_data_cal_respond_get_timezone
 e_data_cal_respond_add_timezone
 e_data_cal_report_error


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]