#include <glib/gi18n-lib.h>
#include <libedataserver/libedataserver.h>
+#include <libedata-book/libedata-book.h>
#include "e-book-client.h"
#include "e-book-client-view.h"
GDBusProxy *dbus_proxy;
GDBusConnection *connection;
gchar *object_path;
- gboolean running;
+ guint running : 1;
+ guint complete : 1;
+
+ EDataBook *direct_book;
gulong objects_added_handler_id;
gulong objects_modified_handler_id;
PROP_0,
PROP_CLIENT,
PROP_CONNECTION,
- PROP_OBJECT_PATH
+ PROP_OBJECT_PATH,
+ PROP_DIRECT_BOOK
};
enum {
G_TYPE_INITABLE,
e_book_client_view_initable_init))
+typedef struct {
+ EBookClientView *view;
+ guint signum;
+} NotificationData;
+
+static gchar *
+direct_contacts_query (const gchar * const *uids)
+{
+ EBookQuery *query, **qs;
+ gchar *sexp;
+ gint i, len;
+
+ len = g_strv_length ((gchar **)uids);
+ qs = g_new0 (EBookQuery *, len);
+
+ for (i = 0; uids[i] != NULL; i++) {
+ const gchar *uid = uids[i];
+
+ qs[i] = e_book_query_field_test (E_CONTACT_UID, E_BOOK_QUERY_IS, uid);
+ }
+
+ query = e_book_query_or (len, qs, TRUE);
+ sexp = e_book_query_to_string (query);
+ e_book_query_unref (query);
+
+ return sexp;
+}
+
+static void
+direct_contacts_ready (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ NotificationData *data = (NotificationData *)user_data;
+ GSList *contacts = NULL;
+ GError *error = NULL;
+
+ if (!e_data_book_get_contacts_finish (E_DATA_BOOK (source_object),
+ res, &contacts, &error)) {
+ g_warning ("Error fetching contacts directly: %s\n", error->message);
+ g_error_free (error);
+ } else {
+ g_signal_emit (data->view, data->signum, 0, contacts);
+ }
+
+ g_slist_free_full (contacts, (GDestroyNotify) g_object_unref);
+ g_object_unref (data->view);
+ g_slice_free (NotificationData, data);
+}
+
+static void
+direct_contacts_fetch (EBookClientView *view,
+ const gchar * const *uids,
+ guint signum)
+{
+ NotificationData *data;
+ gchar *sexp = direct_contacts_query (uids);
+
+ /* Until the view has completely loaded, we need to make
+ * sync calls to the backend
+ */
+ if (!view->priv->complete) {
+ GSList *contacts = NULL;
+ GError *error = NULL;
+
+ if (!e_data_book_get_contacts_sync (view->priv->direct_book,
+ sexp, &contacts, NULL, &error)) {
+ g_warning ("Error fetching contacts directly: %s\n", error->message);
+ g_error_free (error);
+ } else {
+ g_signal_emit (view, signum, 0, contacts);
+ g_slist_free_full (contacts, (GDestroyNotify) g_object_unref);
+ }
+
+ } else {
+ /* Make async calls, avoid blocking the thread owning the view
+ * as much as possible
+ */
+ data = g_slice_new (NotificationData);
+ data->view = g_object_ref (view);
+ data->signum = signum;
+
+ e_data_book_get_contacts (view->priv->direct_book,
+ sexp, NULL, direct_contacts_ready, data);
+ }
+
+ g_free (sexp);
+}
+
static void
book_client_view_objects_added_cb (EGdbusBookView *object,
const gchar * const *vcards,
if (!view->priv->running)
return;
+ /* array contains UIDs only */
+ if (view->priv->direct_book) {
+ direct_contacts_fetch (view, vcards, signals[OBJECTS_ADDED]);
+ return;
+ }
+
/* array contains both UID and vcard */
for (p = vcards; p[0] && p[1]; p += 2) {
EContact *contact;
if (!view->priv->running)
return;
+ /* array contains UIDs only */
+ if (view->priv->direct_book) {
+ direct_contacts_fetch (view, vcards, signals[OBJECTS_MODIFIED]);
+ return;
+ }
+
/* array contains both UID and vcard */
for (p = vcards; p[0] && p[1]; p += 2) {
EContact *contact;
if (!view->priv->running)
return;
+ view->priv->complete = TRUE;
+
g_return_if_fail (e_gdbus_templates_decode_error (in_error_strv, &error));
g_signal_emit (view, signals[COMPLETE], 0, error);
}
static void
+book_client_view_set_direct_book (EBookClientView *view,
+ EDataBook *book)
+{
+ g_return_if_fail (book == NULL ||
+ E_IS_DATA_BOOK (book));
+ g_return_if_fail (view->priv->direct_book == NULL);
+
+ if (book)
+ view->priv->direct_book = g_object_ref (book);
+}
+
+static void
book_client_view_set_property (GObject *object,
guint property_id,
const GValue *value,
E_BOOK_CLIENT_VIEW (object),
g_value_get_string (value));
return;
+
+ case PROP_DIRECT_BOOK:
+ book_client_view_set_direct_book (
+ E_BOOK_CLIENT_VIEW (object),
+ g_value_get_object (value));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
priv->connection = NULL;
}
+ if (priv->direct_book != NULL) {
+ g_object_unref (priv->direct_book);
+ priv->direct_book = NULL;
+ }
+
if (priv->dbus_proxy != NULL) {
GError *error = NULL;
G_CALLBACK (book_client_view_complete_cb), initable);
priv->complete_handler_id = handler_id;
+ /* When in direct read access mode, we add a special field
+ * to fields-of-interest indicating we only want uids sent
+ */
+ if (priv->direct_book)
+ e_book_client_view_set_fields_of_interest (E_BOOK_CLIENT_VIEW (initable), NULL, NULL);
+
return TRUE;
}
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (
+ object_class,
+ PROP_DIRECT_BOOK,
+ g_param_spec_object (
+ "direct-book",
+ "Direct Book",
+ "The EDataBook to fetch contact "
+ "data from, if direct read access "
+ "is enabled",
+ E_TYPE_DATA_BOOK,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
signals[OBJECTS_ADDED] = g_signal_new (
"objects-added",
G_OBJECT_CLASS_TYPE (object_class),
g_return_if_fail (E_IS_BOOK_CLIENT_VIEW (view));
- strv = e_client_util_slist_to_strv (fields_of_interest);
+ /* When in direct read access mode, ensure that the
+ * backend is configured to only send us UIDs for everything,
+ *
+ * Just ignore the fields_of_interest and use them locally
+ * when filtering cards to be returned in direct reads.
+ */
+ if (view->priv->direct_book) {
+ GSList uid_field = { 0, };
+
+ uid_field.data = (gpointer)"x-evolution-uids-only";
+ strv = e_client_util_slist_to_strv (&uid_field);
+ } else
+ strv = e_client_util_slist_to_strv (fields_of_interest);
+
e_gdbus_book_view_call_set_fields_of_interest_sync (
view->priv->dbus_proxy,
(const gchar * const *) strv,
/* Private D-Bus classes. */
#include <e-dbus-address-book.h>
#include <e-dbus-address-book-factory.h>
+#include <e-dbus-direct-book.h>
#include <libedataserver/libedataserver.h>
#include <libedataserver/e-client-private.h>
+#include <libebackend/libebackend.h>
+#include <libedata-book/libedata-book.h>
+
#include "e-book-client.h"
#define E_BOOK_CLIENT_GET_PRIVATE(obj) \
struct _EBookClientPrivate {
GMainContext *main_context;
EDBusAddressBook *dbus_proxy;
+ EDataBook *direct_book;
guint name_watcher_id;
gulong dbus_proxy_error_handler_id;
#define LOCK_FACTORY() g_rec_mutex_lock (&book_factory_lock)
#define UNLOCK_FACTORY() g_rec_mutex_unlock (&book_factory_lock)
+
+typedef struct {
+ EBookClient *client;
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+ gpointer source_tag;
+} PropagateReadyData;
+
+static PropagateReadyData *
+propagate_ready_data_new (EBookClient *client,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ gpointer source_tag)
+{
+ PropagateReadyData *data = g_slice_new0 (PropagateReadyData);
+
+ data->client = g_object_ref (client);
+ data->callback = callback;
+ data->user_data = user_data;
+ data->source_tag = source_tag;
+
+ return data;
+}
+
+static void
+propagate_ready_data_free (PropagateReadyData *data)
+{
+ if (data) {
+ g_object_unref (data->client);
+ g_slice_free (PropagateReadyData, data);
+ }
+}
+
+static void
+propagate_direct_book_async_ready (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ PropagateReadyData *data = (PropagateReadyData *)user_data;
+
+ result = g_simple_async_result_new (G_OBJECT (data->client),
+ data->callback,
+ data->user_data,
+ data->source_tag);
+
+ g_object_ref (res);
+ g_simple_async_result_set_op_res_gpointer (result, res, g_object_unref);
+
+ g_simple_async_result_complete (result);
+ g_object_unref (result);
+
+ propagate_ready_data_free (data);
+}
+
static void
book_factory_disconnect (void)
{
book_client_dispose (GObject *object)
{
EBookClientPrivate *priv;
+ EBookClient *book_client;
- priv = E_BOOK_CLIENT_GET_PRIVATE (object);
+ book_client = E_BOOK_CLIENT (object);
+ priv = book_client->priv;
if (priv->dbus_proxy_error_handler_id > 0) {
g_signal_handler_disconnect (
priv->main_context = NULL;
}
+ if (book_client->priv->direct_book) {
+ e_data_book_close_sync (book_client->priv->direct_book, NULL, NULL);
+ g_object_unref (book_client->priv->direct_book);
+ book_client->priv->direct_book = NULL;
+ }
+
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_book_client_parent_class)->dispose (object);
}
"source", source, NULL);
}
+/**
+ * e_book_client_connect_direct_sync:
+ * @source: An #ESource pointer
+ * @error: A #GError pointer
+ *
+ * Like e_book_client_connect_sync(), except creates the book client for
+ * direct read access to the underlying addressbook.
+ *
+ * Returns: a new but unopened #EBookClient.
+ *
+ * Since: 3.8
+ **/
+EClient *
+e_book_client_connect_direct_sync (ESourceRegistry *registry,
+ ESource *source,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EClient *client;
+ EBookClientPrivate *priv;
+ EDBusDirectBook *direct_config;
+ const gchar *backend_name, *backend_path, *config;
+ GError *local_error = NULL;
+
+ client = e_book_client_connect_sync (source, cancellable, error);
+
+ if (!client)
+ return NULL;
+
+ priv = E_BOOK_CLIENT_GET_PRIVATE (client);
+
+ direct_config = e_dbus_direct_book_proxy_new_sync (
+ g_dbus_proxy_get_connection (G_DBUS_PROXY (priv->dbus_proxy)),
+ G_DBUS_PROXY_FLAGS_NONE,
+ ADDRESS_BOOK_DBUS_SERVICE_NAME,
+ g_dbus_proxy_get_object_path (G_DBUS_PROXY (priv->dbus_proxy)),
+ NULL, NULL);
+
+ backend_path = e_dbus_direct_book_get_backend_path (direct_config);
+ backend_name = e_dbus_direct_book_get_backend_name (direct_config);
+ config = e_dbus_direct_book_get_backend_config (direct_config);
+
+ if (backend_path && backend_path[0] &&
+ backend_name && backend_name[0]) {
+ priv->direct_book =
+ e_data_book_new_direct (registry, source,
+ backend_path,
+ backend_name,
+ config, &local_error);
+ if (!priv->direct_book) {
+ g_warning ("Failed to open addressbook in direct read access mode, "
+ "falling back to normal read access mode. Reason: %s",
+ local_error->message);
+ g_error_free (local_error);
+ }
+
+ } else
+ g_warning ("Direct read access mode not supported by the given backend, falling back to normal read access mode");
+
+ g_object_unref (direct_config);
+
+ /* We have to perform the opeining of the direct book separately
+ * from the EClient->open() implementation, because the direct
+ * book does not exist yet
+ */
+ if (priv->direct_book &&
+ !e_data_book_open_sync (priv->direct_book, cancellable, error)) {
+ g_warning ("Unable to open direct read access book, falling back to normal read access mode");
+ g_clear_object (&priv->direct_book);
+ }
+
+ return client;
+
+}
+
#define SELF_UID_PATH_ID "org.gnome.evolution-data-server.addressbook"
#define SELF_UID_KEY "self-contact-uid"
g_return_if_fail (E_IS_BOOK_CLIENT (client));
g_return_if_fail (uid != NULL);
+ if (client->priv->direct_book) {
+ PropagateReadyData *data = propagate_ready_data_new (client, callback, user_data,
+ e_book_client_get_contact);
+
+ e_data_book_get_contact (client->priv->direct_book, uid, cancellable,
+ propagate_direct_book_async_ready, data);
+ return;
+ }
+
async_context = g_slice_new0 (AsyncContext);
async_context->uid = g_strdup (uid);
result, G_OBJECT (client),
e_book_client_get_contact), FALSE);
+ if (client->priv->direct_book) {
+ GAsyncResult *res = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+
+ return e_data_book_get_contact_finish (client->priv->direct_book, res, out_contact, error);
+ }
+
simple = G_SIMPLE_ASYNC_RESULT (result);
async_context = g_simple_async_result_get_op_res_gpointer (simple);
g_return_val_if_fail (uid != NULL, FALSE);
g_return_val_if_fail (out_contact != NULL, FALSE);
+ if (client->priv->direct_book)
+ return e_data_book_get_contact_sync (client->priv->direct_book, uid, out_contact, cancellable, error);
+
utf8_uid = e_util_utf8_make_valid (uid);
success = e_dbus_address_book_call_get_contact_sync (
g_return_if_fail (E_IS_BOOK_CLIENT (client));
g_return_if_fail (sexp != NULL);
+ if (client->priv->direct_book) {
+ PropagateReadyData *data = propagate_ready_data_new (client, callback, user_data,
+ e_book_client_get_contacts);
+
+ e_data_book_get_contacts (client->priv->direct_book, sexp, cancellable,
+ propagate_direct_book_async_ready, data);
+ return;
+ }
+
async_context = g_slice_new0 (AsyncContext);
async_context->sexp = g_strdup (sexp);
GSimpleAsyncResult *simple;
AsyncContext *async_context;
+ if (client->priv->direct_book) {
+ GAsyncResult *direct_res =
+ g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+
+ return e_data_book_get_contacts_finish (client->priv->direct_book,
+ direct_res, out_contacts, error);
+ }
+
g_return_val_if_fail (
g_simple_async_result_is_valid (
result, G_OBJECT (client),
g_return_val_if_fail (sexp != NULL, FALSE);
g_return_val_if_fail (out_contacts != NULL, FALSE);
+ if (client->priv->direct_book)
+ return e_data_book_get_contacts_sync (client->priv->direct_book,
+ sexp, out_contacts, cancellable, error);
+
utf8_sexp = e_util_utf8_make_valid (sexp);
success = e_dbus_address_book_call_get_contact_list_sync (
g_return_if_fail (E_IS_BOOK_CLIENT (client));
g_return_if_fail (sexp != NULL);
+ if (client->priv->direct_book) {
+ PropagateReadyData *data = propagate_ready_data_new (client, callback, user_data,
+ e_book_client_get_contacts_uids);
+
+ e_data_book_get_contacts_uids (client->priv->direct_book, sexp, cancellable,
+ propagate_direct_book_async_ready, data);
+ return;
+ }
+
async_context = g_slice_new0 (AsyncContext);
async_context->sexp = g_strdup (sexp);
GSimpleAsyncResult *simple;
AsyncContext *async_context;
+ if (client->priv->direct_book) {
+ GAsyncResult *direct_res =
+ g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+
+ return e_data_book_get_contacts_uids_finish (
+ client->priv->direct_book,
+ direct_res, out_contact_uids, error);
+ }
+
g_return_val_if_fail (
g_simple_async_result_is_valid (
result, G_OBJECT (client),
g_return_val_if_fail (sexp != NULL, FALSE);
g_return_val_if_fail (out_contact_uids != NULL, FALSE);
+ if (client->priv->direct_book)
+ return e_data_book_get_contacts_uids_sync (
+ client->priv->direct_book, sexp,
+ out_contact_uids, cancellable, error);
+
utf8_sexp = e_util_utf8_make_valid (sexp);
success = e_dbus_address_book_call_get_contact_list_uids_sync (
"client", client,
"connection", connection,
"object-path", object_path,
+ "direct-book", client->priv->direct_book,
NULL);
/* XXX Would have been easier to return the