[evolution-data-server] Bug #651446 - Implement e_book_client_get_contacts_uids()



commit 6e1a5b1257acdd503d2f3b7f298f13c1cadf466c
Author: Milan Crha <mcrha redhat com>
Date:   Tue May 31 18:10:49 2011 +0200

    Bug #651446 - Implement e_book_client_get_contacts_uids()

 addressbook/backends/file/e-book-backend-file.c    |   96 +++++++++
 .../backends/google/e-book-backend-google.c        |   33 +++
 addressbook/backends/ldap/e-book-backend-ldap.c    |  204 ++++++++++++++++++++
 .../backends/webdav/e-book-backend-webdav.c        |   38 ++++
 addressbook/libebook/e-book-client.c               |  132 +++++++++++++
 addressbook/libebook/e-book-client.h               |    4 +
 addressbook/libedata-book/e-book-backend-sync.c    |   85 ++++++++
 addressbook/libedata-book/e-book-backend-sync.h    |    2 +
 addressbook/libedata-book/e-book-backend.c         |   32 +++
 addressbook/libedata-book/e-book-backend.h         |    2 +
 addressbook/libedata-book/e-data-book.c            |   53 +++++
 addressbook/libedata-book/e-data-book.h            |    1 +
 addressbook/libegdbus/e-gdbus-book.c               |   29 +++
 addressbook/libegdbus/e-gdbus-book.h               |    9 +
 tests/libebook/client/test-client-async.c          |   38 ++++-
 tests/libebook/client/test-client.c                |   37 ++++
 16 files changed, 794 insertions(+), 1 deletions(-)
---
diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c
index 4fc4e0e..87f4180 100644
--- a/addressbook/backends/file/e-book-backend-file.c
+++ b/addressbook/backends/file/e-book-backend-file.c
@@ -499,6 +499,101 @@ e_book_backend_file_get_contact_list (EBookBackendSync *backend,
 	*contacts = contact_list;
 }
 
+static void
+e_book_backend_file_get_contact_list_uids (EBookBackendSync *backend,
+					   EDataBook *book,
+					   GCancellable *cancellable,
+					   const gchar *query,
+					   GSList **contacts_uids,
+					   GError **perror)
+{
+	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
+	DB             *db = bf->priv->file_db;
+	DBC            *dbc;
+	gint            db_error;
+	DBT  id_dbt, vcard_dbt;
+	EBookBackendSExp *card_sexp = NULL;
+	gboolean search_needed;
+	const gchar *search = query;
+	GSList *uids = NULL;
+
+	d(printf ("e_book_backend_file_get_contact_list (%s)\n", search));
+	if (e_book_backend_summary_is_summary_query (bf->priv->summary, search)) {
+		/* do a summary query */
+		GPtrArray *ids = e_book_backend_summary_search (bf->priv->summary, search);
+		gint i;
+
+		if (!ids) {
+			g_propagate_error (perror, EDB_ERROR (CONTACT_NOT_FOUND));
+			return;
+		}
+
+		for (i = 0; i < ids->len; i++) {
+			gchar *id = g_ptr_array_index (ids, i);
+
+			uids = g_slist_prepend (uids, g_strdup (id));
+		}
+
+		g_ptr_array_free (ids, TRUE);
+	} else {
+		search_needed = TRUE;
+		if (!strcmp (search, "(contains \"x-evolution-any-field\" \"\")"))
+			search_needed = FALSE;
+
+		card_sexp = e_book_backend_sexp_new (search);
+		if (!card_sexp) {
+			g_propagate_error (perror, EDB_ERROR (INVALID_QUERY));
+			return;
+		}
+
+		db_error = db->cursor (db, NULL, &dbc, 0);
+
+		if (db_error != 0) {
+			g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error));
+			/* XXX this needs to be some CouldNotOpen error */
+			db_error_to_gerror (db_error, perror);
+			return;
+		}
+
+		memset (&vcard_dbt, 0, sizeof (vcard_dbt));
+		vcard_dbt.flags = DB_DBT_MALLOC;
+		memset (&id_dbt, 0, sizeof (id_dbt));
+		db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST);
+
+		while (db_error == 0) {
+
+			/* don't include the version in the list of cards */
+			if (id_dbt.size != strlen (E_BOOK_BACKEND_FILE_VERSION_NAME) + 1
+			    || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
+
+				if ((!search_needed) || (card_sexp != NULL && e_book_backend_sexp_match_vcard  (card_sexp, vcard_dbt.data))) {
+					uids = g_slist_prepend (uids, g_strdup (id_dbt.data));
+				}
+			}
+
+			g_free (vcard_dbt.data);
+
+			db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT);
+
+		}
+		g_object_unref (card_sexp);
+
+		if (db_error == DB_NOTFOUND) {
+			/* Success */
+		} else {
+			g_warning (G_STRLOC ": dbc->c_get failed with %s", db_strerror (db_error));
+			db_error_to_gerror (db_error, perror);
+		}
+
+		db_error = dbc->c_close (dbc);
+		if (db_error != 0) {
+			g_warning (G_STRLOC ": dbc->c_close failed with %s", db_strerror (db_error));
+		}
+	}
+
+	*contacts_uids = g_slist_reverse (uids);
+}
+
 typedef struct {
 	EBookBackendFile *bf;
 	GThread *thread;
@@ -1370,6 +1465,7 @@ e_book_backend_file_class_init (EBookBackendFileClass *klass)
 	sync_class->modify_contact_sync		= e_book_backend_file_modify_contact;
 	sync_class->get_contact_sync		= e_book_backend_file_get_contact;
 	sync_class->get_contact_list_sync	= e_book_backend_file_get_contact_list;
+	sync_class->get_contact_list_uids_sync	= e_book_backend_file_get_contact_list_uids;
 	sync_class->authenticate_user_sync	= e_book_backend_file_authenticate_user;
 
 	object_class->dispose = e_book_backend_file_dispose;
diff --git a/addressbook/backends/google/e-book-backend-google.c b/addressbook/backends/google/e-book-backend-google.c
index 0ac563e..4f1804e 100644
--- a/addressbook/backends/google/e-book-backend-google.c
+++ b/addressbook/backends/google/e-book-backend-google.c
@@ -1151,6 +1151,38 @@ e_book_backend_google_get_contact_list (EBookBackend *backend, EDataBook *book,
 	g_slist_free (filtered_contacts);
 }
 
+static void
+e_book_backend_google_get_contact_list_uids (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *query)
+{
+	EBookBackendSExp *sexp;
+	GList *all_contacts;
+	GSList *filtered_uids = NULL;
+
+	__debug__ (G_STRFUNC);
+
+	/* Get all contacts */
+	sexp = e_book_backend_sexp_new (query);
+	all_contacts = cache_get_contacts (backend);
+
+	for (; all_contacts; all_contacts = g_list_delete_link (all_contacts, all_contacts)) {
+		EContact *contact = all_contacts->data;
+
+		/* If the search expression matches the contact, include it in the search results */
+		if (e_book_backend_sexp_match_contact (sexp, contact)) {
+			filtered_uids = g_slist_append (filtered_uids, e_contact_get (contact, E_CONTACT_UID));
+		}
+
+		g_object_unref (contact);
+	}
+
+	g_object_unref (sexp);
+
+	e_data_book_respond_get_contact_list_uids (book, opid, NULL, filtered_uids);
+
+	g_slist_foreach (filtered_uids, (GFunc) g_free, NULL);
+	g_slist_free (filtered_uids);
+}
+
 static gboolean
 on_refresh_idle (EBookBackend *backend)
 {
@@ -1687,6 +1719,7 @@ e_book_backend_google_class_init (EBookBackendGoogleClass *klass)
 	backend_class->modify_contact		= e_book_backend_google_modify_contact;
 	backend_class->get_contact		= e_book_backend_google_get_contact;
 	backend_class->get_contact_list		= e_book_backend_google_get_contact_list;
+	backend_class->get_contact_list_uids	= e_book_backend_google_get_contact_list_uids;
 	backend_class->authenticate_user	= e_book_backend_google_authenticate_user;
 
 	object_class->dispose  = e_book_backend_google_dispose;
diff --git a/addressbook/backends/ldap/e-book-backend-ldap.c b/addressbook/backends/ldap/e-book-backend-ldap.c
index fd72bb2..ef2ccd1 100644
--- a/addressbook/backends/ldap/e-book-backend-ldap.c
+++ b/addressbook/backends/ldap/e-book-backend-ldap.c
@@ -2696,6 +2696,209 @@ e_book_backend_ldap_get_contact_list (EBookBackend *backend,
 	}
 }
 
+typedef struct {
+	LDAPOp op;
+	GSList *uids;
+} LDAPGetContactListUIDsOp;
+
+static void
+contact_list_uids_handler (LDAPOp *op, LDAPMessage *res)
+{
+	LDAPGetContactListUIDsOp *contact_list_uids_op = (LDAPGetContactListUIDsOp *) op;
+	EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (op->backend);
+	LDAPMessage *e;
+	gint msg_type;
+	GTimeVal start, end;
+	gulong diff;
+
+	if (enable_debug) {
+		printf ("contact_list_uids_handler ...\n");
+		g_get_current_time (&start);
+	}
+
+	g_static_rec_mutex_lock (&eds_ldap_handler_lock);
+	if (!bl->priv->ldap) {
+		g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
+		e_data_book_respond_get_contact_list_uids (op->book, op->opid, EDB_ERROR_NOT_CONNECTED (), NULL);
+		ldap_op_finished (op);
+		if (enable_debug)
+			printf ("contact_list_uids_handler ... ldap handler is NULL \n");
+		return;
+	}
+	g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
+
+	msg_type = ldap_msgtype (res);
+	if (msg_type == LDAP_RES_SEARCH_ENTRY) {
+		g_static_rec_mutex_lock (&eds_ldap_handler_lock);
+		e = ldap_first_entry (bl->priv->ldap, res);
+		g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
+
+		while (NULL != e) {
+			EContact *contact;
+			gchar *uid = NULL;
+
+			contact = build_contact_from_entry (bl, e, NULL, &uid);
+			g_object_unref (contact);
+
+			if (enable_debug)
+				printf ("uid = %s\n", uid);
+
+			contact_list_uids_op->uids = g_slist_append (contact_list_uids_op->uids, uid);
+
+			g_static_rec_mutex_lock (&eds_ldap_handler_lock);
+			e = ldap_next_entry (bl->priv->ldap, e);
+			g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
+		}
+	}
+	else if (msg_type == LDAP_RES_SEARCH_RESULT) {
+		gchar *ldap_error_msg;
+		gint ldap_error;
+
+		g_static_rec_mutex_lock (&eds_ldap_handler_lock);
+		ldap_parse_result (bl->priv->ldap, res, &ldap_error,
+				   NULL, &ldap_error_msg, NULL, NULL, 0);
+		g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
+		if (ldap_error != LDAP_SUCCESS) {
+			g_warning ("contact_list_uids_handler: %02X (%s), additional info: %s",
+				   ldap_error,
+				   ldap_err2string (ldap_error), ldap_error_msg);
+		}
+		ldap_memfree (ldap_error_msg);
+
+		g_warning ("search returned %d\n", ldap_error);
+
+		if (ldap_error == LDAP_TIMELIMIT_EXCEEDED)
+			e_data_book_respond_get_contact_list_uids (op->book, op->opid, EDB_ERROR (SEARCH_TIME_LIMIT_EXCEEDED), contact_list_uids_op->uids);
+		else if (ldap_error == LDAP_SIZELIMIT_EXCEEDED)
+			e_data_book_respond_get_contact_list_uids (op->book, op->opid, EDB_ERROR (SEARCH_SIZE_LIMIT_EXCEEDED), contact_list_uids_op->uids);
+		else if (ldap_error == LDAP_SUCCESS)
+			e_data_book_respond_get_contact_list_uids (op->book, op->opid, EDB_ERROR (SUCCESS), contact_list_uids_op->uids);
+		else
+			e_data_book_respond_get_contact_list_uids (op->book, op->opid, ldap_error_to_response (ldap_error), contact_list_uids_op->uids);
+
+		ldap_op_finished (op);
+		if (enable_debug) {
+			printf ("contact_list_uids_handler success ");
+			g_get_current_time (&end);
+			diff = end.tv_sec * 1000 + end.tv_usec/1000;
+			diff -= start.tv_sec * 1000 + start.tv_usec/1000;
+			printf("and took %ld.%03ld seconds\n", diff/1000, diff%1000);
+		}
+	}
+	else {
+		g_warning ("unhandled search result type %d returned", msg_type);
+		e_data_book_respond_get_contact_list_uids (op->book, op->opid,
+						           e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
+							      "%s: Unhandled search result type %d returned", G_STRFUNC, msg_type),
+							   NULL);
+		ldap_op_finished (op);
+	}
+}
+
+static void
+contact_list_uids_dtor (LDAPOp *op)
+{
+	LDAPGetContactListUIDsOp *contact_list_uids_op = (LDAPGetContactListUIDsOp *) op;
+
+	g_slist_foreach (contact_list_uids_op->uids, (GFunc) g_free, NULL);
+	g_slist_free (contact_list_uids_op->uids);
+
+	g_free (contact_list_uids_op);
+}
+
+static void
+e_book_backend_ldap_get_contact_list_uids (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *query)
+{
+	LDAPGetContactListUIDsOp *contact_list_uids_op;
+	EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
+	gint contact_list_uids_msgid;
+	EDataBookView *book_view;
+	gint ldap_error;
+	gchar *ldap_query;
+	GTimeVal start, end;
+	gulong diff;
+
+	if (enable_debug) {
+		printf ("e_book_backend_ldap_get_contact_list_uids ... \n");
+		g_get_current_time (&start);
+	}
+
+	if (!bl->priv->is_online) {
+		if (bl->priv->marked_for_offline && bl->priv->cache) {
+			GList *contacts;
+			GSList *uids = NULL;
+			GList *l;
+
+			contacts = e_book_backend_cache_get_contacts (bl->priv->cache, query);
+
+			for (l = contacts; l; l = g_list_next (l)) {
+				EContact *contact = l->data;
+				uids = g_slist_prepend (uids, e_contact_get (contact, E_CONTACT_UID));
+				g_object_unref (contact);
+			}
+
+			g_list_free (contacts);
+			e_data_book_respond_get_contact_list_uids (book, opid, EDB_ERROR (SUCCESS), uids);
+			g_slist_foreach (uids, (GFunc) g_free, NULL);
+			g_slist_free (uids);
+			return;
+		}
+
+		e_data_book_respond_get_contact_list_uids (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
+		return;
+	}
+
+	g_static_rec_mutex_lock (&eds_ldap_handler_lock);
+	if (!bl->priv->ldap) {
+		g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
+		e_data_book_respond_get_contact_list_uids (book, opid, EDB_ERROR_NOT_CONNECTED (), NULL);
+		if (enable_debug)
+			printf ("e_book_backend_ldap_get_contact_list_uids... ldap handler is NULL\n");
+		return;
+	}
+	g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
+
+	contact_list_uids_op = g_new0 (LDAPGetContactListUIDsOp, 1);
+	book_view = find_book_view (bl);
+
+	ldap_query = e_book_backend_ldap_build_query (bl, query);
+
+	if (enable_debug)
+		printf ("getting contact list uids with filter: %s\n", ldap_query);
+
+	do {
+		g_static_rec_mutex_lock (&eds_ldap_handler_lock);
+		ldap_error = ldap_search_ext (bl->priv->ldap,
+					      bl->priv->ldap_rootdn,
+					      bl->priv->ldap_scope,
+					      ldap_query,
+					      NULL, 0, NULL, NULL,
+					      NULL, /* XXX timeout */
+					      LDAP_NO_LIMIT, &contact_list_uids_msgid);
+		g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
+	} while (e_book_backend_ldap_reconnect (bl, book_view, ldap_error));
+
+	g_free (ldap_query);
+
+	if (ldap_error == LDAP_SUCCESS) {
+		ldap_op_add ((LDAPOp*) contact_list_uids_op, backend, book,
+			     book_view, opid, contact_list_uids_msgid,
+			     contact_list_uids_handler, contact_list_uids_dtor);
+		if (enable_debug) {
+			g_get_current_time (&end);
+
+			diff = end.tv_sec * 1000 + end.tv_usec/1000;
+			diff -= start.tv_sec * 1000 + start.tv_usec/1000;
+
+			printf ("e_book_backend_ldap_get_contact_list_uids invoked contact_list_uids_handler ");
+			printf ("and took %ld.%03ld seconds\n", diff/1000, diff%1000);
+		}
+	} else {
+		e_data_book_respond_get_contact_list_uids (book, opid, ldap_error_to_response (ldap_error), NULL);
+		contact_list_uids_dtor ((LDAPOp *) contact_list_uids_op);
+	}
+}
+
 static EContactField email_ids[4] = {
 	E_CONTACT_EMAIL_1,
 	E_CONTACT_EMAIL_2,
@@ -5347,6 +5550,7 @@ e_book_backend_ldap_class_init (EBookBackendLDAPClass *klass)
 	parent_class->modify_contact		= e_book_backend_ldap_modify_contact;
 	parent_class->get_contact		= e_book_backend_ldap_get_contact;
 	parent_class->get_contact_list		= e_book_backend_ldap_get_contact_list;
+	parent_class->get_contact_list_uids	= e_book_backend_ldap_get_contact_list_uids;
 	parent_class->start_book_view		= e_book_backend_ldap_start_book_view;
 	parent_class->stop_book_view		= e_book_backend_ldap_stop_book_view;
 	parent_class->authenticate_user		= e_book_backend_ldap_authenticate_user;
diff --git a/addressbook/backends/webdav/e-book-backend-webdav.c b/addressbook/backends/webdav/e-book-backend-webdav.c
index 7020008..4cf0d15 100644
--- a/addressbook/backends/webdav/e-book-backend-webdav.c
+++ b/addressbook/backends/webdav/e-book-backend-webdav.c
@@ -1095,6 +1095,43 @@ e_book_backend_webdav_get_contact_list (EBookBackend *backend, EDataBook *book,
 }
 
 static void
+e_book_backend_webdav_get_contact_list_uids (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *query)
+{
+	EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
+	EBookBackendWebdavPrivate *priv   = webdav->priv;
+	GList                     *contact_list;
+	GSList                    *uids_list;
+	GList                     *c;
+
+	if (priv->is_online) {
+		/* make sure the cache is up to date */
+		GError *error = download_contacts (webdav, NULL, NULL);
+
+		if (error) {
+			e_data_book_respond_get_contact_list_uids (book, opid, error, NULL);
+			return;
+		}
+	}
+
+	/* answer query from cache */
+	contact_list = e_book_backend_cache_get_contacts (priv->cache, query);
+	uids_list   = NULL;
+	for (c = contact_list; c != NULL; c = g_list_next (c)) {
+		EContact *contact = c->data;
+
+		uids_list = g_slist_append (uids_list, e_contact_get (contact, E_CONTACT_UID));
+
+		g_object_unref (contact);
+	}
+	g_list_free (contact_list);
+
+	e_data_book_respond_get_contact_list_uids (book, opid, EDB_ERROR (SUCCESS), uids_list);
+
+	g_slist_foreach (uids_list, (GFunc) g_free, NULL);
+	g_slist_free (uids_list);
+}
+
+static void
 e_book_backend_webdav_authenticate_user (EBookBackend *backend, GCancellable *cancellable, ECredentials *credentials)
 {
 	EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
@@ -1377,6 +1414,7 @@ e_book_backend_webdav_class_init (EBookBackendWebdavClass *klass)
 	backend_class->modify_contact		= e_book_backend_webdav_modify_contact;
 	backend_class->get_contact		= e_book_backend_webdav_get_contact;
 	backend_class->get_contact_list		= e_book_backend_webdav_get_contact_list;
+	backend_class->get_contact_list_uids	= e_book_backend_webdav_get_contact_list_uids;
 	backend_class->start_book_view		= e_book_backend_webdav_start_book_view;
 	backend_class->stop_book_view		= e_book_backend_webdav_stop_book_view;
 	backend_class->authenticate_user	= e_book_backend_webdav_authenticate_user;
diff --git a/addressbook/libebook/e-book-client.c b/addressbook/libebook/e-book-client.c
index 9993c31..14e50ff 100644
--- a/addressbook/libebook/e-book-client.c
+++ b/addressbook/libebook/e-book-client.c
@@ -1794,6 +1794,138 @@ e_book_client_get_contacts_sync (EBookClient *client, const gchar *sexp, GSList
 }
 
 /**
+ * e_book_client_get_contacts_uids:
+ * @client: an #EBookClient
+ * @sexp: an S-expression representing the query
+ * @cancellable: a #GCancellable; can be %NULL
+ * @callback: callback to call when a result is ready
+ * @user_data: user data for the @callback
+ *
+ * Query @client with @sexp, receiving a list of contacts UIDs which
+ * matched. The call is finished by e_book_client_get_contacts_uids_finish()
+ * from the @callback.
+ *
+ * Note: @sexp can be obtained through #EBookQuery, by converting it
+ * to a string with e_book_query_to_string().
+ *
+ * Since: 3.2
+ **/
+void
+e_book_client_get_contacts_uids (EBookClient *client, const gchar *sexp, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
+{
+	gchar *gdbus_sexp = NULL;
+
+	g_return_if_fail (sexp != NULL);
+
+	e_client_proxy_call_string (E_CLIENT (client), e_util_ensure_gdbus_string (sexp, &gdbus_sexp), cancellable, callback, user_data, e_book_client_get_contacts_uids,
+			e_gdbus_book_call_get_contact_list_uids,
+			NULL, NULL, NULL, e_gdbus_book_call_get_contact_list_uids_finish, NULL);
+
+	g_free (gdbus_sexp);
+}
+
+/**
+ * e_book_client_get_contacts_uids_finish:
+ * @client: an #EBookClient
+ * @result: a #GAsyncResult
+ * @contacts_uids: (out) a #GSList of matched contacts UIDs stored as strings
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Finishes previous call of e_book_client_get_contacts_uids().
+ * If successful, then the @contacts_uids is set to newly allocated list
+ * of UID strings, which should be freed with e_client_util_free_string_slist().
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.2
+ **/
+gboolean
+e_book_client_get_contacts_uids_finish (EBookClient *client, GAsyncResult *result, GSList **contacts_uids, GError **error)
+{
+	gboolean res;
+	gchar **uids = NULL;
+
+	g_return_val_if_fail (contacts_uids != NULL, FALSE);
+
+	res = e_client_proxy_call_finish_strv (E_CLIENT (client), result, &uids, error, e_book_client_get_contacts_uids);
+
+	if (uids && res) {
+		gint ii;
+		GSList *slist = NULL;
+
+		for (ii = 0; uids[ii]; ii++) {
+			slist = g_slist_prepend (slist, g_strdup (uids[ii]));
+		}
+
+		*contacts_uids = g_slist_reverse (slist);
+	} else {
+		*contacts_uids = NULL;
+	}
+
+	g_strfreev (uids);
+
+	return res;
+}
+
+/**
+ * e_book_client_get_contacts_uids_sync:
+ * @client: an #EBookClient
+ * @sexp: an S-expression representing the query
+ * @contacts_uids: (out) a #GSList of matched contacts UIDs stored as strings
+ * @cancellable: a #GCancellable; can be %NULL
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Query @client with @sexp, receiving a list of contacts UIDs which matched.
+ * If successful, then the @contacts_uids is set to newly allocated list
+ * of UID strings, which should be freed with e_client_util_free_string_slist().
+ *
+ * Note: @sexp can be obtained through #EBookQuery, by converting it
+ * to a string with e_book_query_to_string().
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.2
+ **/
+gboolean
+e_book_client_get_contacts_uids_sync (EBookClient *client, const gchar *sexp, GSList **contacts_uids, GCancellable *cancellable, GError **error)
+{
+	gboolean res;
+	gchar *gdbus_sexp = NULL;
+	gchar **uids = NULL;
+
+	g_return_val_if_fail (client != NULL, FALSE);
+	g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
+	g_return_val_if_fail (client->priv != NULL, FALSE);
+	g_return_val_if_fail (sexp != NULL, FALSE);
+	g_return_val_if_fail (contacts_uids != NULL, FALSE);
+
+	if (!client->priv->gdbus_book) {
+		set_proxy_gone_error (error);
+		return FALSE;
+	}
+
+	res = e_client_proxy_call_sync_string__strv (E_CLIENT (client), e_util_ensure_gdbus_string (sexp, &gdbus_sexp), &uids, cancellable, error, e_gdbus_book_call_get_contact_list_uids_sync);
+
+	if (uids && res) {
+		gint ii;
+		GSList *slist = NULL;
+
+		for (ii = 0; uids[ii]; ii++) {
+			slist = g_slist_prepend (slist, g_strdup (uids[ii]));
+		}
+
+		*contacts_uids = g_slist_reverse (slist);
+	} else {
+		*contacts_uids = NULL;
+	}
+
+	g_free (gdbus_sexp);
+	g_strfreev (uids);
+
+	return res;
+}
+
+/**
  * e_book_client_get_view:
  * @client: an #EBookClient
  * @sexp: an S-expression representing the query
diff --git a/addressbook/libebook/e-book-client.h b/addressbook/libebook/e-book-client.h
index 92bddb9..ef6d601 100644
--- a/addressbook/libebook/e-book-client.h
+++ b/addressbook/libebook/e-book-client.h
@@ -121,6 +121,10 @@ void		e_book_client_get_contacts			(EBookClient *client, const gchar *sexp, GCan
 gboolean	e_book_client_get_contacts_finish		(EBookClient *client, GAsyncResult *result, GSList **contacts, GError **error);
 gboolean	e_book_client_get_contacts_sync			(EBookClient *client, const gchar *sexp, GSList **contacts, GCancellable *cancellable, GError **error);
 
+void		e_book_client_get_contacts_uids			(EBookClient *client, const gchar *sexp, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+gboolean	e_book_client_get_contacts_uids_finish		(EBookClient *client, GAsyncResult *result, GSList **contacts_uids, GError **error);
+gboolean	e_book_client_get_contacts_uids_sync		(EBookClient *client, const gchar *sexp, GSList **contacts_uids, GCancellable *cancellable, GError **error);
+
 void		e_book_client_get_view				(EBookClient *client, const gchar *sexp, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 gboolean	e_book_client_get_view_finish			(EBookClient *client, GAsyncResult *result, EBookClientView **view, GError **error);
 gboolean	e_book_client_get_view_sync			(EBookClient *client, const gchar *sexp, EBookClientView **view, GCancellable *cancellable, GError **error);
diff --git a/addressbook/libedata-book/e-book-backend-sync.c b/addressbook/libedata-book/e-book-backend-sync.c
index a944554..141b234 100644
--- a/addressbook/libedata-book/e-book-backend-sync.c
+++ b/addressbook/libedata-book/e-book-backend-sync.c
@@ -304,6 +304,72 @@ e_book_backend_sync_get_contact_list (EBookBackendSync *backend,
 }
 
 /**
+ * e_book_backend_sync_get_contact_list_uids:
+ * @backend: an #EBookBackendSync
+ * @book: an #EDataBook
+ * @cancellable: a #GCancellable for the operation
+ * @query: an s-expression of the query to perform
+ * @contacts_uids: a pointer to a location to store the resulting list of UID strings
+ * @error: #GError to set, when something fails
+ *
+ * Gets a list of contact UIDS from @book. The list and its elements must be freed
+ * by the caller.
+ *
+ * Since: 3.2
+ **/
+void
+e_book_backend_sync_get_contact_list_uids (EBookBackendSync *backend,
+					   EDataBook *book,
+					   GCancellable *cancellable,
+					   const gchar *query,
+					   GSList **contacts_uids,
+					   GError **error)
+{
+	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_SYNC (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
+	e_return_data_book_error_if_fail (E_IS_DATA_BOOK (book), E_DATA_BOOK_STATUS_INVALID_ARG);
+	e_return_data_book_error_if_fail (query, E_DATA_BOOK_STATUS_INVALID_ARG);
+	e_return_data_book_error_if_fail (contacts_uids, E_DATA_BOOK_STATUS_INVALID_ARG);
+
+	if (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_contact_list_uids_sync != NULL) {
+		(* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_contact_list_uids_sync) (backend, book, cancellable, query, contacts_uids, error);
+	} else {
+		/* inefficient fallback code */
+		GSList *vcards = NULL;
+		GError *local_error = NULL;
+
+		e_book_backend_sync_get_contact_list_uids (backend, book, cancellable, query, &vcards, &local_error);
+
+		if (local_error) {
+			g_propagate_error (error, local_error);
+		} else {
+			GSList *v;
+
+			*contacts_uids = NULL;
+
+			for (v = vcards; v; v = v->next) {
+				EVCard *card = e_vcard_new_from_string (v->data);
+				EVCardAttribute *attr;
+
+				if (!card)
+					continue;
+
+				attr = e_vcard_get_attribute (card, EVC_UID);
+
+				if (attr)
+					*contacts_uids = g_slist_prepend (*contacts_uids, e_vcard_attribute_get_value (attr));
+
+				g_object_unref (card);
+			}
+
+			*contacts_uids = g_slist_reverse (*contacts_uids);
+		}
+
+		g_slist_foreach (vcards, (GFunc) g_free, NULL);
+		g_slist_free (vcards);
+	}
+}
+
+/**
  * e_book_backend_sync_authenticate_user:
  * @backend: an #EBookBackendSync
  * @cancellable: a #GCancellable for the operation
@@ -480,6 +546,24 @@ book_backend_get_contact_list (EBookBackend *backend,
 }
 
 static void
+book_backend_get_contact_list_uids (EBookBackend *backend,
+				    EDataBook    *book,
+				    guint32       opid,
+				    GCancellable *cancellable,
+				    const gchar   *query)
+{
+	GError *error = NULL;
+	GSList *uids = NULL;
+
+	e_book_backend_sync_get_contact_list_uids (E_BOOK_BACKEND_SYNC (backend), book, cancellable, query, &uids, &error);
+
+	e_data_book_respond_get_contact_list_uids (book, opid, error, uids);
+
+	g_slist_foreach (uids, (GFunc) g_free, NULL);
+	g_slist_free (uids);
+}
+
+static void
 book_backend_authenticate_user (EBookBackend *backend,
 				GCancellable *cancellable,
 				ECredentials *credentials)
@@ -553,6 +637,7 @@ e_book_backend_sync_class_init (EBookBackendSyncClass *klass)
 	backend_class->modify_contact		= book_backend_modify_contact;
 	backend_class->get_contact		= book_backend_get_contact;
 	backend_class->get_contact_list		= book_backend_get_contact_list;
+	backend_class->get_contact_list_uids	= book_backend_get_contact_list_uids;
 
 	klass->get_backend_property_sync	= book_backend_sync_get_backend_property;
 	klass->set_backend_property_sync	= book_backend_sync_set_backend_property;
diff --git a/addressbook/libedata-book/e-book-backend-sync.h b/addressbook/libedata-book/e-book-backend-sync.h
index 988fa24..7ef2f51 100644
--- a/addressbook/libedata-book/e-book-backend-sync.h
+++ b/addressbook/libedata-book/e-book-backend-sync.h
@@ -38,6 +38,7 @@ struct _EBookBackendSyncClass {
 	void (*modify_contact_sync)		(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *vcard, EContact **contact, GError **error);
 	void (*get_contact_sync)		(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *id, gchar **vcard, GError **error);
 	void (*get_contact_list_sync)		(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *query, GSList **contacts, GError **error);
+	void (*get_contact_list_uids_sync)	(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *query, GSList **contacts_uids, GError **error);
 
 	void (*authenticate_user_sync)		(EBookBackendSync *backend, GCancellable *cancellable, ECredentials *credentials, GError **error);
 };
@@ -56,6 +57,7 @@ void		e_book_backend_sync_remove_contacts	(EBookBackendSync *backend, EDataBook
 void		e_book_backend_sync_modify_contact	(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *vcard, EContact **contact, GError **error);
 void		e_book_backend_sync_get_contact		(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *id, gchar **vcard, GError **error);
 void		e_book_backend_sync_get_contact_list	(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *query, GSList **contacts, GError **error);
+void		e_book_backend_sync_get_contact_list_uids(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *query, GSList **contacts_uids, GError **error);
 
 void		e_book_backend_sync_authenticate_user	(EBookBackendSync *backend, GCancellable *cancellable, ECredentials *credentials, GError **error);
 
diff --git a/addressbook/libedata-book/e-book-backend.c b/addressbook/libedata-book/e-book-backend.c
index 72cfd01..64673d5 100644
--- a/addressbook/libedata-book/e-book-backend.c
+++ b/addressbook/libedata-book/e-book-backend.c
@@ -591,6 +591,38 @@ e_book_backend_get_contact_list (EBookBackend *backend,
 }
 
 /**
+ * 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);
+
+	if (e_book_backend_is_opening (backend))
+		e_data_book_respond_get_contact_list_uids (book, opid, EDB_OPENING_ERROR, NULL);
+	else
+		(* E_BOOK_BACKEND_GET_CLASS (backend)->get_contact_list_uids) (backend, book, opid, cancellable, query);
+}
+
+/**
  * e_book_backend_start_book_view:
  * @backend: an #EBookBackend
  * @book_view: the #EDataBookView to start
diff --git a/addressbook/libedata-book/e-book-backend.h b/addressbook/libedata-book/e-book-backend.h
index 52a9b39..0ee449d 100644
--- a/addressbook/libedata-book/e-book-backend.h
+++ b/addressbook/libedata-book/e-book-backend.h
@@ -74,6 +74,7 @@ struct _EBookBackendClass {
 	void	(* modify_contact)		(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *vcard);
 	void	(* get_contact)			(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *id);
 	void	(* get_contact_list)		(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *query);
+	void	(* get_contact_list_uids)	(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *query);
 
 	void	(* start_book_view)		(EBookBackend *backend, EDataBookView *book_view);
 	void	(* stop_book_view)		(EBookBackend *backend, EDataBookView *book_view);
@@ -112,6 +113,7 @@ void		e_book_backend_remove_contacts	(EBookBackend *backend, EDataBook *book, gu
 void		e_book_backend_modify_contact	(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *vcard);
 void		e_book_backend_get_contact	(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *id);
 void		e_book_backend_get_contact_list	(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *query);
+void		e_book_backend_get_contact_list_uids(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *query);
 
 void		e_book_backend_start_book_view	(EBookBackend *backend, EDataBookView *view);
 void		e_book_backend_stop_book_view	(EBookBackend *backend, EDataBookView *view);
diff --git a/addressbook/libedata-book/e-data-book.c b/addressbook/libedata-book/e-data-book.c
index 7aebda4..9e17703 100644
--- a/addressbook/libedata-book/e-data-book.c
+++ b/addressbook/libedata-book/e-data-book.c
@@ -58,6 +58,7 @@ typedef enum {
 	OP_REFRESH,
 	OP_GET_CONTACT,
 	OP_GET_CONTACTS,
+	OP_GET_CONTACTS_UIDS,
 	OP_AUTHENTICATE,
 	OP_ADD_CONTACT,
 	OP_REMOVE_CONTACTS,
@@ -90,6 +91,7 @@ typedef struct {
 		gchar *vcard;
 		/* OP_GET_BOOK_VIEW */
 		/* OP_GET_CONTACTS */
+		/* OP_GET_CONTACTS_UIDS */
 		gchar *query;
 		/* OP_CANCEL_OPERATION */
 		guint opid;
@@ -150,6 +152,10 @@ operation_thread (gpointer data, gpointer user_data)
 		e_book_backend_get_contact_list (backend, op->book, op->id, op->cancellable, op->d.query);
 		g_free (op->d.query);
 		break;
+	case OP_GET_CONTACTS_UIDS:
+		e_book_backend_get_contact_list_uids (backend, op->book, op->id, op->cancellable, op->d.query);
+		g_free (op->d.query);
+		break;
 	case OP_MODIFY_CONTACT:
 		e_book_backend_modify_contact (backend, op->book, op->id, op->cancellable, op->d.vcard);
 		g_free (op->d.vcard);
@@ -560,6 +566,28 @@ impl_Book_getContactList (EGdbusBook *object, GDBusMethodInvocation *invocation,
 }
 
 static gboolean
+impl_Book_get_contact_list_uids (EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar *in_query, EDataBook *book)
+{
+	OperationData *op;
+
+	if (in_query == NULL || !*in_query) {
+		GError *error = e_data_book_create_error (E_DATA_BOOK_STATUS_INVALID_QUERY, NULL);
+		/* Translators: This is prefix to a detailed error message */
+		data_book_return_error (invocation, error, _("Empty query: "));
+		g_error_free (error);
+		return TRUE;
+	}
+
+	op = op_new (OP_GET_CONTACTS_UIDS, book);
+	op->d.query = g_strdup (in_query);
+
+	e_gdbus_book_complete_get_contact_list_uids (book->priv->gdbus_object, invocation, op->id);
+	e_operation_pool_push (ops_pool, op);
+
+	return TRUE;
+}
+
+static gboolean
 impl_Book_addContact (EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar *in_vcard, EDataBook *book)
 {
 	OperationData *op;
@@ -866,6 +894,30 @@ e_data_book_respond_get_contact_list (EDataBook *book, guint32 opid, GError *err
 }
 
 void
+e_data_book_respond_get_contact_list_uids (EDataBook *book, guint32 opid, GError *error, const GSList *uids)
+{
+	if (error) {
+		/* Translators: This is prefix to a detailed error message */
+		g_prefix_error (&error, "%s", _("Cannot get contact list uids: "));
+		e_gdbus_book_emit_get_contact_list_uids_done (book->priv->gdbus_object, opid, error, NULL);
+		g_error_free (error);
+	} else {
+		gchar **array;
+		const GSList *l;
+		gint i = 0;
+
+		array = g_new0 (gchar *, g_slist_length ((GSList *) uids) + 1);
+		for (l = uids; l != NULL; l = l->next) {
+			array[i++] = e_util_utf8_make_valid (l->data);
+		}
+
+		e_gdbus_book_emit_get_contact_list_uids_done (book->priv->gdbus_object, opid, NULL, (const gchar * const *) array);
+
+		g_strfreev (array);
+	}
+}
+
+void
 e_data_book_respond_create (EDataBook *book, guint32 opid, GError *error, const EContact *contact)
 {
 	gchar *gdbus_uid = NULL;
@@ -1028,6 +1080,7 @@ e_data_book_init (EDataBook *ebook)
 	g_signal_connect (gdbus_object, "handle-refresh", G_CALLBACK (impl_Book_refresh), ebook);
 	g_signal_connect (gdbus_object, "handle-get-contact", G_CALLBACK (impl_Book_getContact), ebook);
 	g_signal_connect (gdbus_object, "handle-get-contact-list", G_CALLBACK (impl_Book_getContactList), ebook);
+	g_signal_connect (gdbus_object, "handle-get-contact-list-uids", G_CALLBACK (impl_Book_get_contact_list_uids), ebook);
 	g_signal_connect (gdbus_object, "handle-authenticate-user", G_CALLBACK (impl_Book_authenticateUser), ebook);
 	g_signal_connect (gdbus_object, "handle-add-contact", G_CALLBACK (impl_Book_addContact), ebook);
 	g_signal_connect (gdbus_object, "handle-remove-contacts", G_CALLBACK (impl_Book_removeContacts), ebook);
diff --git a/addressbook/libedata-book/e-data-book.h b/addressbook/libedata-book/e-data-book.h
index 1f6c5c6..0a1ed32 100644
--- a/addressbook/libedata-book/e-data-book.h
+++ b/addressbook/libedata-book/e-data-book.h
@@ -142,6 +142,7 @@ void		e_data_book_respond_remove_contacts		(EDataBook *book, guint32 opid, GErro
 void		e_data_book_respond_modify			(EDataBook *book, guint32 opid, GError *error, const EContact *contact);
 void		e_data_book_respond_get_contact			(EDataBook *book, guint32 opid, GError *error, const gchar *vcard);
 void		e_data_book_respond_get_contact_list		(EDataBook *book, guint32 opid, GError *error, const GSList *cards);
+void		e_data_book_respond_get_contact_list_uids	(EDataBook *book, guint32 opid, GError *error, const GSList *uids);
 
 void		e_data_book_report_error			(EDataBook *book, const gchar *message);
 void		e_data_book_report_readonly			(EDataBook *book, gboolean readonly);
diff --git a/addressbook/libegdbus/e-gdbus-book.c b/addressbook/libegdbus/e-gdbus-book.c
index c6d2df5..5190d9f 100644
--- a/addressbook/libegdbus/e-gdbus-book.c
+++ b/addressbook/libegdbus/e-gdbus-book.c
@@ -50,6 +50,8 @@ enum
 	__GET_CONTACT_DONE_SIGNAL,
 	__GET_CONTACT_LIST_METHOD,
 	__GET_CONTACT_LIST_DONE_SIGNAL,
+	__GET_CONTACT_LIST_UIDS_METHOD,
+	__GET_CONTACT_LIST_UIDS_DONE_SIGNAL,
 	__ADD_CONTACT_METHOD,
 	__ADD_CONTACT_DONE_SIGNAL,
 	__REMOVE_CONTACTS_METHOD,
@@ -122,6 +124,7 @@ E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID	(GDBUS_BOOK_INTERFACE_NAME,
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID	(GDBUS_BOOK_INTERFACE_NAME, refresh)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRING	(GDBUS_BOOK_INTERFACE_NAME, get_contact)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRV	(GDBUS_BOOK_INTERFACE_NAME, get_contact_list)
+E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRV	(GDBUS_BOOK_INTERFACE_NAME, get_contact_list_uids)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRING	(GDBUS_BOOK_INTERFACE_NAME, add_contact)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID	(GDBUS_BOOK_INTERFACE_NAME, remove_contacts)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID	(GDBUS_BOOK_INTERFACE_NAME, modify_contact)
@@ -151,6 +154,7 @@ e_gdbus_book_default_init (EGdbusBookIface *iface)
 	E_INIT_GDBUS_METHOD_ASYNC_VOID__VOID	(EGdbusBookIface, "refresh",			refresh, __REFRESH_METHOD, __REFRESH_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_ASYNC_STRING__STRING(EGdbusBookIface, "getContact",			get_contact, __GET_CONTACT_METHOD, __GET_CONTACT_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_ASYNC_STRING__STRV	(EGdbusBookIface, "getContactList",		get_contact_list, __GET_CONTACT_LIST_METHOD, __GET_CONTACT_LIST_DONE_SIGNAL)
+	E_INIT_GDBUS_METHOD_ASYNC_STRING__STRV	(EGdbusBookIface, "get_contact_list_uids",	get_contact_list_uids, __GET_CONTACT_LIST_UIDS_METHOD, __GET_CONTACT_LIST_UIDS_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_ASYNC_STRING__STRING(EGdbusBookIface, "addContact",			add_contact, __ADD_CONTACT_METHOD, __ADD_CONTACT_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_ASYNC_STRV__VOID	(EGdbusBookIface, "removeContacts",		remove_contacts, __REMOVE_CONTACTS_METHOD, __REMOVE_CONTACTS_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_ASYNC_STRING__VOID	(EGdbusBookIface, "modifyContact",		modify_contact, __MODIFY_CONTACT_METHOD, __MODIFY_CONTACT_DONE_SIGNAL)
@@ -264,6 +268,26 @@ e_gdbus_book_call_get_contact_list_sync (GDBusProxy *proxy, const gchar *in_quer
 }
 
 void
+e_gdbus_book_call_get_contact_list_uids (GDBusProxy *proxy, const gchar *in_query, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
+{
+	e_gdbus_proxy_call_string ("get_contact_list_uids", e_gdbus_book_call_get_contact_list_uids, E_GDBUS_ASYNC_OP_KEEPER (proxy), in_query, cancellable, callback, user_data);
+}
+
+gboolean
+e_gdbus_book_call_get_contact_list_uids_finish (GDBusProxy *proxy, GAsyncResult *result, gchar ***out_uids, GError **error)
+{
+	return e_gdbus_proxy_finish_call_strv (E_GDBUS_ASYNC_OP_KEEPER (proxy), result, out_uids, error, e_gdbus_book_call_get_contact_list_uids);
+}
+
+gboolean
+e_gdbus_book_call_get_contact_list_uids_sync (GDBusProxy *proxy, const gchar *in_query, gchar ***out_uids, GCancellable *cancellable, GError **error)
+{
+	return e_gdbus_proxy_call_sync_string__strv (proxy, in_query, out_uids, cancellable, error,
+		e_gdbus_book_call_get_contact_list_uids,
+		e_gdbus_book_call_get_contact_list_uids_finish);
+}
+
+void
 e_gdbus_book_call_add_contact (GDBusProxy *proxy, const gchar *in_vcard, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
 {
 	e_gdbus_proxy_call_string ("addContact", e_gdbus_book_call_add_contact, E_GDBUS_ASYNC_OP_KEEPER (proxy), in_vcard, cancellable, callback, user_data);
@@ -505,6 +529,7 @@ DECLARE_EMIT_DONE_SIGNAL_0 (remove,			__REMOVE_DONE_SIGNAL)
 DECLARE_EMIT_DONE_SIGNAL_0 (refresh,			__REFRESH_DONE_SIGNAL)
 DECLARE_EMIT_DONE_SIGNAL_1 (get_contact,		__GET_CONTACT_DONE_SIGNAL, const gchar *)
 DECLARE_EMIT_DONE_SIGNAL_1 (get_contact_list,		__GET_CONTACT_LIST_DONE_SIGNAL, const gchar * const *)
+DECLARE_EMIT_DONE_SIGNAL_1 (get_contact_list_uids,	__GET_CONTACT_LIST_UIDS_DONE_SIGNAL, const gchar * const *)
 DECLARE_EMIT_DONE_SIGNAL_1 (add_contact,		__ADD_CONTACT_DONE_SIGNAL, const gchar *)
 DECLARE_EMIT_DONE_SIGNAL_0 (remove_contacts,		__REMOVE_CONTACTS_DONE_SIGNAL)
 DECLARE_EMIT_DONE_SIGNAL_0 (modify_contact,		__MODIFY_CONTACT_DONE_SIGNAL)
@@ -556,6 +581,7 @@ E_DECLARE_GDBUS_ASYNC_METHOD_0			(book, remove)
 E_DECLARE_GDBUS_ASYNC_METHOD_0			(book, refresh)
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(book, getContact, uid, "s", vcard, "s")
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(book, getContactList, query, "s", vcards, "as")
+E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(book, get_contact_list_uids, query, "s", uids, "as")
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(book, addContact, vcard, "s", uid, "s")
 E_DECLARE_GDBUS_ASYNC_METHOD_1			(book, removeContacts, list, "as")
 E_DECLARE_GDBUS_ASYNC_METHOD_1			(book, modifyContact, vcard, "s")
@@ -575,6 +601,7 @@ static const GDBusMethodInfo * const e_gdbus_book_method_info_pointers[] =
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, refresh),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, getContact),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, getContactList),
+	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, get_contact_list_uids),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, addContact),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, removeContacts),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, modifyContact),
@@ -601,6 +628,7 @@ static const GDBusSignalInfo * const e_gdbus_book_signal_info_pointers[] =
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, refresh_done),
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, getContact_done),
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, getContactList_done),
+	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, get_contact_list_uids_done),
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, addContact_done),
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, removeContacts_done),
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, modifyContact_done),
@@ -806,6 +834,7 @@ e_gdbus_book_proxy_init (EGdbusBookProxy *proxy)
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID   (refresh);
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRING (get_contact);
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRV   (get_contact_list);
+	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRV   (get_contact_list_uids);
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRING (add_contact);
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID   (remove_contacts);
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID   (modify_contact);
diff --git a/addressbook/libegdbus/e-gdbus-book.h b/addressbook/libegdbus/e-gdbus-book.h
index ae0dd9e..4fa97da 100644
--- a/addressbook/libegdbus/e-gdbus-book.h
+++ b/addressbook/libegdbus/e-gdbus-book.h
@@ -134,6 +134,9 @@ struct _EGdbusBookIface
 	gboolean (*handle_get_contact_list)	(EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar *in_query);
 	void	 (*get_contact_list_done)	(EGdbusBook *object, guint arg_opid, const GError *arg_error, gchar ***out_vcards);
 
+	gboolean (*handle_get_contact_list_uids)(EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar *in_query);
+	void	 (*get_contact_list_uids_done)	(EGdbusBook *object, guint arg_opid, const GError *arg_error, gchar ***out_uids);
+
 	gboolean (*handle_add_contact)		(EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar *in_vcard);
 	void	 (*add_contact_done)		(EGdbusBook *object, guint arg_opid, const GError *arg_error, gchar **out_uid);
 
@@ -182,6 +185,10 @@ void		e_gdbus_book_call_get_contact_list (GDBusProxy *proxy, const gchar *in_que
 gboolean	e_gdbus_book_call_get_contact_list_finish (GDBusProxy *proxy, GAsyncResult *result, gchar ***out_vcards, GError **error);
 gboolean	e_gdbus_book_call_get_contact_list_sync (GDBusProxy *proxy, const gchar *in_query, gchar ***out_vcards, GCancellable *cancellable, GError **error);
 
+void		e_gdbus_book_call_get_contact_list_uids (GDBusProxy *proxy, const gchar *in_query, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+gboolean	e_gdbus_book_call_get_contact_list_uids_finish (GDBusProxy *proxy, GAsyncResult *result, gchar ***out_uids, GError **error);
+gboolean	e_gdbus_book_call_get_contact_list_uids_sync (GDBusProxy *proxy, const gchar *in_query, gchar ***out_uids, GCancellable *cancellable, GError **error);
+
 void		e_gdbus_book_call_add_contact (GDBusProxy *proxy, const gchar *in_vcard, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 gboolean	e_gdbus_book_call_add_contact_finish (GDBusProxy *proxy, GAsyncResult *result, gchar **out_uid, GError **error);
 gboolean	e_gdbus_book_call_add_contact_sync (GDBusProxy *proxy, const gchar *in_vcard, gchar **out_uid, GCancellable *cancellable, GError **error);
@@ -230,6 +237,7 @@ gboolean	e_gdbus_book_call_close_sync (GDBusProxy *proxy, GCancellable *cancella
 #define e_gdbus_book_complete_refresh				e_gdbus_complete_async_method
 #define e_gdbus_book_complete_get_contact			e_gdbus_complete_async_method
 #define e_gdbus_book_complete_get_contact_list			e_gdbus_complete_async_method
+#define e_gdbus_book_complete_get_contact_list_uids		e_gdbus_complete_async_method
 #define e_gdbus_book_complete_add_contact			e_gdbus_complete_async_method
 #define e_gdbus_book_complete_remove_contacts			e_gdbus_complete_async_method
 #define e_gdbus_book_complete_modify_contact			e_gdbus_complete_async_method
@@ -246,6 +254,7 @@ void e_gdbus_book_emit_remove_done			(EGdbusBook *object, guint arg_opid, const
 void e_gdbus_book_emit_refresh_done			(EGdbusBook *object, guint arg_opid, const GError *arg_error);
 void e_gdbus_book_emit_get_contact_done			(EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar *out_vcard);
 void e_gdbus_book_emit_get_contact_list_done		(EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar * const *out_vcards);
+void e_gdbus_book_emit_get_contact_list_uids_done	(EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar * const *out_uids);
 void e_gdbus_book_emit_add_contact_done			(EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar *out_uid);
 void e_gdbus_book_emit_remove_contacts_done		(EGdbusBook *object, guint arg_opid, const GError *arg_error);
 void e_gdbus_book_emit_modify_contact_done		(EGdbusBook *object, guint arg_opid, const GError *arg_error);
diff --git a/tests/libebook/client/test-client-async.c b/tests/libebook/client/test-client-async.c
index e3cc419..3e01758 100644
--- a/tests/libebook/client/test-client-async.c
+++ b/tests/libebook/client/test-client-async.c
@@ -7,9 +7,39 @@
 #include "client-test-utils.h"
 
 static void
+print_all_uids_cb (GObject *source_object, GAsyncResult *result, gpointer user_data)
+{
+	EBookClient *book_client;
+	GSList *uids = NULL, *u;
+	GError *error = NULL;
+
+	book_client = E_BOOK_CLIENT (source_object);
+	g_return_if_fail (book_client != NULL);
+
+	if (!e_book_client_get_contacts_uids_finish (book_client, result, &uids, &error)) {
+		report_error ("get contacts uids finish", &error);
+		stop_main_loop (1);
+		return;
+	}
+
+	for (u = uids; u; u = u->next) {
+		const gchar *uid = u->data;
+
+		g_print ("   uid:'%s'\n", uid);
+	}
+
+	g_slist_foreach (uids, (GFunc) g_free, NULL);
+	g_slist_free (uids);
+
+	stop_main_loop (0);
+}
+
+static void
 print_all_emails_cb (GObject *source_object, GAsyncResult *result, gpointer user_data)
 {
 	EBookClient *book_client;
+	EBookQuery *query;
+	gchar *sexp;
 	GSList *contacts = NULL, *c;
 	GError *error = NULL;
 
@@ -31,7 +61,13 @@ print_all_emails_cb (GObject *source_object, GAsyncResult *result, gpointer user
 	g_slist_foreach (contacts, (GFunc) g_object_unref, NULL);
 	g_slist_free (contacts);
 
-	stop_main_loop (0);
+	query = e_book_query_field_exists (E_CONTACT_FULL_NAME);
+	sexp = e_book_query_to_string (query);
+	e_book_query_unref (query);
+
+	e_book_client_get_contacts_uids (book_client, sexp, NULL, print_all_uids_cb, NULL);
+
+	g_free (sexp);
 }
 
 static void
diff --git a/tests/libebook/client/test-client.c b/tests/libebook/client/test-client.c
index 45011ab..5866bc4 100644
--- a/tests/libebook/client/test-client.c
+++ b/tests/libebook/client/test-client.c
@@ -7,6 +7,40 @@
 #include "client-test-utils.h"
 
 static void
+print_all_uids (EBookClient *book)
+{
+	GError *error = NULL;
+	EBookQuery *query;
+	gchar *sexp;
+	gboolean result;
+	GSList *uids, *u;
+
+	query = e_book_query_field_exists (E_CONTACT_FULL_NAME);
+	sexp = e_book_query_to_string (query);
+	e_book_query_unref (query);
+
+	result = e_book_client_get_contacts_uids_sync (book, sexp, &uids, NULL, &error);
+
+	g_free (sexp);
+
+	if (!result) {
+		fprintf (stderr, "Error getting uid list: %s\n", error ? error->message : "Unknown error");
+		if (error)
+			g_error_free (error);
+		exit (1);
+	}
+
+	for (u = uids; u; u = u->next) {
+		const gchar *uid = u->data;
+
+		g_print ("   uid:'%s'\n", uid);
+	}
+
+	g_slist_foreach (uids, (GFunc) g_free, NULL);
+	g_slist_free (uids);
+}
+
+static void
 print_all_emails (EBookClient *book)
 {
 	GError *error = NULL;
@@ -76,6 +110,9 @@ main (gint argc, gchar **argv)
 	printf ("printing all contacts\n");
 	print_all_emails (book_client);
 
+	printf ("printing all uids\n");
+	print_all_uids (book_client);
+
 	g_object_unref (book_client);
 
 	return 0;



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