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



commit a56367dc71cbd1d4371ec6024fa4e3b4e7147bcc
Author: Matthew Barnes <mbarnes redhat com>
Date:   Mon Mar 25 09:28:40 2013 -0400

    Modernize EBookBackend's public API.
    
    This leaves EBookBackend and EDataBook 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 EDataBook.

 addressbook/libedata-book/e-book-backend.c         | 2224 +++++++++++++++++---
 addressbook/libedata-book/e-book-backend.h         |  172 ++-
 addressbook/libedata-book/e-data-book.c            | 2106 +++++++++----------
 addressbook/libedata-book/e-data-book.h            |   26 +-
 configure.ac                                       |    2 +-
 .../libedata-book/libedata-book-sections.txt       |   21 +-
 6 files changed, 3017 insertions(+), 1534 deletions(-)
---
diff --git a/addressbook/libedata-book/e-book-backend.c b/addressbook/libedata-book/e-book-backend.c
index 4f17bcb..1218ce7 100644
--- a/addressbook/libedata-book/e-book-backend.c
+++ b/addressbook/libedata-book/e-book-backend.c
@@ -1,9 +1,19 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- * Author:
- *   Nat Friedman (nat ximian com)
+ * e-book-backend.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; 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.
+ *
+ * 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/>
  *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  */
 
 #include <config.h>
@@ -14,12 +24,13 @@
 #include "e-data-book.h"
 #include "e-book-backend.h"
 
-#define EDB_NOT_OPENED_ERROR   e_data_book_create_error (E_DATA_BOOK_STATUS_NOT_OPENED, NULL)
-
 #define E_BOOK_BACKEND_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
        ((obj), E_TYPE_BOOK_BACKEND, EBookBackendPrivate))
 
+typedef struct _AsyncContext AsyncContext;
+typedef struct _DispatchNode DispatchNode;
+
 struct _EBookBackendPrivate {
        ESourceRegistry *registry;
        EDataBook *data_book;
@@ -31,9 +42,40 @@ struct _EBookBackendPrivate {
        GList *views;
 
        gchar *cache_dir;
+
+       GMutex operation_lock;
+       GHashTable *operation_ids;
+       GQueue pending_operations;
+       guint32 next_operation_id;
+       GSimpleAsyncResult *blocked;
+};
+
+struct _AsyncContext {
+       /* Inputs */
+       gchar *uid;
+       gchar *query;
+       const gchar *prop_name;
+       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;
 };
 
-/* Property IDs */
 enum {
        PROP_0,
        PROP_CACHE_DIR,
@@ -51,6 +93,175 @@ static guint signals[LAST_SIGNAL];
 G_DEFINE_TYPE (EBookBackend, e_book_backend, E_TYPE_BACKEND)
 
 static void
+async_context_free (AsyncContext *async_context)
+{
+       GQueue *queue;
+
+       g_free (async_context->uid);
+       g_free (async_context->query);
+
+       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
+book_backend_push_operation (EBookBackend *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
+book_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
+book_backend_dispatch_next_operation (EBookBackend *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 (
+               book_backend_dispatch_thread,
+               node, (GDestroyNotify) dispatch_node_free,
+               G_PRIORITY_DEFAULT,
+               node->cancellable);
+
+       return TRUE;
+}
+
+static guint32
+book_backend_stash_operation (EBookBackend *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 *
+book_backend_claim_operation (EBookBackend *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
 book_backend_set_default_cache_dir (EBookBackend *backend)
 {
        ESource *source;
@@ -224,6 +435,13 @@ book_backend_dispose (GObject *object)
                priv->views = NULL;
        }
 
+       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_book_backend_parent_class)->dispose (object);
 }
@@ -239,6 +457,9 @@ book_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_book_backend_parent_class)->finalize (object);
 }
@@ -369,6 +590,14 @@ e_book_backend_init (EBookBackend *backend)
 
        backend->priv->views = NULL;
        g_mutex_init (&backend->priv->views_mutex);
+
+       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);
 }
 
 /**
@@ -533,370 +762,1547 @@ e_book_backend_set_writable (EBookBackend *backend,
 }
 
 /**
- * e_book_backend_open:
+ * e_book_backend_open_sync:
  * @backend: an #EBookBackend
- * @book: an #EDataBook
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @only_if_exists: %TRUE to prevent the creation of a new book
- *
- * Executes an 'open' request specified by @opid on @book
- * using @backend. This call might be finished
- * with e_data_book_respond_open() or e_book_backend_respond_opened(),
- * though the overall opening phase finishes only after call
- * of e_book_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_book_backend_notify_opened() is called either from this function
- * or from e_book_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_book_backend_open() - the opening phase begun
- * 3) either the backend is opened during this call, and notifies client
- *    with e_book_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_book_backend_notify_auth_required() and is
- *    waiting for credentials, which will be received from client
- *    by e_book_backend_authenticate_user() call. Backend's opening
- *    phase is still running in this case, thus it doesn't call
- *    e_book_backend_notify_opened() within e_book_backend_open() call.
- * 5) when backend receives credentials in e_book_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_book_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_book_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_book_backend_open() should be always finished
- * with e_data_book_respond_open(), which has no influence on the opening phase,
- * or alternatively with e_book_backend_respond_opened(). Never use authentication
- * errors in e_data_book_respond_open() to notify the client the authentication is
- * required, there is e_book_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_book_backend_open (EBookBackend *backend,
-                     EDataBook *book,
-                     guint32 opid,
-                     GCancellable *cancellable,
-                     gboolean only_if_exists)
+gboolean
+e_book_backend_open_sync (EBookBackend *backend,
+                          GCancellable *cancellable,
+                          GError **error)
 {
-       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
-       g_return_if_fail (E_IS_DATA_BOOK (book));
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_book_backend_open (
+               backend, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_book_backend_open_finish (backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_book_backend_open() */
+static void
+book_backend_open_thread (GSimpleAsyncResult *simple,
+                          GObject *source_object,
+                          GCancellable *cancellable)
+{
+       EBookBackend *backend;
+       EBookBackendClass *class;
+       EDataBook *data_book;
+
+       backend = E_BOOK_BACKEND (source_object);
+
+       class = E_BOOK_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->open != NULL);
+
+       data_book = e_book_backend_ref_data_book (backend);
+       g_return_if_fail (data_book != NULL);
 
        if (e_book_backend_is_opened (backend)) {
-               e_data_book_respond_open (book, opid, NULL);
+               g_simple_async_result_complete_in_idle (simple);
+
        } else {
-               g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->open != NULL);
+               guint32 opid;
+
+               opid = book_backend_stash_operation (backend, simple);
 
-               (* E_BOOK_BACKEND_GET_CLASS (backend)->open) (backend, book, opid, cancellable, 
only_if_exists);
+               class->open (backend, data_book, opid, cancellable, FALSE);
        }
+
+       g_object_unref (data_book);
 }
 
 /**
- * e_book_backend_refresh:
+ * e_book_backend_open:
  * @backend: an #EBookBackend
- * @book: an #EDataBook
- * @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
  *
- * Refreshes the address book being accessed by the given backend.
- * This might be finished with e_data_book_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.
+ * 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.
  *
- * Since: 3.2
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_book_backend_open_finish() to get the result of the operation.
+ *
+ * Since: 3.10
  **/
 void
-e_book_backend_refresh (EBookBackend *backend,
-                        EDataBook *book,
-                        guint32 opid,
-                        GCancellable *cancellable)
+e_book_backend_open (EBookBackend *backend,
+                     GCancellable *cancellable,
+                     GAsyncReadyCallback callback,
+                     gpointer user_data)
 {
-       g_return_if_fail (backend != NULL);
+       GSimpleAsyncResult *simple;
+
        g_return_if_fail (E_IS_BOOK_BACKEND (backend));
 
-       if (!E_BOOK_BACKEND_GET_CLASS (backend)->refresh)
-               e_data_book_respond_refresh (book, opid, e_data_book_create_error 
(E_DATA_BOOK_STATUS_NOT_SUPPORTED, NULL));
-       else if (!e_book_backend_is_opened (backend))
-               e_data_book_respond_refresh (book, opid, EDB_NOT_OPENED_ERROR);
-       else
-               (* E_BOOK_BACKEND_GET_CLASS (backend)->refresh) (backend, book, opid, cancellable);
-}
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback,
+               user_data, e_book_backend_open);
 
-/**
- * e_book_backend_create_contacts
- * @backend: an #EBookBackend
- * @book: an #EDataBook
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @vcards: a #GSList of vCards to add
- *
- * Executes a 'create contacts' request specified by @opid on @book
- * using @backend.
- * This might be finished with e_data_book_respond_create_contacts().
- *
- * Since: 3.4
- **/
-void
-e_book_backend_create_contacts (EBookBackend *backend,
-                               EDataBook *book,
-                               guint32 opid,
-                               GCancellable *cancellable,
-                               const GSList *vcards)
-{
-       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
-       g_return_if_fail (E_IS_DATA_BOOK (book));
-       g_return_if_fail (vcards);
-       g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->create_contacts);
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
 
-       if (!e_book_backend_is_opened (backend))
-               e_data_book_respond_create_contacts (book, opid, EDB_NOT_OPENED_ERROR, NULL);
-       else
-               (* E_BOOK_BACKEND_GET_CLASS (backend)->create_contacts) (backend, book, opid, cancellable, 
vcards);
-}
+       book_backend_push_operation (
+               backend, simple, cancellable, TRUE,
+               book_backend_open_thread);
 
-/**
- * e_book_backend_remove_contacts:
- * @backend: an #EBookBackend
- * @book: an #EDataBook
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @id_list: list of string IDs to remove
- *
- * Executes a 'remove contacts' request specified by @opid on @book
- * using @backend.
- * This might be finished with e_data_book_respond_remove_contacts().
- **/
-void
-e_book_backend_remove_contacts (EBookBackend *backend,
-                                EDataBook *book,
-                                guint32 opid,
-                                GCancellable *cancellable,
-                                const GSList *id_list)
-{
-       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
-       g_return_if_fail (E_IS_DATA_BOOK (book));
-       g_return_if_fail (id_list);
-       g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->remove_contacts);
+       book_backend_dispatch_next_operation (backend);
 
-       if (!e_book_backend_is_opened (backend))
-               e_data_book_respond_remove_contacts (book, opid, EDB_NOT_OPENED_ERROR, NULL);
-       else
-               (* E_BOOK_BACKEND_GET_CLASS (backend)->remove_contacts) (backend, book, opid, cancellable, 
id_list);
+       g_object_unref (simple);
 }
 
 /**
- * e_book_backend_modify_contacts:
+ * e_book_backend_open_finish:
  * @backend: an #EBookBackend
- * @book: an #EDataBook
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @vcards: the VCards to update
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
  *
- * Executes a 'modify contacts' request specified by @opid on @book
- * using @backend.
- * This might be finished with e_data_book_respond_modify_contacts().
+ * Finishes the operation started with e_book_backend_open().
  *
- * Since: 3.4
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
  **/
-void
-e_book_backend_modify_contacts (EBookBackend *backend,
-                               EDataBook *book,
-                               guint32 opid,
-                               GCancellable *cancellable,
-                               const GSList *vcards)
+gboolean
+e_book_backend_open_finish (EBookBackend *backend,
+                            GAsyncResult *result,
+                            GError **error)
 {
-       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
-       g_return_if_fail (E_IS_DATA_BOOK (book));
-       g_return_if_fail (vcards);
-       g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->modify_contacts);
-
-       if (!e_book_backend_is_opened (backend))
-               e_data_book_respond_modify_contacts (book, opid, EDB_NOT_OPENED_ERROR, NULL);
-       else
-               (* E_BOOK_BACKEND_GET_CLASS (backend)->modify_contacts) (backend, book, opid, cancellable, 
vcards);
+       GSimpleAsyncResult *simple;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_book_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 (book_backend_dispatch_next_operation (backend))
+               ;
+
+       /* Assume success unless a GError is set. */
+       return !g_simple_async_result_propagate_error (simple, error);
 }
 
 /**
- * e_book_backend_get_contact:
+ * e_book_backend_refresh_sync:
  * @backend: an #EBookBackend
- * @book: an #EDataBook
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @id: the ID of the contact to get
- *
- * Executes a 'get contact' request specified by @opid on @book
- * using @backend.
- * This might be finished with e_data_book_respond_get_contact().
+ * @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 occurs while initiating the refresh, the function will set
+ * @error and return %FALSE.  If the @backend does not support refreshing,
+ * the function will set an %E_DATA_BOOK_STATUS_NOT_SUPPORTED error and
+ * return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
  **/
-void
-e_book_backend_get_contact (EBookBackend *backend,
-                            EDataBook *book,
-                            guint32 opid,
-                            GCancellable *cancellable,
-                            const gchar *id)
+gboolean
+e_book_backend_refresh_sync (EBookBackend *backend,
+                             GCancellable *cancellable,
+                             GError **error)
 {
-       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
-       g_return_if_fail (E_IS_DATA_BOOK (book));
-       g_return_if_fail (id);
-       g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->get_contact);
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
 
-       if (!e_book_backend_is_opened (backend))
-               e_data_book_respond_get_contact (book, opid, EDB_NOT_OPENED_ERROR, NULL);
-       else
-               (* E_BOOK_BACKEND_GET_CLASS (backend)->get_contact) (backend, book, opid, cancellable, id);
-}
+       g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
 
-/**
- * e_book_backend_get_contact_list:
- * @backend: an #EBookBackend
- * @book: an #EDataBook
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @query: the s-expression to match
- *
- * Executes a 'get contact list' request specified by @opid on @book
- * using @backend.
- * This might be finished with e_data_book_respond_get_contact_list().
- **/
-void
-e_book_backend_get_contact_list (EBookBackend *backend,
-                                 EDataBook *book,
-                                 guint32 opid,
-                                 GCancellable *cancellable,
-                                 const gchar *query)
-{
-       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
-       g_return_if_fail (E_IS_DATA_BOOK (book));
-       g_return_if_fail (query);
-       g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->get_contact_list);
+       closure = e_async_closure_new ();
 
-       if (!e_book_backend_is_opened (backend))
-               e_data_book_respond_get_contact_list (book, opid, EDB_NOT_OPENED_ERROR, NULL);
-       else
-               (* E_BOOK_BACKEND_GET_CLASS (backend)->get_contact_list) (backend, book, opid, cancellable, 
query);
-}
+       e_book_backend_refresh (
+               backend, cancellable,
+               e_async_closure_callback, closure);
 
-/**
- * e_book_backend_get_contact_list_uids:
- * @backend: an #EBookBackend
- * @book: an #EDataBook
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- * @query: the s-expression to match
- *
- * Executes a 'get contact list uids' request specified by @opid on @book
- * using @backend.
- * This might be finished with e_data_book_respond_get_contact_list_uids().
- *
- * Since: 3.2
- **/
-void
-e_book_backend_get_contact_list_uids (EBookBackend *backend,
-                                      EDataBook *book,
-                                      guint32 opid,
-                                      GCancellable *cancellable,
-                                      const gchar *query)
-{
-       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
-       g_return_if_fail (E_IS_DATA_BOOK (book));
-       g_return_if_fail (query);
-       g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->get_contact_list_uids);
+       result = e_async_closure_wait (closure);
+
+       success = e_book_backend_refresh_finish (backend, result, error);
 
-       if (!e_book_backend_is_opened (backend))
-               e_data_book_respond_get_contact_list_uids (book, opid, EDB_NOT_OPENED_ERROR, NULL);
-       else
-               (* E_BOOK_BACKEND_GET_CLASS (backend)->get_contact_list_uids) (backend, book, opid, 
cancellable, query);
+       e_async_closure_free (closure);
+
+       return success;
 }
 
-/**
- * e_book_backend_start_view:
- * @backend: an #EBookBackend
- * @view: the #EDataBookView to start
- *
- * Starts running the query specified by @view, emitting signals for
- * matching contacts.
- **/
-void
-e_book_backend_start_view (EBookBackend *backend,
-                           EDataBookView *view)
+/* Helper for e_book_backend_refresh() */
+static void
+book_backend_refresh_thread (GSimpleAsyncResult *simple,
+                             GObject *source_object,
+                             GCancellable *cancellable)
 {
+       EBookBackend *backend;
        EBookBackendClass *class;
+       EDataBook *data_book;
 
-       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
-       g_return_if_fail (E_IS_DATA_BOOK_VIEW (view));
+       backend = E_BOOK_BACKEND (source_object);
 
        class = E_BOOK_BACKEND_GET_CLASS (backend);
-       g_return_if_fail (class->start_view);
 
-       class->start_view (backend, view);
+       data_book = e_book_backend_ref_data_book (backend);
+       g_return_if_fail (data_book != NULL);
+
+       if (class->refresh == NULL) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_BOOK_ERROR,
+                       E_DATA_BOOK_STATUS_NOT_SUPPORTED,
+                       "%s", e_data_book_status_to_string (
+                       E_DATA_BOOK_STATUS_NOT_SUPPORTED));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else if (!e_book_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_BOOK_ERROR,
+                       E_DATA_BOOK_STATUS_NOT_OPENED,
+                       "%s", e_data_book_status_to_string (
+                       E_DATA_BOOK_STATUS_NOT_OPENED));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = book_backend_stash_operation (backend, simple);
+
+               class->refresh (backend, data_book, opid, cancellable);
+       }
+
+       g_object_unref (data_book);
 }
 
 /**
- * e_book_backend_stop_view:
+ * e_book_backend_refresh:
  * @backend: an #EBookBackend
- * @view: the #EDataBookView to stop
+ * @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
  *
- * Stops running the query specified by @view, emitting no more signals.
+ * 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_book_backend_refresh_finish(), merely initiates the
+ * operation.
+ *
+ * Once the refresh is initiated, @callback will be called.  You can then
+ * call e_book_backend_refresh_finish() to get the result of the initiation.
+ *
+ * Since: 3.10
  **/
 void
-e_book_backend_stop_view (EBookBackend *backend,
-                          EDataBookView *view)
+e_book_backend_refresh (EBookBackend *backend,
+                        GCancellable *cancellable,
+                        GAsyncReadyCallback callback,
+                        gpointer user_data)
 {
-       EBookBackendClass *class;
+       GSimpleAsyncResult *simple;
 
        g_return_if_fail (E_IS_BOOK_BACKEND (backend));
-       g_return_if_fail (E_IS_DATA_BOOK_VIEW (view));
 
-       class = E_BOOK_BACKEND_GET_CLASS (backend);
-       g_return_if_fail (class->stop_view != NULL);
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback,
+               user_data, e_book_backend_refresh);
 
-       class->stop_view (backend, view);
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       book_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               book_backend_refresh_thread);
+
+       book_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
 }
 
 /**
- * e_book_backend_add_view:
+ * e_book_backend_refresh_finish:
  * @backend: an #EBookBackend
- * @view: an #EDataBookView
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
  *
- * Adds @view to @backend for querying.
+ * Finishes the refresh initiation started with e_book_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 %E_DATA_BOOK_STATUS_NOT_SUPPORTED error and
+ * return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
  **/
-void
-e_book_backend_add_view (EBookBackend *backend,
-                         EDataBookView *view)
+gboolean
+e_book_backend_refresh_finish (EBookBackend *backend,
+                               GAsyncResult *result,
+                               GError **error)
 {
-       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
+       GSimpleAsyncResult *simple;
 
-       g_mutex_lock (&backend->priv->views_mutex);
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_book_backend_refresh), FALSE);
 
-       g_object_ref (view);
-       backend->priv->views = g_list_append (backend->priv->views, view);
+       simple = G_SIMPLE_ASYNC_RESULT (result);
 
-       g_mutex_unlock (&backend->priv->views_mutex);
+       /* Assume success unless a GError is set. */
+       return !g_simple_async_result_propagate_error (simple, error);
 }
 
 /**
- * e_book_backend_remove_view:
+ * e_book_backend_create_contacts_sync:
  * @backend: an #EBookBackend
- * @view: an #EDataBookView
+ * @vcards: a %NULL-terminated array of vCard strings
+ * @out_contacts: a #GQueue in which to deposit results
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
  *
- * Removes @view from @backend.
+ * Creates one or more new contacts from @vcards, and deposits an #EContact
+ * instance for each newly-created contact in @out_contacts.
+ *
+ * The returned #EContact 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.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_book_backend_create_contacts_sync (EBookBackend *backend,
+                                     const gchar * const *vcards,
+                                     GQueue *out_contacts,
+                                     GCancellable *cancellable,
+                                     GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
+       g_return_val_if_fail (vcards != NULL, FALSE);
+       g_return_val_if_fail (out_contacts != NULL, FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_book_backend_create_contacts (
+               backend, vcards, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_book_backend_create_contacts_finish (
+               backend, result, out_contacts, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_book_backend_create_contacts() */
+static void
+book_backend_create_contacts_thread (GSimpleAsyncResult *simple,
+                                     GObject *source_object,
+                                     GCancellable *cancellable)
+{
+       EBookBackend *backend;
+       EBookBackendClass *class;
+       EDataBook *data_book;
+       AsyncContext *async_context;
+
+       backend = E_BOOK_BACKEND (source_object);
+
+       class = E_BOOK_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->create_contacts != NULL);
+
+       data_book = e_book_backend_ref_data_book (backend);
+       g_return_if_fail (data_book != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_book_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_BOOK_ERROR,
+                       E_DATA_BOOK_STATUS_NOT_OPENED,
+                       "%s", e_data_book_status_to_string (
+                       E_DATA_BOOK_STATUS_NOT_OPENED));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = book_backend_stash_operation (backend, simple);
+
+               class->create_contacts (
+                       backend, data_book, opid, cancellable,
+                       async_context->string_list);
+       }
+
+       g_object_unref (data_book);
+}
+
+/**
+ * e_book_backend_create_contacts
+ * @backend: an #EBookBackend
+ * @vcards: a %NULL-terminated array of vCard 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 creates one or more new contacts from @vcards.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_book_backend_create_contacts_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_book_backend_create_contacts (EBookBackend *backend,
+                                const gchar * const *vcards,
+                                GCancellable *cancellable,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       GSList *list = NULL;
+       gint ii;
+
+       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
+       g_return_if_fail (vcards != NULL);
+
+       for (ii = 0; vcards[ii] != NULL; ii++)
+               list = g_slist_prepend (list, g_strdup (vcards[ii]));
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->string_list = g_slist_reverse (list);
+       async_context->object_queue = &async_context->result_queue;
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_book_backend_create_contacts);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       book_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               book_backend_create_contacts_thread);
+
+       book_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_book_backend_create_contacts_finish:
+ * @backend: an #EBookBackend
+ * @result: a #GAsyncResult
+ * @out_contacts: a #GQueue in which to deposit results
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_book_backend_create_contacts().
+ *
+ * An #EContact instance for each newly-created contact is deposited in
+ * @out_contacts.  The returned #EContact 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.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_book_backend_create_contacts_finish (EBookBackend *backend,
+                                       GAsyncResult *result,
+                                       GQueue *out_contacts,
+                                       GError **error)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_book_backend_create_contacts), FALSE);
+       g_return_val_if_fail (out_contacts != 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;
+
+       while (!g_queue_is_empty (&async_context->result_queue)) {
+               EContact *contact;
+
+               contact = g_queue_pop_head (&async_context->result_queue);
+               g_queue_push_tail (out_contacts, g_object_ref (contact));
+               e_book_backend_notify_update (backend, contact);
+               g_object_unref (contact);
+       }
+
+       e_book_backend_notify_complete (backend);
+
+       return TRUE;
+}
+
+/**
+ * e_book_backend_modify_contacts_sync:
+ * @backend: an #EBookBackend
+ * @vcards: a %NULL-terminated array of vCard strings
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Modifies one or more contacts according to @vcards.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_book_backend_modify_contacts_sync (EBookBackend *backend,
+                                     const gchar * const *vcards,
+                                     GCancellable *cancellable,
+                                     GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       closure = e_async_closure_new ();
+
+       e_book_backend_modify_contacts (
+               backend, vcards, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_book_backend_modify_contacts_finish (
+               backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_book_backend_modify_contacts() */
+static void
+book_backend_modify_contacts_thread (GSimpleAsyncResult *simple,
+                                     GObject *source_object,
+                                     GCancellable *cancellable)
+{
+       EBookBackend *backend;
+       EBookBackendClass *class;
+       EDataBook *data_book;
+       AsyncContext *async_context;
+
+       backend = E_BOOK_BACKEND (source_object);
+
+       class = E_BOOK_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->modify_contacts != NULL);
+
+       data_book = e_book_backend_ref_data_book (backend);
+       g_return_if_fail (data_book != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_book_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_BOOK_ERROR,
+                       E_DATA_BOOK_STATUS_NOT_OPENED,
+                       "%s", e_data_book_status_to_string (
+                       E_DATA_BOOK_STATUS_NOT_OPENED));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = book_backend_stash_operation (backend, simple);
+
+               class->modify_contacts (
+                       backend, data_book, opid, cancellable,
+                       async_context->string_list);
+       }
+
+       g_object_unref (data_book);
+}
+
+/**
+ * e_book_backend_modify_contacts:
+ * @backend: an #EBookBackend
+ * @vcards: a %NULL-terminated array of vCard 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 modifies one or more contacts according to @vcards.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_book_backend_modify_contacts_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_book_backend_modify_contacts (EBookBackend *backend,
+                                const gchar * const *vcards,
+                                GCancellable *cancellable,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       GSList *list = NULL;
+       gint ii;
+
+       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
+       g_return_if_fail (vcards != NULL);
+
+       for (ii = 0; vcards[ii] != NULL; ii++)
+               list = g_slist_prepend (list, g_strdup (vcards[ii]));
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->string_list = g_slist_reverse (list);
+       async_context->object_queue = &async_context->result_queue;
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_book_backend_modify_contacts);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       book_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               book_backend_modify_contacts_thread);
+
+       book_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_book_backend_modify_contacts_finish:
+ * @backend: an #EBookBackend
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_book_backend_modify_contacts().
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_book_backend_modify_contacts_finish (EBookBackend *backend,
+                                       GAsyncResult *result,
+                                       GError **error)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_book_backend_modify_contacts), 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;
+
+       while (!g_queue_is_empty (&async_context->result_queue)) {
+               EContact *contact;
+
+               contact = g_queue_pop_head (&async_context->result_queue);
+               e_book_backend_notify_update (backend, contact);
+               g_object_unref (contact);
+       }
+
+       e_book_backend_notify_complete (backend);
+
+       return TRUE;
+}
+
+/**
+ * e_book_backend_remove_contacts_sync:
+ * @backend: an #EBookBackend
+ * @uids: a %NULL-terminated array of contact ID strings
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Removes one or more contacts according to @uids.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_book_backend_remove_contacts_sync (EBookBackend *backend,
+                                     const gchar * const *uids,
+                                     GCancellable *cancellable,
+                                     GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
+       g_return_val_if_fail (uids != NULL, FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_book_backend_remove_contacts (
+               backend, uids, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_book_backend_remove_contacts_finish (
+               backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_book_backend_remove_contacts() */
+static void
+book_backend_remove_contacts_thread (GSimpleAsyncResult *simple,
+                                     GObject *source_object,
+                                     GCancellable *cancellable)
+{
+       EBookBackend *backend;
+       EBookBackendClass *class;
+       EDataBook *data_book;
+       AsyncContext *async_context;
+
+       backend = E_BOOK_BACKEND (source_object);
+
+       class = E_BOOK_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->remove_contacts != NULL);
+
+       data_book = e_book_backend_ref_data_book (backend);
+       g_return_if_fail (data_book != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_book_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_BOOK_ERROR,
+                       E_DATA_BOOK_STATUS_NOT_OPENED,
+                       "%s", e_data_book_status_to_string (
+                       E_DATA_BOOK_STATUS_NOT_OPENED));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = book_backend_stash_operation (backend, simple);
+
+               class->remove_contacts (
+                       backend, data_book, opid, cancellable,
+                       async_context->string_list);
+       }
+
+       g_object_unref (data_book);
+}
+
+/**
+ * e_book_backend_remove_contacts:
+ * @backend: an #EBookBackend
+ * @uids: a %NULL-terminated array of contact ID 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 removes one or more contacts according to @uids.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_book_backend_remove_contacts_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_book_backend_remove_contacts (EBookBackend *backend,
+                                const gchar * const *uids,
+                                GCancellable *cancellable,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       GSList *list = NULL;
+       gint ii;
+
+       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
+       g_return_if_fail (uids != NULL);
+
+       for (ii = 0; uids[ii] != NULL; ii++)
+               list = g_slist_prepend (list, g_strdup (uids[ii]));
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->string_list = g_slist_reverse (list);
+       async_context->string_queue = &async_context->result_queue;
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_book_backend_remove_contacts);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       book_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               book_backend_remove_contacts_thread);
+
+       book_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_book_backend_remove_contacts_finish:
+ * @backend: an #EBookBackend
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_book_backend_remove_contacts().
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.10
+ **/
+gboolean
+e_book_backend_remove_contacts_finish (EBookBackend *backend,
+                                       GAsyncResult *result,
+                                       GError **error)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_book_backend_remove_contacts), 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;
+
+       while (!g_queue_is_empty (&async_context->result_queue)) {
+               gchar *uid;
+
+               uid = g_queue_pop_head (&async_context->result_queue);
+               e_book_backend_notify_remove (backend, uid);
+               g_free (uid);
+       }
+
+       e_book_backend_notify_complete (backend);
+
+       return TRUE;
+}
+
+/**
+ * e_book_backend_get_contact_sync:
+ * @backend: an #EBookBackend
+ * @uid: a contact ID
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Obtains an #EContact for @uid.
+ *
+ * The returned #EContact 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 #EContact, or %NULL
+ *
+ * Since: 3.10
+ **/
+EContact *
+e_book_backend_get_contact_sync (EBookBackend *backend,
+                                 const gchar *uid,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       EContact *contact;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
+       g_return_val_if_fail (uid != NULL, NULL);
+
+       closure = e_async_closure_new ();
+
+       e_book_backend_get_contact (
+               backend, uid, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       contact = e_book_backend_get_contact_finish (
+               backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return contact;
+}
+
+/* Helper for e_book_backend_get_contact() */
+static void
+book_backend_get_contact_thread (GSimpleAsyncResult *simple,
+                                 GObject *source_object,
+                                 GCancellable *cancellable)
+{
+       EBookBackend *backend;
+       EBookBackendClass *class;
+       EDataBook *data_book;
+       AsyncContext *async_context;
+
+       backend = E_BOOK_BACKEND (source_object);
+
+       class = E_BOOK_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->get_contact != NULL);
+
+       data_book = e_book_backend_ref_data_book (backend);
+       g_return_if_fail (data_book != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_book_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_BOOK_ERROR,
+                       E_DATA_BOOK_STATUS_NOT_OPENED,
+                       "%s", e_data_book_status_to_string (
+                       E_DATA_BOOK_STATUS_NOT_OPENED));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = book_backend_stash_operation (backend, simple);
+
+               class->get_contact (
+                       backend, data_book, opid, cancellable,
+                       async_context->uid);
+       }
+
+       g_object_unref (data_book);
+}
+
+/**
+ * e_book_backend_get_contact:
+ * @backend: an #EBookBackend
+ * @uid: a contact ID
+ * @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 #EContact for @uid.
+ *
+ * When the operation is finished, @callback will be called.  You can
+ * then call e_book_backend_get_contact_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_book_backend_get_contact (EBookBackend *backend,
+                            const gchar *uid,
+                            GCancellable *cancellable,
+                            GAsyncReadyCallback callback,
+                            gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
+       g_return_if_fail (uid != NULL);
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->uid = g_strdup (uid);
+       async_context->object_queue = &async_context->result_queue;
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_book_backend_get_contact);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       book_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               book_backend_get_contact_thread);
+
+       book_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_book_backend_get_contact_finish:
+ * @backend: an #EBookBackend
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_book_backend_get_contact_finish().
+ *
+ * The returned #EContact is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * If an error occurred, the function will set @error and return %NULL.
+ *
+ * Returns: an #EContact, or %NULL
+ *
+ * Since: 3.10
+ **/
+EContact *
+e_book_backend_get_contact_finish (EBookBackend *backend,
+                                   GAsyncResult *result,
+                                   GError **error)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+       EContact *contact;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_book_backend_get_contact), 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;
+
+       contact = g_queue_pop_head (&async_context->result_queue);
+       g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
+
+       g_warn_if_fail (g_queue_is_empty (&async_context->result_queue));
+
+       return contact;
+}
+
+/**
+ * e_book_backend_get_contact_list_sync:
+ * @backend: an #EBookBackend
+ * @query: a search query in S-expression format
+ * @out_contacts: 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 #EContact instances which satisfy the criteria specified
+ * in @query, and deposits them in @out_contacts.
+ *
+ * The returned #EContact 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_book_backend_get_contact_list_sync (EBookBackend *backend,
+                                      const gchar *query,
+                                      GQueue *out_contacts,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
+       g_return_val_if_fail (query != NULL, FALSE);
+       g_return_val_if_fail (out_contacts != NULL, FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_book_backend_get_contact_list (
+               backend, query, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_book_backend_get_contact_list_finish (
+               backend, result, out_contacts, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_book_backend_get_contact_list() */
+static void
+book_backend_get_contact_list_thread (GSimpleAsyncResult *simple,
+                                      GObject *source_object,
+                                      GCancellable *cancellable)
+{
+       EBookBackend *backend;
+       EBookBackendClass *class;
+       EDataBook *data_book;
+       AsyncContext *async_context;
+
+       backend = E_BOOK_BACKEND (source_object);
+
+       class = E_BOOK_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->get_contact_list != NULL);
+
+       data_book = e_book_backend_ref_data_book (backend);
+       g_return_if_fail (data_book != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_book_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_BOOK_ERROR,
+                       E_DATA_BOOK_STATUS_NOT_OPENED,
+                       "%s", e_data_book_status_to_string (
+                       E_DATA_BOOK_STATUS_NOT_OPENED));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = book_backend_stash_operation (backend, simple);
+
+               class->get_contact_list (
+                       backend, data_book, opid, cancellable,
+                       async_context->query);
+       }
+
+       g_object_unref (data_book);
+}
+
+/**
+ * e_book_backend_get_contact_list:
+ * @backend: an #EBookBackend
+ * @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 #EContact instances which satisfy the
+ * criteria specified in @query.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_book_backend_get_contact_list_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_book_backend_get_contact_list (EBookBackend *backend,
+                                 const gchar *query,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
+       g_return_if_fail (query != NULL);
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->query = g_strdup (query);
+       async_context->object_queue = &async_context->result_queue;
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_book_backend_get_contact_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);
+
+       book_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               book_backend_get_contact_list_thread);
+
+       book_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_book_backend_get_contact_list_finish:
+ * @backend: an #EBookBackend
+ * @result: a #GAsyncResult
+ * @out_contacts: a #GQueue in which to deposit results
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_book_backend_get_contact_list().
+ *
+ * The matching #EContact instances are deposited in @out_contacts.  The
+ * returned #EContact 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_book_backend_get_contact_list_finish (EBookBackend *backend,
+                                        GAsyncResult *result,
+                                        GQueue *out_contacts,
+                                        GError **error)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_book_backend_get_contact_list), FALSE);
+       g_return_val_if_fail (out_contacts != 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_contacts);
+
+       return TRUE;
+}
+
+/**
+ * e_book_backend_get_contact_list_uids_sync:
+ * @backend: an #EBookBackend
+ * @query: a search query in S-expression format
+ * @out_uids: 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 ID strings for contacts which satisfy the criteria
+ * specified in @query, and deposits them in @out_uids.
+ *
+ * The returned ID strings must be freed with g_free() with 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_book_backend_get_contact_list_uids_sync (EBookBackend *backend,
+                                           const gchar *query,
+                                           GQueue *out_uids,
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
+       g_return_val_if_fail (query != NULL, FALSE);
+       g_return_val_if_fail (out_uids != NULL, FALSE);
+
+       closure = e_async_closure_new ();
+
+       e_book_backend_get_contact_list_uids (
+               backend, query, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       success = e_book_backend_get_contact_list_uids_finish (
+               backend, result, out_uids, error);
+
+       e_async_closure_free (closure);
+
+       return success;
+}
+
+/* Helper for e_book_backend_get_contact_list_uids() */
+static void
+book_backend_get_contact_list_uids_thread (GSimpleAsyncResult *simple,
+                                           GObject *source_object,
+                                           GCancellable *cancellable)
+{
+       EBookBackend *backend;
+       EBookBackendClass *class;
+       EDataBook *data_book;
+       AsyncContext *async_context;
+
+       backend = E_BOOK_BACKEND (source_object);
+
+       class = E_BOOK_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->get_contact_list_uids != NULL);
+
+       data_book = e_book_backend_ref_data_book (backend);
+       g_return_if_fail (data_book != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (!e_book_backend_is_opened (backend)) {
+               g_simple_async_result_set_error (
+                       simple, E_DATA_BOOK_ERROR,
+                       E_DATA_BOOK_STATUS_NOT_OPENED,
+                       "%s", e_data_book_status_to_string (
+                       E_DATA_BOOK_STATUS_NOT_OPENED));
+               g_simple_async_result_complete_in_idle (simple);
+
+       } else {
+               guint32 opid;
+
+               opid = book_backend_stash_operation (backend, simple);
+
+               class->get_contact_list_uids (
+                       backend, data_book, opid, cancellable,
+                       async_context->query);
+       }
+
+       g_object_unref (data_book);
+}
+
+/**
+ * e_book_backend_get_contact_list_uids:
+ * @backend: an #EBookBackend
+ * @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 ID strings for contacts which satisfy
+ * the criteria specified in @query.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_book_backend_get_contact_list_uids_finish() to get the result of
+ * the operation.
+ *
+ * Since: 3.10
+ **/
+void
+e_book_backend_get_contact_list_uids (EBookBackend *backend,
+                                      const gchar *query,
+                                      GCancellable *cancellable,
+                                      GAsyncReadyCallback callback,
+                                      gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
+       g_return_if_fail (query != NULL);
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->query = g_strdup (query);
+       async_context->string_queue = &async_context->result_queue;
+
+       simple = g_simple_async_result_new (
+               G_OBJECT (backend), callback, user_data,
+               e_book_backend_get_contact_list_uids);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+       g_simple_async_result_set_op_res_gpointer (
+               simple, async_context, (GDestroyNotify) async_context_free);
+
+       book_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               book_backend_get_contact_list_uids_thread);
+
+       book_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_book_backend_get_contact_list_uids_finish:
+ * @backend: an #EBookBackend
+ * @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_book_backend_get_contact_list_uids_finish().
+ *
+ * ID strings for the matching contacts are deposited in @out_uids, and
+ * must be freed 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_book_backend_get_contact_list_uids_finish (EBookBackend *backend,
+                                             GAsyncResult *result,
+                                             GQueue *out_uids,
+                                             GError **error)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (backend),
+               e_book_backend_get_contact_list_uids), 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;
+
+       e_queue_transfer (&async_context->result_queue, out_uids);
+
+       return TRUE;
+}
+
+/**
+ * e_book_backend_start_view:
+ * @backend: an #EBookBackend
+ * @view: the #EDataBookView to start
+ *
+ * Starts running the query specified by @view, emitting signals for
+ * matching contacts.
+ **/
+void
+e_book_backend_start_view (EBookBackend *backend,
+                           EDataBookView *view)
+{
+       EBookBackendClass *class;
+
+       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
+       g_return_if_fail (E_IS_DATA_BOOK_VIEW (view));
+
+       class = E_BOOK_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->start_view);
+
+       class->start_view (backend, view);
+}
+
+/**
+ * e_book_backend_stop_view:
+ * @backend: an #EBookBackend
+ * @view: the #EDataBookView to stop
+ *
+ * Stops running the query specified by @view, emitting no more signals.
+ **/
+void
+e_book_backend_stop_view (EBookBackend *backend,
+                          EDataBookView *view)
+{
+       EBookBackendClass *class;
+
+       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
+       g_return_if_fail (E_IS_DATA_BOOK_VIEW (view));
+
+       class = E_BOOK_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->stop_view != NULL);
+
+       class->stop_view (backend, view);
+}
+
+/**
+ * e_book_backend_add_view:
+ * @backend: an #EBookBackend
+ * @view: an #EDataBookView
+ *
+ * Adds @view to @backend for querying.
+ **/
+void
+e_book_backend_add_view (EBookBackend *backend,
+                         EDataBookView *view)
+{
+       g_return_if_fail (E_IS_BOOK_BACKEND (backend));
+
+       g_mutex_lock (&backend->priv->views_mutex);
+
+       g_object_ref (view);
+       backend->priv->views = g_list_append (backend->priv->views, view);
+
+       g_mutex_unlock (&backend->priv->views_mutex);
+}
+
+/**
+ * e_book_backend_remove_view:
+ * @backend: an #EBookBackend
+ * @view: an #EDataBookView
+ *
+ * Removes @view from @backend.
  **/
 void
 e_book_backend_remove_view (EBookBackend *backend,
@@ -961,33 +2367,180 @@ e_book_backend_list_views (EBookBackend *backend)
 }
 
 /**
- * e_book_backend_get_book_backend_property:
+ * e_book_backend_get_backend_property_sync:
  * @backend: an #EBookBackend
- * @book: an #EDataBook
- * @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
- *
- * Calls the get_backend_property method on the given backend.
- * This might be finished with e_data_book_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.
+ * @prop_name: a backend property name
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
  *
- * Since: 3.2
+ * 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.  It 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_book_backend_get_backend_property_sync (EBookBackend *backend,
+                                          const gchar *prop_name,
+                                          GCancellable *cancellable,
+                                          GError **error)
+{
+       EAsyncClosure *closure;
+       GAsyncResult *result;
+       gchar *prop_value;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
+       g_return_val_if_fail (prop_name != NULL, NULL);
+
+       closure = e_async_closure_new ();
+
+       e_book_backend_get_backend_property (
+               backend, prop_name, cancellable,
+               e_async_closure_callback, closure);
+
+       result = e_async_closure_wait (closure);
+
+       prop_value = e_book_backend_get_backend_property_finish (
+               backend, result, error);
+
+       e_async_closure_free (closure);
+
+       return prop_value;
+}
+
+/* Helper for e_book_backend_get_backend_property() */
+static void
+book_backend_get_backend_property_thread (GSimpleAsyncResult *simple,
+                                          GObject *source_object,
+                                          GCancellable *cancellable)
+{
+       EBookBackend *backend;
+       EBookBackendClass *class;
+       EDataBook *data_book;
+       AsyncContext *async_context;
+       guint32 opid;
+
+       backend = E_BOOK_BACKEND (source_object);
+
+       class = E_BOOK_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->get_backend_property != NULL);
+
+       data_book = e_book_backend_ref_data_book (backend);
+       g_return_if_fail (data_book != NULL);
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       opid = book_backend_stash_operation (backend, simple);
+
+       class->get_backend_property (
+               backend, data_book, opid, cancellable,
+               async_context->prop_name);
+
+       g_object_unref (data_book);
+}
+
+/**
+ * e_book_backend_get_backend_property:
+ * @backend: an #EBookBackend
+ * @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
+ *
+ * Asynchronously obtains the value of the backend property named @prop_name.
+ *
+ * Despite appearances, e_book_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 #EDataBook.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_book_backend_get_backend_property_finish() to get the result of
+ * the operation.
+ *
+ * Since: 3.10
  **/
 void
 e_book_backend_get_backend_property (EBookBackend *backend,
-                                     EDataBook *book,
-                                     guint32 opid,
+                                     const gchar *prop_name,
                                      GCancellable *cancellable,
-                                     const gchar *prop_name)
+                                     GAsyncReadyCallback callback,
+                                     gpointer user_data)
 {
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
        g_return_if_fail (E_IS_BOOK_BACKEND (backend));
-       g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->get_backend_property);
+       g_return_if_fail (prop_name != NULL);
+
+       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_book_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);
+
+       book_backend_push_operation (
+               backend, simple, cancellable, FALSE,
+               book_backend_get_backend_property_thread);
+
+       book_backend_dispatch_next_operation (backend);
+
+       g_object_unref (simple);
+}
+
+/**
+ * e_book_backend_get_backend_property_finish:
+ * @backend: an #EBookBackend
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_book_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_book_backend_get_backend_property_finish (EBookBackend *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_book_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;
 
-       E_BOOK_BACKEND_GET_CLASS (backend)->get_backend_property (backend, book, opid, cancellable, 
prop_name);
+       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;
 }
 
 /**
@@ -1284,3 +2837,50 @@ e_book_backend_notify_property_changed (EBookBackend *backend,
        }
 }
 
+/**
+ * e_book_backend_prepare_for_completion:
+ * @backend: an #EBookBackend
+ * @opid: an operation ID given to #EDataBook
+ * @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 #EDataBook'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_book_backend_prepare_for_completion (EBookBackend *backend,
+                                       guint32 opid,
+                                       GQueue **result_queue)
+{
+       GSimpleAsyncResult *simple;
+       AsyncContext *async_context;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
+       g_return_val_if_fail (opid > 0, NULL);
+
+       simple = book_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/addressbook/libedata-book/e-book-backend.h b/addressbook/libedata-book/e-book-backend.h
index 97d308f..e1e8714 100644
--- a/addressbook/libedata-book/e-book-backend.h
+++ b/addressbook/libedata-book/e-book-backend.h
@@ -1,22 +1,19 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- * An abstract class which defines the API to a given backend.
- * There will be one EBookBackend object for every URI which is loaded.
+ * e-book-backend.h
  *
- * Two people will call into the EBookBackend API:
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
  *
- * 1. The PASBookFactory, when it has been asked to load a book.
- *    It will create a new EBookBackend if one is not already running
- *    for the requested URI.  It will call e_book_backend_add_client to
- *    add a new client to an existing EBookBackend server.
+ * 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.
  *
- * 2. A PASBook, when a client has requested an operation on the
- *    GNOME_Evolution_Addressbook_Book interface.
+ * 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/>
  *
- * Author:
- *   Nat Friedman (nat ximian com)
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  */
 
 #if !defined (__LIBEDATA_BOOK_H_INSIDE__) && !defined (LIBEDATA_BOOK_COMPILATION)
@@ -226,53 +223,131 @@ gboolean e_book_backend_is_opened        (EBookBackend *backend);
 gboolean       e_book_backend_is_readonly      (EBookBackend *backend);
 gboolean       e_book_backend_is_removed       (EBookBackend *backend);
 
+gchar *                e_book_backend_get_backend_property_sync
+                                               (EBookBackend *backend,
+                                                const gchar *prop_name,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_book_backend_get_backend_property
                                                (EBookBackend *backend,
-                                                EDataBook *book,
-                                                guint32 opid,
+                                                const gchar *prop_name,
                                                 GCancellable *cancellable,
-                                                const gchar *prop_name);
-
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gchar *                e_book_backend_get_backend_property_finish
+                                               (EBookBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+gboolean       e_book_backend_open_sync        (EBookBackend *backend,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_book_backend_open             (EBookBackend *backend,
-                                                EDataBook *book,
-                                                guint32 opid,
                                                 GCancellable *cancellable,
-                                                gboolean only_if_exists);
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_book_backend_open_finish      (EBookBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+gboolean       e_book_backend_refresh_sync     (EBookBackend *backend,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_book_backend_refresh          (EBookBackend *backend,
-                                                EDataBook *book,
-                                                guint32 opid,
-                                                GCancellable *cancellable);
+                                                GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_book_backend_refresh_finish   (EBookBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+gboolean       e_book_backend_create_contacts_sync
+                                               (EBookBackend *backend,
+                                                const gchar * const *vcards,
+                                                GQueue *out_contacts,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_book_backend_create_contacts  (EBookBackend *backend,
-                                                EDataBook *book,
-                                                guint32 opid,
+                                                const gchar * const *vcards,
                                                 GCancellable *cancellable,
-                                                const GSList *vcards);
-void           e_book_backend_remove_contacts  (EBookBackend *backend,
-                                                EDataBook *book,
-                                                guint32 opid,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_book_backend_create_contacts_finish
+                                               (EBookBackend *backend,
+                                                GAsyncResult *result,
+                                                GQueue *out_contacts,
+                                                GError **error);
+gboolean       e_book_backend_modify_contacts_sync
+                                               (EBookBackend *backend,
+                                                const gchar * const *vcards,
                                                 GCancellable *cancellable,
-                                                const GSList *id_list);
+                                                GError **error);
 void           e_book_backend_modify_contacts  (EBookBackend *backend,
-                                                EDataBook *book,
-                                                guint32 opid,
+                                                const gchar * const *vcards,
                                                 GCancellable *cancellable,
-                                                const GSList *vcards);
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_book_backend_modify_contacts_finish
+                                               (EBookBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+gboolean       e_book_backend_remove_contacts_sync
+                                               (EBookBackend *backend,
+                                                const gchar * const *uids,
+                                                GCancellable *cancellable,
+                                                GError **error);
+void           e_book_backend_remove_contacts  (EBookBackend *backend,
+                                                const gchar * const *uids,
+                                                GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_book_backend_remove_contacts_finish
+                                               (EBookBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+EContact *     e_book_backend_get_contact_sync (EBookBackend *backend,
+                                                const gchar *uid,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_book_backend_get_contact      (EBookBackend *backend,
-                                                EDataBook *book,
-                                                guint32 opid,
+                                                const gchar *uid,
                                                 GCancellable *cancellable,
-                                                const gchar *id);
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+EContact *     e_book_backend_get_contact_finish
+                                               (EBookBackend *backend,
+                                                GAsyncResult *result,
+                                                GError **error);
+gboolean       e_book_backend_get_contact_list_sync
+                                               (EBookBackend *backend,
+                                                const gchar *query,
+                                                GQueue *out_contacts,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_book_backend_get_contact_list (EBookBackend *backend,
-                                                EDataBook *book,
-                                                guint32 opid,
+                                                const gchar *query,
                                                 GCancellable *cancellable,
-                                                const gchar *query);
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_book_backend_get_contact_list_finish
+                                               (EBookBackend *backend,
+                                                GAsyncResult *result,
+                                                GQueue *out_contacts,
+                                                GError **error);
+gboolean       e_book_backend_get_contact_list_uids_sync
+                                               (EBookBackend *backend,
+                                                const gchar *query,
+                                                GQueue *out_uids,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_book_backend_get_contact_list_uids
                                                (EBookBackend *backend,
-                                                EDataBook *book,
-                                                guint32 opid,
+                                                const gchar *query,
                                                 GCancellable *cancellable,
-                                                const gchar *query);
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_book_backend_get_contact_list_uids_finish
+                                               (EBookBackend *backend,
+                                                GAsyncResult *result,
+                                                GQueue *out_uids,
+                                                GError **error);
 
 void           e_book_backend_start_view       (EBookBackend *backend,
                                                 EDataBookView *view);
@@ -298,8 +373,9 @@ void                e_book_backend_notify_property_changed
                                                 const gchar *prop_value);
 
 EDataBookDirect *
-                e_book_backend_get_direct_book  (EBookBackend *backend);
-void            e_book_backend_configure_direct (EBookBackend *backend, const gchar *config);
+               e_book_backend_get_direct_book  (EBookBackend *backend);
+void           e_book_backend_configure_direct (EBookBackend *backend,
+                                                const gchar *config);
 
 void           e_book_backend_sync             (EBookBackend *backend);
 
@@ -307,6 +383,12 @@ void               e_book_backend_sync             (EBookBackend *backend);
 void           e_book_backend_set_is_removed   (EBookBackend *backend,
                                                 gboolean is_removed);
 
+GSimpleAsyncResult *
+               e_book_backend_prepare_for_completion
+                                               (EBookBackend *backend,
+                                                guint32 opid,
+                                                GQueue **result_queue);
+
 G_END_DECLS
 
 #endif /* E_BOOK_BACKEND_H */
diff --git a/addressbook/libedata-book/e-data-book.c b/addressbook/libedata-book/e-data-book.c
index c9d66a0..5f81fe6 100644
--- a/addressbook/libedata-book/e-data-book.c
+++ b/addressbook/libedata-book/e-data-book.c
@@ -1,23 +1,19 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- * Copyright (C) 2006 OpenedHand Ltd
- * Copyright (C) 2009 Intel Corporation
+ * e-data-book.c
  *
- * This library is free software; you can redistribute it and/or modify it under
- * the terms of version 2.1 of the GNU Lesser General Public License as
- * published by the Free Software Foundation.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
  *
- * This library is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
- * details.
+ * 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.
  *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * 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/>
  *
- * Author: Ross Burton <ross linux intel com>
  */
 
 #include <unistd.h>
@@ -41,6 +37,8 @@
        (G_TYPE_INSTANCE_GET_PRIVATE \
        ((obj), E_TYPE_DATA_BOOK, EDataBookPrivate))
 
+typedef struct _AsyncContext AsyncContext;
+
 struct _EDataBookPrivate {
        GDBusConnection *connection;
        EDBusAddressBook *dbus_interface;
@@ -52,15 +50,16 @@ struct _EDataBookPrivate {
 
        gboolean opened;
 
-       GRecMutex pending_ops_lock;
-       GHashTable *pending_ops; /* opid -> OperationData */
-       GHashTable *direct_ops; /* opid to DirectOperationData for still running operations */
+       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 {
+       EDataBook *data_book;
+       EDBusAddressBook *interface;
+       GDBusMethodInvocation *invocation;
+       GCancellable *cancellable;
+       guint watcher_id;
 };
 
 enum {
@@ -70,66 +69,6 @@ enum {
        PROP_OBJECT_PATH
 };
 
-static EOperationPool *ops_pool = NULL;
-
-typedef enum {
-       OP_OPEN,
-       OP_REFRESH,
-       OP_GET_CONTACT,
-       OP_GET_CONTACTS,
-       OP_GET_CONTACTS_UIDS,
-       OP_ADD_CONTACTS,
-       OP_REMOVE_CONTACTS,
-       OP_MODIFY_CONTACTS,
-       OP_GET_BACKEND_PROPERTY,
-       OP_GET_VIEW,
-       OP_CLOSE
-} OperationID;
-
-typedef struct {
-       volatile gint ref_count;
-
-       OperationID op;
-       guint32 id; /* operation id */
-       EDataBook *book;
-       EBookBackend *backend;
-       GCancellable *cancellable;
-       GDBusMethodInvocation *invocation;
-       const gchar *sender; /* owned by invocation */
-       guint watcher_id;
-
-       union {
-               /* OP_GET_CONTACT */
-               gchar *uid;
-               /* OP_REMOVE_CONTACTS */
-               GSList *ids;
-               /* OP_ADD_CONTACTS */
-               /* OP_MODIFY_CONTACTS */
-               GSList *vcards;
-               /* OP_GET_VIEW */
-               /* OP_GET_CONTACTS */
-               /* OP_GET_CONTACTS_UIDS */
-               gchar *query;
-               /* OP_GET_BACKEND_PROPERTY */
-               const gchar *prop_name;
-
-               /* OP_REFRESH */
-               /* OP_CLOSE */
-       } d;
-} OperationData;
-
-typedef struct {
-       GAsyncReadyCallback callback;
-       gpointer            user_data;
-       GCancellable       *cancellable;
-       GSimpleAsyncResult *result;
-
-       gboolean            is_sync_call;
-       gboolean            sync_call_complete;
-       GMutex              sync_result_mutex;
-       GCond               sync_result_condition;
-} DirectOperationData;
-
 /* EModule's can never be free'd, however the use count can change
  * Here we ensure that there is only one ever created by way of
  * static variables and locks
@@ -138,20 +77,7 @@ static GHashTable *modules_table = NULL;
 G_LOCK_DEFINE (modules_table);
 
 /* Forward Declarations */
-static void                 e_data_book_initable_init  (GInitableIface *interface);
-static DirectOperationData *direct_operation_data_push (EDataBook          *book,
-                                                       OperationID         opid,
-                                                       GAsyncReadyCallback callback,
-                                                       gpointer            user_data,
-                                                       GCancellable       *cancellable,
-                                                       gpointer            source_tag,
-                                                       gboolean            sync_call);
-static void                 direct_operation_data_free (DirectOperationData *data);
-static void                 direct_operation_complete  (DirectOperationData *data);
-static void                 direct_operation_wait      (DirectOperationData *data);
-static void                 e_data_book_respond_close  (EDataBook *book,
-                                                       guint opid,
-                                                       GError *error);
+static void e_data_book_initable_init (GInitableIface *interface);
 
 G_DEFINE_TYPE_WITH_CODE (
        EDataBook,
@@ -161,315 +87,156 @@ G_DEFINE_TYPE_WITH_CODE (
                G_TYPE_INITABLE,
                e_data_book_initable_init))
 
-static EModule *
-load_module (const gchar *module_path)
-{
-       EModule *module = NULL;
-
-       G_LOCK (modules_table);
-
-       if (!modules_table)
-               modules_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-
-       module = g_hash_table_lookup (modules_table, module_path);
-
-       if (!module) {
-               module = e_module_new (module_path);
-               if (!module)
-                       g_warning ("Failed to open EModule at path: %s", module_path);
-               else
-                       g_hash_table_insert (modules_table, g_strdup (module_path), module);
-       }
-
-       G_UNLOCK (modules_table);
-
-       return module;
-}
-
-static DirectOperationData *
-direct_operation_data_push (EDataBook *book,
-                            OperationID opid,
-                            GAsyncReadyCallback callback,
-                            gpointer user_data,
-                            GCancellable *cancellable,
-                            gpointer source_tag,
-                            gboolean sync_call)
-{
-       DirectOperationData *data;
-
-       data = g_slice_new (DirectOperationData);
-       data->callback = callback;
-       data->user_data = user_data;
-       data->cancellable = g_object_ref (cancellable);
-       data->result = g_simple_async_result_new (
-               G_OBJECT (book),
-               data->callback,
-               data->user_data,
-               source_tag);
-       data->is_sync_call = sync_call;
-       data->sync_call_complete = FALSE;
-
-       if (data->is_sync_call) {
-               g_mutex_init (&data->sync_result_mutex);
-               g_cond_init (&data->sync_result_condition);
-       }
-
-       g_hash_table_insert (book->priv->direct_ops, GUINT_TO_POINTER (opid), data);
-
-       return data;
-}
-
 static void
-direct_operation_data_free (DirectOperationData *data)
+sender_vanished_cb (GDBusConnection *connection,
+                    const gchar *sender,
+                    GCancellable *cancellable)
 {
-       if (data) {
-               if (data->is_sync_call) {
-                       g_mutex_clear (&data->sync_result_mutex);
-                       g_cond_clear (&data->sync_result_condition);
-               }
-
-               g_object_unref (data->result);
-               g_object_unref (data->cancellable);
-               g_slice_free (DirectOperationData, data);
-       }
-}
-
-static void
-direct_operation_complete (DirectOperationData *data)
-{
-       /* If it was a sync call, we have the calling thread
-        * waiting on the sync call condition, it's up to
-        * the sync call to free the data with direct_operation_data_free().
-        *
-        * Otherwise for async calls we need to complete
-        * in the calling thread.
-        */
-       if (data->is_sync_call) {
-               g_mutex_lock (&data->sync_result_mutex);
-               data->sync_call_complete = TRUE;
-               g_cond_signal (&data->sync_result_condition);
-               g_mutex_unlock (&data->sync_result_mutex);
-       } else {
-               g_simple_async_result_complete_in_idle (data->result);
-               direct_operation_data_free (data);
-       }
+       g_cancellable_cancel (cancellable);
 }
 
 static void
-direct_operation_wait (DirectOperationData *data)
-{
-       g_mutex_lock (&data->sync_result_mutex);
-       while (data->sync_call_complete == FALSE)
-               g_cond_wait (&data->sync_result_condition, &data->sync_result_mutex);
-       g_mutex_unlock (&data->sync_result_mutex);
-}
-
-static gchar *
-construct_bookview_path (void)
+sender_table_insert (EDataBook *data_book,
+                     const gchar *sender,
+                     GCancellable *cancellable)
 {
-       static volatile gint counter = 1;
+       GHashTable *sender_table;
+       GPtrArray *array;
 
-       g_atomic_int_inc (&counter);
+       g_return_if_fail (sender != NULL);
 
-       return g_strdup_printf (
-               "/org/gnome/evolution/dataserver/AddressBookView/%d/%d",
-               getpid (), counter);
-}
+       g_mutex_lock (&data_book->priv->sender_lock);
 
-static void
-op_sender_vanished_cb (GDBusConnection *connection,
-                       const gchar *sender,
-                       GCancellable *cancellable)
-{
-       g_cancellable_cancel (cancellable);
-}
+       sender_table = data_book->priv->sender_table;
+       array = g_hash_table_lookup (sender_table, sender);
 
-static OperationData *
-op_ref (OperationData *data)
-{
-       g_return_val_if_fail (data != NULL, data);
-       g_return_val_if_fail (data->ref_count > 0, data);
+       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_atomic_int_inc (&data->ref_count);
+       g_ptr_array_add (array, g_object_ref (cancellable));
 
-       return data;
+       g_mutex_unlock (&data_book->priv->sender_lock);
 }
 
-static OperationData *
-op_new (OperationID op,
-        EDataBook *book,
-        EBookBackend *backend,
-        GDBusMethodInvocation *invocation)
+static gboolean
+sender_table_remove (EDataBook *data_book,
+                     const gchar *sender,
+                     GCancellable *cancellable)
 {
-       OperationData *data;
+       GHashTable *sender_table;
+       GPtrArray *array;
+       gboolean removed = FALSE;
 
-       data = g_slice_new0 (OperationData);
-       data->ref_count = 1;
-       data->op = op;
-       data->id = e_operation_pool_reserve_opid (ops_pool);
-       data->book = g_object_ref (book);
-       data->backend = g_object_ref (backend);
-       data->cancellable = g_cancellable_new ();
+       g_return_val_if_fail (sender != NULL, FALSE);
 
-       /* This is optional so we can fake client requests. */
-       if (invocation != NULL) {
-               GDBusConnection *connection;
+       g_mutex_lock (&data_book->priv->sender_lock);
 
-               data->invocation = g_object_ref (invocation);
-               data->sender = g_dbus_method_invocation_get_sender (invocation);
+       sender_table = data_book->priv->sender_table;
+       array = g_hash_table_lookup (sender_table, sender);
 
-               connection = e_data_book_get_connection (book);
+       if (array != NULL) {
+               removed = g_ptr_array_remove_fast (array, cancellable);
 
-               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),
-                       (GDestroyNotify) g_object_unref);
+               if (array->len == 0)
+                       g_hash_table_remove (sender_table, sender);
        }
 
-       g_rec_mutex_lock (&book->priv->pending_ops_lock);
-       g_hash_table_insert (
-               book->priv->pending_ops,
-               GUINT_TO_POINTER (data->id),
-               op_ref (data));
-       g_rec_mutex_unlock (&book->priv->pending_ops_lock);
+       g_mutex_unlock (&data_book->priv->sender_lock);
 
-       return data;
+       return removed;
 }
 
-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_CONTACT:
-                               g_free (data->d.uid);
-                               break;
-                       case OP_REMOVE_CONTACTS:
-                               g_slist_free_full (
-                                       data->d.ids,
-                                       (GDestroyNotify) g_free);
-                               break;
-                       case OP_ADD_CONTACTS:
-                       case OP_MODIFY_CONTACTS:
-                               g_slist_free_full (
-                                       data->d.vcards,
-                                       (GDestroyNotify) g_free);
-                               break;
-                       case OP_GET_VIEW:
-                       case OP_GET_CONTACTS:
-                       case OP_GET_CONTACTS_UIDS:
-                               g_free (data->d.query);
-                               break;
-                       default:
-                               break;
-               }
+static AsyncContext *
+async_context_new (EDataBook *data_book,
+                   GDBusMethodInvocation *invocation)
+{
+       AsyncContext *async_context;
+       EDBusAddressBook *interface;
 
-               g_object_unref (data->book);
-               g_object_unref (data->backend);
-               g_object_unref (data->cancellable);
+       interface = data_book->priv->dbus_interface;
 
-               if (data->invocation != NULL)
-                       g_object_unref (data->invocation);
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->data_book = g_object_ref (data_book);
+       async_context->interface = g_object_ref (interface);
+       async_context->invocation = g_object_ref (invocation);
+       async_context->cancellable = g_cancellable_new ();
 
-               if (data->watcher_id > 0)
-                       g_bus_unwatch_name (data->watcher_id);
+       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_unref);
 
-               g_slice_free (OperationData, data);
-       }
+       sender_table_insert (
+               async_context->data_book,
+               g_dbus_method_invocation_get_sender (invocation),
+               async_context->cancellable);
+
+       return async_context;
 }
 
 static void
-op_dispatch (EDataBook *book,
-             OperationData *data)
+async_context_free (AsyncContext *async_context)
 {
-       g_mutex_lock (&book->priv->open_lock);
+       sender_table_remove (
+               async_context->data_book,
+               g_dbus_method_invocation_get_sender (
+                       async_context->invocation),
+               async_context->cancellable);
 
-       /* If an open operation is currently in progress, queue this
-        * operation to be dispatched when the open operation finishes. */
-       if (book->priv->open_opid > 0) {
-               g_queue_push_tail (&book->priv->open_queue, data);
-       } else {
-               if (data->op == OP_OPEN)
-                       book->priv->open_opid = data->id;
-               e_operation_pool_push (ops_pool, data);
-       }
+       g_clear_object (&async_context->data_book);
+       g_clear_object (&async_context->interface);
+       g_clear_object (&async_context->invocation);
+       g_clear_object (&async_context->cancellable);
+
+       if (async_context->watcher_id > 0)
+               g_bus_unwatch_name (async_context->watcher_id);
 
-       g_mutex_unlock (&book->priv->open_lock);
+       g_slice_free (AsyncContext, async_context);
 }
 
-static OperationData *
-op_claim (EDataBook *book,
-          guint32 opid,
-          DirectOperationData **direct)
+static EModule *
+load_module (const gchar *module_path)
 {
-       OperationData *data;
-       DirectOperationData *direct_data;
+       EModule *module = NULL;
 
-       g_return_val_if_fail (E_IS_DATA_BOOK (book), NULL);
+       G_LOCK (modules_table);
 
-       e_operation_pool_release_opid (ops_pool, opid);
-
-       g_rec_mutex_lock (&book->priv->pending_ops_lock);
-       data = g_hash_table_lookup (
-               book->priv->pending_ops,
-               GUINT_TO_POINTER (opid));
-       if (data != NULL) {
-               /* Steal the hash table's reference. */
-               g_hash_table_steal (
-                       book->priv->pending_ops,
-                       GUINT_TO_POINTER (opid));
-       }
+       if (!modules_table)
+               modules_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
-       direct_data = g_hash_table_lookup (
-               book->priv->direct_ops,
-               GUINT_TO_POINTER (opid));
-       if (direct_data != NULL) {
-               g_hash_table_steal (
-                       book->priv->direct_ops,
-                       GUINT_TO_POINTER (opid));
+       module = g_hash_table_lookup (modules_table, module_path);
+
+       if (!module) {
+               module = e_module_new (module_path);
+               if (!module)
+                       g_warning ("Failed to open EModule at path: %s", module_path);
+               else
+                       g_hash_table_insert (modules_table, g_strdup (module_path), module);
        }
-       g_rec_mutex_unlock (&book->priv->pending_ops_lock);
 
-       g_warn_if_fail (direct_data == NULL || direct != NULL);
-       if (direct)
-               *direct = direct_data;
+       G_UNLOCK (modules_table);
 
-       return data;
+       return module;
 }
 
-static DirectOperationData *
-op_complete (EDataBook *book,
-             guint32 opid)
+static gchar *
+construct_bookview_path (void)
 {
-       DirectOperationData *direct_data;
-
-       g_return_val_if_fail (E_IS_DATA_BOOK (book), NULL);
-
-       e_operation_pool_release_opid (ops_pool, opid);
-
-       g_rec_mutex_lock (&book->priv->pending_ops_lock);
-       g_hash_table_remove (
-               book->priv->pending_ops,
-               GUINT_TO_POINTER (opid));
+       static volatile gint counter = 1;
 
-       direct_data = g_hash_table_lookup (
-               book->priv->direct_ops,
-               GUINT_TO_POINTER (opid));
-       if (direct_data != NULL) {
-               g_hash_table_steal (
-                       book->priv->direct_ops,
-                       GUINT_TO_POINTER (opid));
-       }
-       g_rec_mutex_unlock (&book->priv->pending_ops_lock);
+       g_atomic_int_inc (&counter);
 
-       return direct_data;
+       return g_strdup_printf (
+               "/org/gnome/evolution/dataserver/AddressBookView/%d/%d",
+               getpid (), counter);
 }
 
 static void
@@ -593,207 +360,6 @@ data_book_convert_to_client_error (GError *error)
        }
 }
 
-static void
-cancel_operations_for_sender (EDataBook *book,
-                              const gchar *sender)
-{
-       GHashTableIter iter;
-       gpointer value;
-
-       g_return_if_fail (sender != NULL);
-
-       g_rec_mutex_lock (&book->priv->pending_ops_lock);
-
-       g_hash_table_iter_init (&iter, book->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 (&book->priv->pending_ops_lock);
-}
-
-static void
-operation_thread (gpointer data,
-                  gpointer user_data)
-{
-       OperationData *op = data;
-
-       switch (op->op) {
-       case OP_OPEN:
-               e_book_backend_open (
-                       op->backend, op->book, op->id,
-                       op->cancellable, FALSE);
-               break;
-
-       case OP_ADD_CONTACTS:
-               e_book_backend_create_contacts (
-                       op->backend, op->book, op->id,
-                       op->cancellable, op->d.vcards);
-               break;
-
-       case OP_GET_CONTACT:
-               e_book_backend_get_contact (
-                       op->backend, op->book, op->id,
-                       op->cancellable, op->d.uid);
-               break;
-
-       case OP_GET_CONTACTS:
-               e_book_backend_get_contact_list (
-                       op->backend, op->book, op->id,
-                       op->cancellable, op->d.query);
-               break;
-
-       case OP_GET_CONTACTS_UIDS:
-               e_book_backend_get_contact_list_uids (
-                       op->backend, op->book, op->id,
-                       op->cancellable, op->d.query);
-               break;
-
-       case OP_MODIFY_CONTACTS:
-               e_book_backend_modify_contacts (
-                       op->backend, op->book, op->id,
-                       op->cancellable, op->d.vcards);
-               break;
-
-       case OP_REMOVE_CONTACTS:
-               e_book_backend_remove_contacts (
-                       op->backend, op->book, op->id,
-                       op->cancellable, op->d.ids);
-               break;
-
-       case OP_REFRESH:
-               e_book_backend_refresh (
-                       op->backend, op->book, op->id, op->cancellable);
-               break;
-
-       case OP_GET_BACKEND_PROPERTY:
-               e_book_backend_get_backend_property (
-                       op->backend, op->book, op->id,
-                       op->cancellable, op->d.prop_name);
-               break;
-
-       case OP_GET_VIEW:
-               if (op->d.query) {
-                       EDataBookView *view;
-                       EBookBackendSExp *card_sexp;
-                       GDBusConnection *connection;
-                       gchar *object_path;
-                       GError *error = NULL;
-
-                       card_sexp = e_book_backend_sexp_new (op->d.query);
-                       if (!card_sexp) {
-                               g_dbus_method_invocation_return_error_literal (
-                                       op->invocation,
-                                       E_CLIENT_ERROR,
-                                       E_CLIENT_ERROR_INVALID_QUERY,
-                                       _("Invalid query"));
-
-                               op_complete (op->book, op->id);
-                               break;
-                       }
-
-                       object_path = construct_bookview_path ();
-                       connection = e_data_book_get_connection (op->book);
-
-                       view = e_data_book_view_new (
-                               op->backend, card_sexp,
-                               connection, object_path, &error);
-
-                       g_object_unref (card_sexp);
-
-                       /* Sanity check. */
-                       g_return_if_fail (
-                               ((view != NULL) && (error == NULL)) ||
-                               ((view == NULL) && (error != NULL)));
-
-                       if (error != NULL) {
-                               /* Translators: This is prefix to a detailed error message */
-                               g_prefix_error (&error, "%s", _("Invalid query: "));
-                               data_book_convert_to_client_error (error);
-                               g_dbus_method_invocation_take_error (
-                                       op->invocation, error);
-
-                               op_complete (op->book, op->id);
-                               g_free (object_path);
-                               break;
-                       }
-
-                       e_book_backend_add_view (op->backend, view);
-
-                       e_dbus_address_book_complete_get_view (
-                               op->book->priv->dbus_interface,
-                               op->invocation,
-                               object_path);
-
-                       op_complete (op->book, op->id);
-
-                       g_object_unref (view);
-                       g_free (object_path);
-               }
-               break;
-
-       case OP_CLOSE:
-               if (op->sender != NULL)
-                       cancel_operations_for_sender (op->book, op->sender);
-
-               if (op->book->priv->dbus_interface != NULL)
-                       e_dbus_address_book_complete_close (
-                               op->book->priv->dbus_interface,
-                               op->invocation);
-
-               /* Let direct calls return, notify the direct callers that it's closed */
-               e_data_book_respond_close (op->book, op->id, NULL);
-               break;
-       }
-
-       op_unref (op);
-}
-
-static OperationData *
-op_direct_new (OperationID op,
-               EDataBook *book,
-               EBookBackend *backend,
-               GCancellable *cancellable,
-               GAsyncReadyCallback callback,
-               gpointer user_data,
-               gpointer source_tag,
-               gboolean sync_call,
-               DirectOperationData **ret_data)
-{
-       OperationData *data;
-       DirectOperationData *direct_data;
-
-       data = g_slice_new0 (OperationData);
-       data->ref_count = 1;
-       data->op = op;
-       data->book = g_object_ref (book);
-       data->backend = g_object_ref (backend);
-       data->id = e_operation_pool_reserve_opid (ops_pool);
-
-       if (cancellable)
-               data->cancellable = g_object_ref (cancellable);
-       else
-               data->cancellable = g_cancellable_new ();
-
-       g_rec_mutex_lock (&book->priv->pending_ops_lock);
-       g_hash_table_insert (
-               book->priv->pending_ops,
-               GUINT_TO_POINTER (data->id),
-               op_ref (data));
-       direct_data = direct_operation_data_push (
-               book, data->id, callback, user_data,
-               data->cancellable, source_tag, sync_call);
-       g_rec_mutex_unlock (&book->priv->pending_ops_lock);
-
-       if (ret_data)
-               *ret_data = direct_data;
-
-       return data;
-}
-
 /**
  * e_data_book_status_to_string:
  *
@@ -970,176 +536,457 @@ e_data_book_string_slist_to_comma_string (const GSList *strings)
        return res;
 }
 
+static void
+data_book_complete_open_cb (GObject *source_object,
+                            GAsyncResult *result,
+                            gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GError *error = NULL;
+
+       e_book_backend_open_finish (
+               E_BOOK_BACKEND (source_object), result, &error);
+
+       if (error == NULL) {
+               e_dbus_address_book_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_book_handle_open_cb (EDBusAddressBook *interface,
                           GDBusMethodInvocation *invocation,
-                          EDataBook *book)
+                          EDataBook *data_book)
 {
-       OperationData *op;
        EBookBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_book_ref_backend (book);
+       backend = e_data_book_ref_backend (data_book);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_OPEN, book, backend, invocation);
+       async_context = async_context_new (data_book, invocation);
 
-       op_dispatch (book, op);
+       e_book_backend_open (
+               backend,
+               async_context->cancellable,
+               data_book_complete_open_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_book_complete_refresh_cb (GObject *source_object,
+                               GAsyncResult *result,
+                               gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GError *error = NULL;
+
+       e_book_backend_refresh_finish (
+               E_BOOK_BACKEND (source_object), result, &error);
+
+       if (error == NULL) {
+               e_dbus_address_book_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_book_handle_refresh_cb (EDBusAddressBook *interface,
                              GDBusMethodInvocation *invocation,
-                             EDataBook *book)
+                             EDataBook *data_book)
 {
-       OperationData *op;
        EBookBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_book_ref_backend (book);
+       backend = e_data_book_ref_backend (data_book);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_REFRESH, book, backend, invocation);
+       async_context = async_context_new (data_book, invocation);
 
-       op_dispatch (book, op);
+       e_book_backend_refresh (
+               backend,
+               async_context->cancellable,
+               data_book_complete_refresh_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_book_complete_get_contact_cb (GObject *source_object,
+                                   GAsyncResult *result,
+                                   gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       EContact *contact;
+       GError *error = NULL;
+
+       contact = e_book_backend_get_contact_finish (
+               E_BOOK_BACKEND (source_object), result, &error);
+
+       /* Sanity check. */
+       g_return_if_fail (
+               ((contact != NULL) && (error == NULL)) ||
+               ((contact == NULL) && (error != NULL)));
+
+       if (contact != NULL) {
+               gchar *vcard;
+               gchar *utf8_vcard;
+
+               vcard = e_vcard_to_string (
+                       E_VCARD (contact),
+                       EVC_FORMAT_VCARD_30);
+               utf8_vcard = e_util_utf8_make_valid (vcard);
+               e_dbus_address_book_complete_get_contact (
+                       async_context->interface,
+                       async_context->invocation,
+                       utf8_vcard);
+               g_free (utf8_vcard);
+               g_free (vcard);
+
+               g_object_unref (contact);
+       } else {
+               g_dbus_method_invocation_take_error (
+                       async_context->invocation, error);
+       }
+
+       async_context_free (async_context);
+}
+
 static gboolean
 data_book_handle_get_contact_cb (EDBusAddressBook *interface,
                                  GDBusMethodInvocation *invocation,
                                  const gchar *in_uid,
-                                 EDataBook *book)
+                                 EDataBook *data_book)
 {
-       OperationData *op;
        EBookBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_book_ref_backend (book);
+       backend = e_data_book_ref_backend (data_book);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_GET_CONTACT, book, backend, invocation);
-       op->d.uid = g_strdup (in_uid);
+       async_context = async_context_new (data_book, invocation);
 
-       op_dispatch (book, op);
+       e_book_backend_get_contact (
+               backend, in_uid,
+               async_context->cancellable,
+               data_book_complete_get_contact_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_book_complete_get_contact_list_cb (GObject *source_object,
+                                        GAsyncResult *result,
+                                        gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GQueue queue = G_QUEUE_INIT;
+       GError *error = NULL;
+
+       e_book_backend_get_contact_list_finish (
+               E_BOOK_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)) {
+                       EContact *contact;
+                       gchar *vcard;
+
+                       contact = g_queue_pop_head (&queue);
+
+                       vcard = e_vcard_to_string (
+                               E_VCARD (contact),
+                               EVC_FORMAT_VCARD_30);
+                       strv[ii++] = e_util_utf8_make_valid (vcard);
+                       g_free (vcard);
+
+                       g_object_unref (contact);
+               }
+
+               e_dbus_address_book_complete_get_contact_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_book_handle_get_contact_list_cb (EDBusAddressBook *interface,
                                       GDBusMethodInvocation *invocation,
                                       const gchar *in_query,
-                                      EDataBook *book)
+                                      EDataBook *data_book)
 {
-       OperationData *op;
        EBookBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_book_ref_backend (book);
+       backend = e_data_book_ref_backend (data_book);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_GET_CONTACTS, book, backend, invocation);
-       op->d.query = g_strdup (in_query);
+       async_context = async_context_new (data_book, invocation);
 
-       op_dispatch (book, op);
+       e_book_backend_get_contact_list (
+               backend, in_query,
+               async_context->cancellable,
+               data_book_complete_get_contact_list_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_book_complete_get_contact_list_uids_cb (GObject *source_object,
+                                             GAsyncResult *result,
+                                             gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GQueue queue = G_QUEUE_INIT;
+       GError *error = NULL;
+
+       e_book_backend_get_contact_list_uids_finish (
+               E_BOOK_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 = g_queue_pop_head (&queue);
+                       strv[ii++] = e_util_utf8_make_valid (uid);
+                       g_free (uid);
+               }
+
+               e_dbus_address_book_complete_get_contact_list_uids (
+                       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_book_handle_get_contact_list_uids_cb (EDBusAddressBook *interface,
                                            GDBusMethodInvocation *invocation,
                                            const gchar *in_query,
-                                           EDataBook *book)
+                                           EDataBook *data_book)
 {
-       OperationData *op;
        EBookBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_book_ref_backend (book);
+       backend = e_data_book_ref_backend (data_book);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_GET_CONTACTS_UIDS, book, backend, invocation);
-       op->d.query = g_strdup (in_query);
+       async_context = async_context_new (data_book, invocation);
 
-       op_dispatch (book, op);
+       e_book_backend_get_contact_list_uids (
+               backend, in_query,
+               async_context->cancellable,
+               data_book_complete_get_contact_list_uids_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_book_complete_create_contacts_cb (GObject *source_object,
+                                       GAsyncResult *result,
+                                       gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GQueue queue = G_QUEUE_INIT;
+       GError *error = NULL;
+
+       e_book_backend_create_contacts_finish (
+               E_BOOK_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)) {
+                       EContact *contact;
+                       const gchar *uid;
+
+                       contact = g_queue_pop_head (&queue);
+                       uid = e_contact_get_const (contact, E_CONTACT_UID);
+                       strv[ii++] = e_util_utf8_make_valid (uid);
+               }
+
+               e_dbus_address_book_complete_create_contacts (
+                       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_book_handle_create_contacts_cb (EDBusAddressBook *interface,
                                      GDBusMethodInvocation *invocation,
                                      const gchar * const *in_vcards,
-                                     EDataBook *book)
+                                     EDataBook *data_book)
 {
-       OperationData *op;
        EBookBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_book_ref_backend (book);
+       backend = e_data_book_ref_backend (data_book);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_ADD_CONTACTS, book, backend, invocation);
-       op->d.vcards = e_util_strv_to_slist (in_vcards);
+       async_context = async_context_new (data_book, invocation);
 
-       op_dispatch (book, op);
+       e_book_backend_create_contacts (
+               backend, in_vcards,
+               async_context->cancellable,
+               data_book_complete_create_contacts_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_book_complete_modify_contacts_cb (GObject *source_object,
+                                       GAsyncResult *result,
+                                       gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GError *error = NULL;
+
+       e_book_backend_modify_contacts_finish (
+               E_BOOK_BACKEND (source_object), result, &error);
+
+       if (error == NULL) {
+               e_dbus_address_book_complete_modify_contacts (
+                       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_book_handle_modify_contacts_cb (EDBusAddressBook *interface,
                                      GDBusMethodInvocation *invocation,
                                      const gchar * const *in_vcards,
-                                     EDataBook *book)
+                                     EDataBook *data_book)
 {
-       OperationData *op;
        EBookBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_book_ref_backend (book);
+       backend = e_data_book_ref_backend (data_book);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_MODIFY_CONTACTS, book, backend, invocation);
-       op->d.vcards = e_util_strv_to_slist (in_vcards);
+       async_context = async_context_new (data_book, invocation);
 
-       op_dispatch (book, op);
+       e_book_backend_modify_contacts (
+               backend, in_vcards,
+               async_context->cancellable,
+               data_book_complete_modify_contacts_cb,
+               async_context);
 
        g_object_unref (backend);
 
        return TRUE;
 }
 
+static void
+data_book_complete_remove_contacts_cb (GObject *source_object,
+                                       GAsyncResult *result,
+                                       gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       GError *error = NULL;
+
+       e_book_backend_remove_contacts_finish (
+               E_BOOK_BACKEND (source_object), result, &error);
+
+       if (error == NULL) {
+               e_dbus_address_book_complete_remove_contacts (
+                       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_book_handle_remove_contacts_cb (EDBusAddressBook *interface,
                                      GDBusMethodInvocation *invocation,
                                      const gchar * const *in_uids,
-                                     EDataBook *book)
+                                     EDataBook *data_book)
 {
-       OperationData *op;
        EBookBackend *backend;
+       AsyncContext *async_context;
 
-       backend = e_data_book_ref_backend (book);
+       backend = e_data_book_ref_backend (data_book);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_REMOVE_CONTACTS, book, backend, invocation);
+       async_context = async_context_new (data_book, invocation);
 
-       /* Allow an empty array to be removed */
-       for (; in_uids && *in_uids; in_uids++) {
-               op->d.ids = g_slist_prepend (op->d.ids, g_strdup (*in_uids));
-       }
-
-       op_dispatch (book, op);
+       e_book_backend_remove_contacts (
+               backend, in_uids,
+               async_context->cancellable,
+               data_book_complete_remove_contacts_cb,
+               async_context);
 
        g_object_unref (backend);
 
@@ -1150,19 +997,54 @@ static gboolean
 data_book_handle_get_view_cb (EDBusAddressBook *interface,
                               GDBusMethodInvocation *invocation,
                               const gchar *in_query,
-                              EDataBook *book)
+                              EDataBook *data_book)
 {
-       OperationData *op;
        EBookBackend *backend;
+       EDataBookView *view;
+       EBookBackendSExp *sexp;
+       GDBusConnection *connection;
+       gchar *object_path;
+       GError *error = NULL;
 
-       backend = e_data_book_ref_backend (book);
+       backend = e_data_book_ref_backend (data_book);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_new (OP_GET_VIEW, book, backend, invocation);
-       op->d.query = g_strdup (in_query);
+       sexp = e_book_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_bookview_path ();
+       connection = g_dbus_method_invocation_get_connection (invocation);
 
-       /* This operation is never queued. */
-       e_operation_pool_push (ops_pool, op);
+       view = e_data_book_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_address_book_complete_get_view (
+                       interface, invocation, object_path);
+               e_book_backend_add_view (backend, view);
+               g_object_unref (view);
+       } else {
+               data_book_convert_to_client_error (error);
+               g_prefix_error (&error, "%s", _("Invalid query: "));
+               g_dbus_method_invocation_take_error (invocation, error);
+       }
+
+       g_free (object_path);
 
        g_object_unref (backend);
 
@@ -1172,19 +1054,15 @@ data_book_handle_get_view_cb (EDBusAddressBook *interface,
 static gboolean
 data_book_handle_close_cb (EDBusAddressBook *interface,
                            GDBusMethodInvocation *invocation,
-                           EDataBook *book)
+                           EDataBook *data_book)
 {
-       OperationData *op;
        EBookBackend *backend;
        const gchar *sender;
 
-       backend = e_data_book_ref_backend (book);
-       g_return_val_if_fail (backend != NULL, FALSE);
-
-       op = op_new (OP_CLOSE, book, backend, invocation);
+       e_dbus_address_book_complete_close (interface, invocation);
 
-       /* This operation is never queued. */
-       e_operation_pool_push (ops_pool, op);
+       backend = e_data_book_ref_backend (data_book);
+       g_return_val_if_fail (backend != NULL, FALSE);
 
        sender = g_dbus_method_invocation_get_sender (invocation);
        g_signal_emit_by_name (backend, "closed", sender);
@@ -1199,66 +1077,31 @@ e_data_book_respond_open (EDataBook *book,
                           guint opid,
                           GError *error)
 {
-       DirectOperationData *direct = NULL;
-       OperationData *data;
+       EBookBackend *backend;
+       GSimpleAsyncResult *simple;
 
        g_return_if_fail (E_IS_DATA_BOOK (book));
 
-       data = op_claim (book, opid, &direct);
-       g_return_if_fail (data != NULL);
+       backend = e_data_book_ref_backend (book);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_book_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 book: "));
 
        book->priv->opened = (error == NULL);
 
-       if (direct) {
-               gboolean result = FALSE;
-
-               if (error) {
-                       data_book_convert_to_client_error (error);
-                       g_simple_async_result_take_error (
-                               direct->result, error);
-               } else {
-                       g_simple_async_result_set_check_cancellable (
-                               direct->result,
-                               direct->cancellable);
-
-                       if (!g_cancellable_is_cancelled (direct->cancellable))
-                               result = TRUE;
-               }
-
-               g_simple_async_result_set_op_res_gboolean (direct->result, result);
-
-               /* Deliver the result to the caller */
-               direct_operation_complete (direct);
-       } else if (error == NULL) {
-               e_dbus_address_book_complete_open (
-                       book->priv->dbus_interface,
-                       data->invocation);
-       } else {
+       if (error != NULL) {
                data_book_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);
-
-       /* Dispatch any pending operations. */
-       g_mutex_lock (&book->priv->open_lock);
-
-       if (opid == book->priv->open_opid) {
-               OperationData *op;
-
-               book->priv->open_opid = 0;
-
-               while (!g_queue_is_empty (&book->priv->open_queue)) {
-                       op = g_queue_pop_head (&book->priv->open_queue);
-                       e_operation_pool_push (ops_pool, op);
-               }
-       }
+       g_simple_async_result_complete_in_idle (simple);
 
-       g_mutex_unlock (&book->priv->open_lock);
+       g_object_unref (simple);
+       g_object_unref (backend);
 }
 
 /**
@@ -1275,27 +1118,29 @@ e_data_book_respond_refresh (EDataBook *book,
                              guint32 opid,
                              GError *error)
 {
-       OperationData *data;
+       EBookBackend *backend;
+       GSimpleAsyncResult *simple;
 
        g_return_if_fail (E_IS_DATA_BOOK (book));
 
-       data = op_claim (book, opid, NULL);
-       g_return_if_fail (data != NULL);
+       backend = e_data_book_ref_backend (book);
+       g_return_if_fail (backend != NULL);
 
-       /* Translators: This is prefix to a detailed error message */
-       g_prefix_error (&error, "%s", _("Cannot refresh address book: "));
+       simple = e_book_backend_prepare_for_completion (backend, opid, NULL);
+       g_return_if_fail (simple);
 
-       if (error == NULL) {
-               e_dbus_address_book_complete_refresh (
-                       book->priv->dbus_interface,
-                       data->invocation);
-       } else {
+       /* Translators: This is prefix to a detailed error message */
+       g_prefix_error (&error, "%s", _("Cannot refresh address book: "));
+
+       if (error != NULL) {
                data_book_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);
 }
 
 /**
@@ -1311,24 +1156,32 @@ e_data_book_respond_get_backend_property (EDataBook *book,
                                           GError *error,
                                           const gchar *prop_value)
 {
-       OperationData *data;
+       EBookBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_BOOK (book));
 
-       data = op_claim (book, opid, NULL);
-       g_return_if_fail (data != NULL);
+       backend = e_data_book_ref_backend (book);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_book_backend_prepare_for_completion (backend, opid, &queue);
+       g_return_if_fail (simple != NULL);
+       g_return_if_fail (queue != NULL);
 
        if (error == NULL) {
-               e_data_book_report_backend_property_changed (
-                       book, 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);
 }
 
 void
@@ -1337,64 +1190,37 @@ e_data_book_respond_get_contact (EDataBook *book,
                                  GError *error,
                                  const gchar *vcard)
 {
-       DirectOperationData *direct = NULL;
-       OperationData *data;
+       EBookBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_BOOK (book));
 
-       data = op_claim (book, opid, &direct);
-       g_return_if_fail (data != NULL);
-
-       if (error) {
-               /* Translators: This is prefix to a detailed error message */
-               g_prefix_error (&error, "%s", _("Cannot get contact: "));
-               data_book_convert_to_client_error (error);
-       }
-
-       if (direct) {
-
-               if (error) {
-                       g_simple_async_result_set_error (
-                               direct->result,
-                               error->domain,
-                               error->code,
-                               "%s", error->message);
-                       g_error_free (error);
-               } else {
-                       g_simple_async_result_set_check_cancellable (
-                               direct->result,
-                               direct->cancellable);
-
-                       if (!g_cancellable_is_cancelled (direct->cancellable)) {
-                               EContact *contact;
-
-                               contact = e_contact_new_from_vcard (vcard);
-
-                               /* Give it an EContact for the return value */
-                               g_simple_async_result_set_op_res_gpointer (direct->result, contact, 
g_object_unref);
-                       }
-               }
-
-               /* Deliver the result to the caller */
-               direct_operation_complete (direct);
+       backend = e_data_book_ref_backend (book);
+       g_return_if_fail (backend != NULL);
 
-       } else if (error == NULL) {
-               gchar *utf8_vcard;
+       simple = e_book_backend_prepare_for_completion (backend, opid, &queue);
+       g_return_if_fail (simple != NULL);
+       g_return_if_fail (queue != NULL);
 
-               utf8_vcard = e_util_utf8_make_valid (vcard);
+       /* Translators: This is prefix to a detailed error message */
+       g_prefix_error (&error, "%s", _("Cannot get contact: "));
 
-               e_dbus_address_book_complete_get_contact (
-                       book->priv->dbus_interface,
-                       data->invocation,
-                       utf8_vcard);
+       if (error == NULL) {
+               EContact *contact;
 
-               g_free (utf8_vcard);
+               contact = e_contact_new_from_vcard (vcard);
+               g_queue_push_tail (queue, g_object_ref (contact));
+               g_object_unref (contact);
        } else {
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               data_book_convert_to_client_error (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);
 }
 
 void
@@ -1403,83 +1229,44 @@ e_data_book_respond_get_contact_list (EDataBook *book,
                                       GError *error,
                                       const GSList *cards)
 {
-       DirectOperationData *direct = NULL;
-       OperationData *data;
+       EBookBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_BOOK (book));
 
-       data = op_claim (book, opid, &direct);
-       g_return_if_fail (data != NULL);
+       backend = e_data_book_ref_backend (book);
+       g_return_if_fail (backend != NULL);
 
-       if (error) {
-               /* Translators: This is prefix to a detailed error message */
-               g_prefix_error (&error, "%s", _("Cannot get contact list: "));
-               data_book_convert_to_client_error (error);
-       }
+       simple = e_book_backend_prepare_for_completion (backend, opid, &queue);
+       g_return_if_fail (simple != NULL);
+       g_return_if_fail (queue != NULL);
 
-       if (direct) {
-
-               if (error) {
-                       g_simple_async_result_set_error (
-                               direct->result,
-                               error->domain,
-                               error->code,
-                               "%s", error->message);
-                       g_error_free (error);
-               } else {
-                       g_simple_async_result_set_check_cancellable (
-                               direct->result,
-                               direct->cancellable);
-
-                       if (!g_cancellable_is_cancelled (direct->cancellable)) {
-                               EContact *contact;
-                               const GSList *l;
-                               GSList *contacts = NULL;
-
-                               for (l = cards; l; l = l->next) {
-                                       const gchar *vcard = l->data;
-
-                                       contact = e_contact_new_from_vcard (vcard);
-                                       contacts = g_slist_prepend (contacts, contact);
-                               }
-
-                               contacts = g_slist_reverse (contacts);
-
-                               /* Give it an EContact for the return value */
-                               g_simple_async_result_set_op_res_gpointer (
-                                       direct->result, contacts,
-                                       (GDestroyNotify) e_util_free_object_slist);
-                       }
-               }
+       /* Translators: This is prefix to a detailed error message */
+       g_prefix_error (&error, "%s", _("Cannot get contact list: "));
 
-               /* Deliver the result to the caller */
-               direct_operation_complete (direct);
+       if (error == NULL) {
+               GSList *list, *link;
 
-       } else if (error == NULL) {
-               gchar **strv;
-               guint length;
-               gint ii = 0;
+               list = (GSList *) cards;
 
-               length = g_slist_length ((GSList *) cards);
-               strv = g_new0 (gchar *, length + 1);
+               for (link = list; link != NULL; link = g_slist_next (link)) {
+                       EContact *contact;
 
-               while (cards != NULL) {
-                       strv[ii++] = e_util_utf8_make_valid (cards->data);
-                       cards = g_slist_next ((GSList *) cards);
+                       contact = e_contact_new_from_vcard (link->data);
+                       g_queue_push_tail (queue, g_object_ref (contact));
+                       g_object_unref (contact);
                }
 
-               e_dbus_address_book_complete_get_contact_list (
-                       book->priv->dbus_interface,
-                       data->invocation,
-                       (const gchar * const *) strv);
-
-               g_strfreev (strv);
        } else {
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               data_book_convert_to_client_error (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);
 }
 
 /**
@@ -1495,73 +1282,39 @@ e_data_book_respond_get_contact_list_uids (EDataBook *book,
                                            GError *error,
                                            const GSList *uids)
 {
-       DirectOperationData *direct = NULL;
-       OperationData *data;
+       EBookBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_BOOK (book));
 
-       data = op_claim (book, opid, &direct);
-       g_return_if_fail (data != NULL);
-
-       if (error) {
-               /* Translators: This is prefix to a detailed error message */
-               g_prefix_error (&error, "%s", _("Cannot get contact list uids: "));
-               data_book_convert_to_client_error (error);
-       }
-
-       if (direct) {
-
-               if (error) {
-                       g_simple_async_result_set_error (
-                               direct->result,
-                               error->domain,
-                               error->code,
-                               "%s", error->message);
-                       g_error_free (error);
-               } else {
-                       g_simple_async_result_set_check_cancellable (
-                               direct->result,
-                               direct->cancellable);
-
-                       if (!g_cancellable_is_cancelled (direct->cancellable)) {
-                               GSList *ret_uids = NULL;
-
-                               ret_uids = e_util_copy_string_slist (NULL, uids);
-
-                               g_simple_async_result_set_op_res_gpointer (
-                                       direct->result, ret_uids,
-                                       (GDestroyNotify) e_util_free_string_slist);
-                       }
-               }
+       backend = e_data_book_ref_backend (book);
+       g_return_if_fail (backend != NULL);
 
-               /* Deliver the result to the caller */
-               direct_operation_complete (direct);
+       simple = e_book_backend_prepare_for_completion (backend, opid, &queue);
+       g_return_if_fail (simple != NULL);
+       g_return_if_fail (queue != NULL);
 
-       } else if (error == NULL) {
-               gchar **strv;
-               guint length;
-               gint ii = 0;
+       /* Translators: This is prefix to a detailed error message */
+       g_prefix_error (&error, "%s", _("Cannot get contact list uids: "));
 
-               length = g_slist_length ((GSList *) uids);
-               strv = g_new0 (gchar *, length + 1);
+       if (error == NULL) {
+               GSList *list, *link;
 
-               while (uids != NULL) {
-                       strv[ii++] = e_util_utf8_make_valid (uids->data);
-                       uids = g_slist_next ((GSList *) uids);
-               }
+               list = (GSList *) uids;
 
-               e_dbus_address_book_complete_get_contact_list_uids (
-                       book->priv->dbus_interface,
-                       data->invocation,
-                       (const gchar * const *) strv);
+               for (link = list; link != NULL; link = g_slist_next (link))
+                       g_queue_push_tail (queue, g_strdup (link->data));
 
-               g_strfreev (strv);
        } else {
-               g_dbus_method_invocation_take_error (
-                       data->invocation, error);
+               data_book_convert_to_client_error (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);
 }
 
 /**
@@ -1577,52 +1330,41 @@ e_data_book_respond_create_contacts (EDataBook *book,
                                      GError *error,
                                      const GSList *contacts)
 {
-       OperationData *data;
+       EBookBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_BOOK (book));
 
-       data = op_claim (book, opid, NULL);
-       g_return_if_fail (data != NULL);
+       backend = e_data_book_ref_backend (book);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_book_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 add contact: "));
 
        if (error == NULL) {
-               gchar **strv;
-               guint length;
-               gint ii = 0;
+               GSList *list, *link;
 
-               length = g_slist_length ((GSList *) contacts);
-               strv = g_new0 (gchar *, length + 1);
+               list = (GSList *) contacts;
 
-               while (contacts != NULL) {
-                       EContact *contact = E_CONTACT (contacts->data);
-                       const gchar *uid;
-
-                       uid = e_contact_get_const (contact, E_CONTACT_UID);
-                       strv[ii++] = e_util_utf8_make_valid (uid);
-
-                       e_book_backend_notify_update (data->backend, contact);
-
-                       contacts = g_slist_next ((GSList *) contacts);
+               for (link = list; link != NULL; link = g_slist_next (link)) {
+                       EContact *contact = E_CONTACT (link->data);
+                       g_queue_push_tail (queue, g_object_ref (contact));
                }
 
-               e_dbus_address_book_complete_create_contacts (
-                       book->priv->dbus_interface,
-                       data->invocation,
-                       (const gchar * const *) strv);
-
-               e_book_backend_notify_complete (data->backend);
-
-               g_strfreev (strv);
-
        } else {
                data_book_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);
 }
 
 /**
@@ -1638,36 +1380,41 @@ e_data_book_respond_modify_contacts (EDataBook *book,
                                      GError *error,
                                      const GSList *contacts)
 {
-       OperationData *data;
+       EBookBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_BOOK (book));
 
-       data = op_claim (book, opid, NULL);
-       g_return_if_fail (data != NULL);
+       backend = e_data_book_ref_backend (book);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_book_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 contacts: "));
 
        if (error == NULL) {
-               e_dbus_address_book_complete_modify_contacts (
-                       book->priv->dbus_interface,
-                       data->invocation);
+               GSList *list, *link;
+
+               list = (GSList *) contacts;
 
-               while (contacts != NULL) {
+               for (link = list; link != NULL; link = g_slist_next (link)) {
                        EContact *contact = E_CONTACT (contacts->data);
-                       e_book_backend_notify_update (data->backend, contact);
-                       contacts = g_slist_next ((GSList *) contacts);
+                       g_queue_push_tail (queue, g_object_ref (contact));
                }
 
-               e_book_backend_notify_complete (data->backend);
-
        } else {
                data_book_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);
 }
 
 void
@@ -1676,35 +1423,39 @@ e_data_book_respond_remove_contacts (EDataBook *book,
                                      GError *error,
                                      const GSList *ids)
 {
-       OperationData *data;
+       EBookBackend *backend;
+       GSimpleAsyncResult *simple;
+       GQueue *queue = NULL;
 
        g_return_if_fail (E_IS_DATA_BOOK (book));
 
-       data = op_claim (book, opid, NULL);
-       g_return_if_fail (data != NULL);
+       backend = e_data_book_ref_backend (book);
+       g_return_if_fail (backend != NULL);
+
+       simple = e_book_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 contacts: "));
 
        if (error == NULL) {
-               e_dbus_address_book_complete_remove_contacts (
-                       book->priv->dbus_interface,
-                       data->invocation);
+               GSList *list, *link;
 
-               while (ids != NULL) {
-                       e_book_backend_notify_remove (data->backend, ids->data);
-                       ids = g_slist_next ((GSList *) ids);
-               }
+               list = (GSList *) ids;
 
-               e_book_backend_notify_complete (data->backend);
+               for (link = list; link != NULL; link = g_slist_next (link))
+                       g_queue_push_tail (queue, g_strdup (link->data));
 
        } else {
                data_book_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,6 +1645,8 @@ data_book_dispose (GObject *object)
                priv->direct_module = NULL;
        }
 
+       g_hash_table_remove_all (priv->sender_table);
+
        /* Chain up to parent's dispose() metnod. */
        G_OBJECT_CLASS (e_data_book_parent_class)->dispose (object);
 }
@@ -1907,27 +1660,14 @@ data_book_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);
-       if (priv->direct_ops) {
-               g_hash_table_destroy (priv->direct_ops);
-               priv->direct_ops = NULL;
-       }
+       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_book_parent_class)->finalize (object);
 }
@@ -1939,17 +1679,18 @@ data_book_initable_init (GInitable *initable,
 {
        EBookBackend *backend;
        EDataBook *book;
-       OperationData *op;
        gboolean success = TRUE;
 
        book = E_DATA_BOOK (initable);
 
        backend = e_data_book_ref_backend (book);
 
-       if (book->priv->connection != NULL && book->priv->object_path != NULL) {
+       /* Attach ourselves to the EBookBackend. */
+       e_book_backend_set_data_book (backend, book);
 
-               /* Attach ourselves to the EBookBackend. */
-               e_book_backend_set_data_book (backend, book);
+       if (book->priv->connection != NULL && book->priv->object_path != NULL) {
+               const gchar *prop_name;
+               gchar *prop_value;
 
                book->priv->dbus_interface =
                        e_dbus_address_book_skeleton_new ();
@@ -2015,37 +1756,35 @@ data_book_initable_init (GInitable *initable,
                        book->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 EBookBackend's antiquated API. */
-
-               op = op_new (OP_GET_BACKEND_PROPERTY, book, backend, NULL);
-               op->d.prop_name = CLIENT_BACKEND_PROPERTY_CAPABILITIES;
-               e_book_backend_get_backend_property (
-                       backend, book, op->id,
-                       op->cancellable, op->d.prop_name);
-               op_unref (op);
-
-               op = op_new (OP_GET_BACKEND_PROPERTY, book, backend, NULL);
-               op->d.prop_name = CLIENT_BACKEND_PROPERTY_REVISION;
-               e_book_backend_get_backend_property (
-                       backend, book, op->id,
-                       op->cancellable, op->d.prop_name);
-               op_unref (op);
-
-               op = op_new (OP_GET_BACKEND_PROPERTY, book, backend, NULL);
-               op->d.prop_name = BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS;
-               e_book_backend_get_backend_property (
-                       backend, book, op->id,
-                       op->cancellable, op->d.prop_name);
-               op_unref (op);
-
-               op = op_new (OP_GET_BACKEND_PROPERTY, book, backend, NULL);
-               op->d.prop_name = BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS;
-               e_book_backend_get_backend_property (
-                       backend, book, 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_book_backend_get_backend_property_sync (
+                       backend, prop_name, NULL, NULL);
+               e_data_book_report_backend_property_changed (
+                       book, prop_name, prop_value);
+               g_free (prop_value);
+
+               prop_name = CLIENT_BACKEND_PROPERTY_REVISION;
+               prop_value = e_book_backend_get_backend_property_sync (
+                       backend, prop_name, NULL, NULL);
+               e_data_book_report_backend_property_changed (
+                       book, prop_name, prop_value);
+               g_free (prop_value);
+
+               prop_name = BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS;
+               prop_value = e_book_backend_get_backend_property_sync (
+                       backend, prop_name, NULL, NULL);
+               e_data_book_report_backend_property_changed (
+                       book, prop_name, prop_value);
+               g_free (prop_value);
+
+               prop_name = BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS;
+               prop_value = e_book_backend_get_backend_property_sync (
+                       backend, prop_name, NULL, NULL);
+               e_data_book_report_backend_property_changed (
+                       book, prop_name, prop_value);
+               g_free (prop_value);
 
                success = g_dbus_interface_skeleton_export (
                        G_DBUS_INTERFACE_SKELETON (book->priv->dbus_interface),
@@ -2110,9 +1849,6 @@ e_data_book_class_init (EDataBookClass *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
@@ -2122,20 +1858,17 @@ e_data_book_initable_init (GInitableIface *interface)
 }
 
 static void
-e_data_book_init (EDataBook *ebook)
+e_data_book_init (EDataBook *data_book)
 {
-       ebook->priv = E_DATA_BOOK_GET_PRIVATE (ebook);
+       data_book->priv = E_DATA_BOOK_GET_PRIVATE (data_book);
 
-       ebook->priv->pending_ops = g_hash_table_new_full (
-               (GHashFunc) g_direct_hash,
-               (GEqualFunc) g_direct_equal,
-               (GDestroyNotify) NULL,
-               (GDestroyNotify) op_unref);
-       ebook->priv->direct_ops = g_hash_table_new_full (
-               g_direct_hash, g_direct_equal, NULL,
-               (GDestroyNotify) direct_operation_data_free);
-       g_rec_mutex_init (&ebook->priv->pending_ops_lock);
-       g_mutex_init (&ebook->priv->open_lock);
+       g_mutex_init (&data_book->priv->sender_lock);
+
+       data_book->priv->sender_table = g_hash_table_new_full (
+               (GHashFunc) g_str_hash,
+               (GEqualFunc) g_str_equal,
+               (GDestroyNotify) g_free,
+               (GDestroyNotify) g_ptr_array_unref);
 }
 
 /**
@@ -2262,7 +1995,6 @@ e_data_book_new_direct (ESourceRegistry *registry,
        book = g_initable_new (
                E_TYPE_DATA_BOOK, NULL, error,
                "backend", backend, NULL);
-       e_book_backend_set_data_book (backend, book);
 
        if (!book) {
                g_type_module_unuse (G_TYPE_MODULE (module));
@@ -2366,22 +2098,6 @@ e_data_book_is_opened (EDataBook *book)
  *                        Direct Read Access APIs                        *
  *************************************************************************/
 
-static gboolean
-e_data_book_open_finish (EDataBook *book,
-                         GAsyncResult *result,
-                         GError **error)
-{
-       gboolean res;
-
-       g_return_val_if_fail (E_IS_DATA_BOOK (book), FALSE);
-       g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
-
-       res = g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result));
-       g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
-
-       return res;
-}
-
 /**
  * e_data_book_open_sync:
  * @book: an #EDataBook
@@ -2403,64 +2119,18 @@ e_data_book_open_sync (EDataBook *book,
                        GError **error)
 {
        EBookBackend *backend;
-       DirectOperationData *data = NULL;
-       OperationData *op;
-       gboolean result = FALSE;
+       gboolean success;
 
        g_return_val_if_fail (E_IS_DATA_BOOK (book), FALSE);
 
        backend = e_data_book_ref_backend (book);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_direct_new (OP_OPEN, book, backend, cancellable, NULL, NULL, e_data_book_open_sync, TRUE, 
&data);
-
-       op_dispatch (book, op);
-
-       direct_operation_wait (data);
-       result = e_data_book_open_finish (book, G_ASYNC_RESULT (data->result), error);
-       direct_operation_data_free (data);
+       success = e_book_backend_open_sync (backend, cancellable, error);
 
        g_object_unref (backend);
 
-       return result;
-}
-
-static void
-e_data_book_respond_close (EDataBook *book,
-                           guint opid,
-                           GError *error)
-{
-       DirectOperationData *data;
-
-       data = op_complete (book, opid);
-
-       if (data) {
-               gboolean result = FALSE;
-
-               if (error)
-                       g_simple_async_result_set_error (
-                               data->result,
-                               error->domain,
-                               error->code,
-                               "%s", error->message);
-
-               else {
-                       if (!g_cancellable_is_cancelled (data->cancellable))
-                               result = TRUE;
-
-                       g_simple_async_result_set_check_cancellable (
-                               data->result,
-                               data->cancellable);
-               }
-
-               g_simple_async_result_set_op_res_gboolean (data->result, result);
-
-               /* Deliver the result to the caller */
-               direct_operation_complete (data);
-       }
-
-       if (error)
-               g_error_free (error);
+       return success;
 }
 
 /**
@@ -2484,20 +2154,21 @@ e_data_book_close (EDataBook *book,
                    GAsyncReadyCallback callback,
                    gpointer user_data)
 {
-       EBookBackend *backend;
-       OperationData *op;
+       GSimpleAsyncResult *simple;
 
        g_return_if_fail (E_IS_DATA_BOOK (book));
 
-       backend = e_data_book_ref_backend (book);
-       g_return_if_fail (backend != NULL);
+       simple = g_simple_async_result_new (
+               G_OBJECT (book), callback,
+               user_data, e_data_book_close);
 
-       op = op_direct_new (OP_CLOSE, book, backend, cancellable, callback, user_data, e_data_book_close, 
FALSE, NULL);
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
 
-       /* This operation is never queued. */
-       e_operation_pool_push (ops_pool, op);
+       /* XXX This operation has no GDBusMethodInvocation,
+        *     so there's nothing to do. */
+       g_simple_async_result_complete_in_idle (simple);
 
-       g_object_unref (backend);
+       g_object_unref (simple);
 }
 
 /**
@@ -2518,15 +2189,16 @@ e_data_book_close_finish (EDataBook *book,
                           GAsyncResult *result,
                           GError **error)
 {
-       gboolean res;
+       GSimpleAsyncResult *simple;
 
-       g_return_val_if_fail (E_IS_DATA_BOOK (book), FALSE);
-       g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (book), e_data_book_close), FALSE);
 
-       res = g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result));
-       g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+       simple = G_SIMPLE_ASYNC_RESULT (result);
 
-       return res;
+       /* Assume success unless a GError is set. */
+       return !g_simple_async_result_propagate_error (simple, error);
 }
 
 /**
@@ -2549,28 +2221,43 @@ e_data_book_close_sync (EDataBook *book,
                         GCancellable *cancellable,
                         GError **error)
 {
-       EBookBackend *backend;
-       DirectOperationData *data = NULL;
-       OperationData *op;
-       gboolean result = FALSE;
+       /* XXX This operation has no GDBusMethodInvocation,
+        *     so there's nothing to do. */
+       return TRUE;
+}
 
-       g_return_val_if_fail (E_IS_DATA_BOOK (book), FALSE);
+static void
+data_book_get_contact_cb (GObject *source_object,
+                          GAsyncResult *result,
+                          gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       EContact *contact;
+       GError *error = NULL;
 
-       backend = e_data_book_ref_backend (book);
-       g_return_val_if_fail (backend != NULL, FALSE);
+       simple = G_SIMPLE_ASYNC_RESULT (user_data);
 
-       op = op_direct_new (OP_CLOSE, book, backend, cancellable, NULL, NULL, e_data_book_close_sync, TRUE, 
&data);
+       contact = e_book_backend_get_contact_finish (
+               E_BOOK_BACKEND (source_object), result, &error);
 
-       /* This operation is never queued. */
-       e_operation_pool_push (ops_pool, op);
+       /* Sanity check. */
+       g_return_if_fail (
+               ((contact != NULL) && (error == NULL)) ||
+               ((contact == NULL) && (error != NULL)));
 
-       direct_operation_wait (data);
-       result = e_data_book_close_finish (book, G_ASYNC_RESULT (data->result), error);
-       direct_operation_data_free (data);
+       if (contact != NULL) {
+               g_simple_async_result_set_op_res_gpointer (
+                       simple, g_object_ref (contact),
+                       (GDestroyNotify) g_object_unref);
+               g_object_unref (contact);
+       }
 
-       g_object_unref (backend);
+       if (error != NULL)
+               g_simple_async_result_take_error (simple, error);
 
-       return result;
+       g_simple_async_result_complete (simple);
+
+       g_object_unref (simple);
 }
 
 /**
@@ -2599,7 +2286,7 @@ e_data_book_get_contact (EDataBook *book,
                          gpointer user_data)
 {
        EBookBackend *backend;
-       OperationData *op;
+       GSimpleAsyncResult *simple;
 
        g_return_if_fail (E_IS_DATA_BOOK (book));
        g_return_if_fail (uid && uid[0]);
@@ -2607,11 +2294,16 @@ e_data_book_get_contact (EDataBook *book,
        backend = e_data_book_ref_backend (book);
        g_return_if_fail (backend != NULL);
 
-       op = op_direct_new (OP_GET_CONTACT, book, backend, cancellable, callback, user_data, 
e_data_book_get_contact, FALSE, NULL);
-       op->d.uid = g_strdup (uid);
+       simple = g_simple_async_result_new (
+               G_OBJECT (book), callback,
+               user_data, e_data_book_get_contact);
 
-       op_dispatch (book, op);
+       e_book_backend_get_contact (
+               backend, uid, cancellable,
+               data_book_get_contact_cb,
+               g_object_ref (simple));
 
+       g_object_unref (simple);
        g_object_unref (backend);
 }
 
@@ -2635,22 +2327,24 @@ e_data_book_get_contact_finish (EDataBook *book,
                                 EContact **contact,
                                 GError **error)
 {
+       GSimpleAsyncResult *simple;
        EContact *ret_contact;
 
        g_return_val_if_fail (E_IS_DATA_BOOK (book), FALSE);
        g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
 
-       ret_contact = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
-       g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+       ret_contact = g_simple_async_result_get_op_res_gpointer (simple);
 
-       if (contact) {
-               if (ret_contact)
-                       *contact = g_object_ref (ret_contact);
-               else
-                       *contact = NULL;
-       }
+       if (g_simple_async_result_propagate_error (simple, error))
+               return FALSE;
+
+       g_return_val_if_fail (ret_contact != NULL, FALSE);
 
-       return ret_contact != NULL;
+       if (contact != NULL)
+               *contact = g_object_ref (ret_contact);
+
+       return TRUE;
 }
 
 /**
@@ -2678,9 +2372,8 @@ e_data_book_get_contact_sync (EDataBook *book,
                               GError **error)
 {
        EBookBackend *backend;
-       DirectOperationData *data = NULL;
-       OperationData *op;
-       gboolean result = FALSE;
+       EContact *ret_contact;
+       gboolean success = FALSE;
 
        g_return_val_if_fail (E_IS_DATA_BOOK (book), FALSE);
        g_return_val_if_fail (uid && uid[0], FALSE);
@@ -2688,18 +2381,57 @@ e_data_book_get_contact_sync (EDataBook *book,
        backend = e_data_book_ref_backend (book);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_direct_new (OP_GET_CONTACT, book, backend, cancellable, NULL, NULL, 
e_data_book_get_contact_sync, TRUE, &data);
-       op->d.uid = g_strdup (uid);
+       ret_contact = e_book_backend_get_contact_sync (
+               backend, uid, cancellable, error);
+
+       if (ret_contact != NULL) {
+               if (contact != NULL)
+                       *contact = g_object_ref (ret_contact);
 
-       op_dispatch (book, op);
+               g_object_unref (ret_contact);
 
-       direct_operation_wait (data);
-       result = e_data_book_get_contact_finish (book, G_ASYNC_RESULT (data->result), contact, error);
-       direct_operation_data_free (data);
+               success = TRUE;
+       }
 
        g_object_unref (backend);
 
-       return result;
+       return success;
+}
+
+static void
+data_book_get_contacts_cb (GObject *source_object,
+                           GAsyncResult *result,
+                           gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       GQueue queue = G_QUEUE_INIT;
+       GError *error = NULL;
+
+       simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+       e_book_backend_get_contact_list_finish (
+               E_BOOK_BACKEND (source_object), result, &queue, &error);
+
+       if (error == NULL) {
+               GSList *list = NULL;
+
+               while (!g_queue_is_empty (&queue)) {
+                       EContact *contact;
+
+                       contact = g_queue_pop_tail (&queue);
+                       list = g_slist_prepend (list, contact);
+               }
+
+               /* XXX This assumes the finish() function will be called. */
+               g_simple_async_result_set_op_res_gpointer (
+                       simple, list, (GDestroyNotify) NULL);
+       } else {
+               g_simple_async_result_take_error (simple, error);
+       }
+
+       g_simple_async_result_complete (simple);
+
+       g_object_unref (simple);
 }
 
 /**
@@ -2728,18 +2460,25 @@ e_data_book_get_contacts (EDataBook *book,
                           gpointer user_data)
 {
        EBookBackend *backend;
-       OperationData *op;
+       GSimpleAsyncResult *simple;
 
        g_return_if_fail (E_IS_DATA_BOOK (book));
 
        backend = e_data_book_ref_backend (book);
        g_return_if_fail (backend != NULL);
 
-       op = op_direct_new (OP_GET_CONTACTS, book, backend, cancellable, callback, user_data, 
e_data_book_get_contacts, FALSE, NULL);
-       op->d.query = g_strdup (sexp);
+       simple = g_simple_async_result_new (
+               G_OBJECT (book), callback, user_data,
+               e_data_book_get_contacts);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
 
-       op_dispatch (book, op);
+       e_book_backend_get_contact_list (
+               backend, sexp, cancellable,
+               data_book_get_contacts_cb,
+               g_object_ref (simple));
 
+       g_object_unref (simple);
        g_object_unref (backend);
 }
 
@@ -2763,26 +2502,26 @@ e_data_book_get_contacts_finish (EDataBook *book,
                                  GSList **contacts,
                                  GError **error)
 {
-       GSList *ret_contacts;
-
-       g_return_val_if_fail (E_IS_DATA_BOOK (book), FALSE);
-       g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+       GSimpleAsyncResult *simple;
+       GSList *list;
 
-       ret_contacts = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (book),
+               e_data_book_get_contacts), FALSE);
 
-       if (contacts) {
-               if (ret_contacts)
-                       *contacts = e_util_copy_object_slist (NULL, ret_contacts);
-               else
-                       *contacts = NULL;
-       }
+       simple = G_SIMPLE_ASYNC_RESULT (result);
 
-       /* If there was an error, the return is FALSE, otherwise
-        * the call was successfull even if no results were found
-        */
-       if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+       if (g_simple_async_result_propagate_error (simple, error))
                return FALSE;
 
+       list = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (contacts != NULL)
+               *contacts = list;
+       else
+               g_slist_free_full (list, (GDestroyNotify) g_object_unref);
+
        return TRUE;
 }
 
@@ -2812,27 +2551,65 @@ e_data_book_get_contacts_sync (EDataBook *book,
                                GError **error)
 {
        EBookBackend *backend;
-       DirectOperationData *data = NULL;
-       OperationData *op;
-       gboolean result = FALSE;
+       GQueue queue = G_QUEUE_INIT;
+       gboolean success;
 
        g_return_val_if_fail (E_IS_DATA_BOOK (book), FALSE);
 
        backend = e_data_book_ref_backend (book);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_direct_new (OP_GET_CONTACTS, book, backend, cancellable, NULL, NULL, 
e_data_book_get_contacts_sync, TRUE, &data);
-       op->d.query = g_strdup (sexp);
+       success = e_book_backend_get_contact_list_sync (
+               backend, sexp, &queue, cancellable, error);
 
-       op_dispatch (book, op);
-
-       direct_operation_wait (data);
-       result = e_data_book_get_contacts_finish (book, G_ASYNC_RESULT (data->result), contacts, error);
-       direct_operation_data_free (data);
+       if (contacts != NULL) {
+               while (!g_queue_is_empty (&queue))
+                       *contacts = g_slist_prepend (
+                               *contacts, g_queue_pop_tail (&queue));
+       } else {
+               while (!g_queue_is_empty (&queue))
+                       g_object_unref (g_queue_pop_head (&queue));
+       }
 
        g_object_unref (backend);
 
-       return result;
+       return success;
+}
+
+static void
+data_book_get_contacts_uids_cb (GObject *source_object,
+                                GAsyncResult *result,
+                                gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+       GQueue queue = G_QUEUE_INIT;
+       GError *error = NULL;
+
+       simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+       e_book_backend_get_contact_list_uids_finish (
+               E_BOOK_BACKEND (source_object), result, &queue, &error);
+
+       if (error == NULL) {
+               GSList *list = NULL;
+
+               while (!g_queue_is_empty (&queue)) {
+                       gchar *uid;
+
+                       uid = g_queue_pop_tail (&queue);
+                       list = g_slist_prepend (list, uid);
+               }
+
+               /* XXX This assumes the finish() function will be called. */
+               g_simple_async_result_set_op_res_gpointer (
+                       simple, list, (GDestroyNotify) NULL);
+       } else {
+               g_simple_async_result_take_error (simple, error);
+       }
+
+       g_simple_async_result_complete (simple);
+
+       g_object_unref (simple);
 }
 
 /**
@@ -2861,18 +2638,25 @@ e_data_book_get_contacts_uids (EDataBook *book,
                                gpointer user_data)
 {
        EBookBackend *backend;
-       OperationData *op;
+       GSimpleAsyncResult *simple;
 
        g_return_if_fail (E_IS_DATA_BOOK (book));
 
        backend = e_data_book_ref_backend (book);
        g_return_if_fail (backend != NULL);
 
-       op = op_direct_new (OP_GET_CONTACTS_UIDS, book, backend, cancellable, callback, user_data, 
e_data_book_get_contacts_uids, FALSE, NULL);
-       op->d.query = g_strdup (sexp);
+       simple = g_simple_async_result_new (
+               G_OBJECT (book), callback, user_data,
+               e_data_book_get_contacts_uids);
+
+       g_simple_async_result_set_check_cancellable (simple, cancellable);
 
-       op_dispatch (book, op);
+       e_book_backend_get_contact_list_uids (
+               backend, sexp, cancellable,
+               data_book_get_contacts_uids_cb,
+               g_object_ref (simple));
 
+       g_object_unref (simple);
        g_object_unref (backend);
 }
 
@@ -2896,26 +2680,26 @@ e_data_book_get_contacts_uids_finish (EDataBook *book,
                                       GSList **contacts_uids,
                                       GError **error)
 {
-       GSList *ret_uids;
+       GSimpleAsyncResult *simple;
+       GSList *list;
 
-       g_return_val_if_fail (E_IS_DATA_BOOK (book), FALSE);
-       g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+       g_return_val_if_fail (
+               g_simple_async_result_is_valid (
+               result, G_OBJECT (book),
+               e_data_book_get_contacts_uids), FALSE);
 
-       ret_uids = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
-
-       if (contacts_uids) {
-               if (ret_uids)
-                       *contacts_uids = e_util_copy_string_slist (NULL, ret_uids);
-               else
-                       *contacts_uids = NULL;
-       }
+       simple = G_SIMPLE_ASYNC_RESULT (result);
 
-       /* If there was an error, the return is FALSE, otherwise
-        * the call was successfull even if no results were found
-        */
-       if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+       if (g_simple_async_result_propagate_error (simple, error))
                return FALSE;
 
+       list = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (contacts_uids != NULL)
+               *contacts_uids = list;
+       else
+               g_slist_free_full (list, (GDestroyNotify) g_free);
+
        return TRUE;
 }
 
@@ -2945,25 +2729,27 @@ e_data_book_get_contacts_uids_sync (EDataBook *book,
                                     GError **error)
 {
        EBookBackend *backend;
-       DirectOperationData *data = NULL;
-       OperationData *op;
-       gboolean result = FALSE;
+       GQueue queue = G_QUEUE_INIT;
+       gboolean success;
 
        g_return_val_if_fail (E_IS_DATA_BOOK (book), FALSE);
 
        backend = e_data_book_ref_backend (book);
        g_return_val_if_fail (backend != NULL, FALSE);
 
-       op = op_direct_new (OP_GET_CONTACTS_UIDS, book, backend, cancellable, NULL, NULL, 
e_data_book_get_contacts_uids_sync, TRUE, &data);
-       op->d.query = g_strdup (sexp);
+       success = e_book_backend_get_contact_list_uids_sync (
+               backend, sexp, &queue, cancellable, error);
 
-       op_dispatch (book, op);
-
-       direct_operation_wait (data);
-       result = e_data_book_get_contacts_uids_finish (book, G_ASYNC_RESULT (data->result), contacts_uids, 
error);
-       direct_operation_data_free (data);
+       if (contacts_uids != NULL) {
+               while (!g_queue_is_empty (&queue))
+                       *contacts_uids = g_slist_prepend (
+                               *contacts_uids, g_queue_pop_tail (&queue));
+       } else {
+               while (!g_queue_is_empty (&queue))
+                       g_free (g_queue_pop_head (&queue));
+       }
 
        g_object_unref (backend);
 
-       return result;
+       return success;
 }
diff --git a/addressbook/libedata-book/e-data-book.h b/addressbook/libedata-book/e-data-book.h
index 836d96e..289048d 100644
--- a/addressbook/libedata-book/e-data-book.h
+++ b/addressbook/libedata-book/e-data-book.h
@@ -1,23 +1,19 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- * Copyright (C) 2006 OpenedHand Ltd
- * Copyright (C) 2009 Intel Corporation
+ * e-data-book.h
  *
- * This library is free software; you can redistribute it and/or modify it under
- * the terms of version 2.1 of the GNU Lesser General Public License as
- * published by the Free Software Foundation.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
  *
- * This library is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
- * details.
+ * 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.
  *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * 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/>
  *
- * Author: Ross Burton <ross linux intel com>
  */
 
 #if !defined (__LIBEDATA_BOOK_H_INSIDE__) && !defined (LIBEDATA_BOOK_COMPILATION)
diff --git a/configure.ac b/configure.ac
index 6112c33..1e1ac78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -118,7 +118,7 @@ LIBEDATACAL_CURRENT=20
 LIBEDATACAL_REVISION=0
 LIBEDATACAL_AGE=0
 
-LIBEDATABOOK_CURRENT=18
+LIBEDATABOOK_CURRENT=19
 LIBEDATABOOK_REVISION=0
 LIBEDATABOOK_AGE=0
 
diff --git a/docs/reference/addressbook/libedata-book/libedata-book-sections.txt 
b/docs/reference/addressbook/libedata-book/libedata-book-sections.txt
index 78e8f48..eb8405b 100644
--- a/docs/reference/addressbook/libedata-book/libedata-book-sections.txt
+++ b/docs/reference/addressbook/libedata-book/libedata-book-sections.txt
@@ -19,15 +19,33 @@ e_book_backend_set_writable
 e_book_backend_is_opened
 e_book_backend_is_readonly
 e_book_backend_is_removed
+e_book_backend_get_backend_property_sync
 e_book_backend_get_backend_property
+e_book_backend_get_backend_property_finish
+e_book_backend_open_sync
 e_book_backend_open
+e_book_backend_open_finish
+e_book_backend_refresh_sync
 e_book_backend_refresh
+e_book_backend_refresh_finish
+e_book_backend_create_contacts_sync
 e_book_backend_create_contacts
-e_book_backend_remove_contacts
+e_book_backend_create_contacts_finish
+e_book_backend_modify_contacts_sync
 e_book_backend_modify_contacts
+e_book_backend_modify_contacts_finish
+e_book_backend_remove_contacts_sync
+e_book_backend_remove_contacts
+e_book_backend_remove_contacts_finish
+e_book_backend_get_contact_sync
 e_book_backend_get_contact
+e_book_backend_get_contact_finish
+e_book_backend_get_contact_list_sync
 e_book_backend_get_contact_list
+e_book_backend_get_contact_list_finish
+e_book_backend_get_contact_list_uids_sync
 e_book_backend_get_contact_list_uids
+e_book_backend_get_contact_list_uids_finish
 e_book_backend_start_view
 e_book_backend_stop_view
 e_book_backend_add_view
@@ -53,6 +71,7 @@ EBookBackendClass
 e_book_backend_get_type
 <SUBSECTION Private>
 EBookBackendPrivate
+e_book_backend_prepare_for_completion
 </SECTION>
 
 <SECTION>


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