commit 5089248e732827a84029e1d6cce7d93c08dbc800
Author: Tristan Van Berkom <tristan.van.berkom@gmail.com>
Date:   Wed Jul 20 22:46:23 2011 -0400

    This commit adds e_book_get_revisioned_contact_uid_list().
    
    Like e_book_get_contact_uid_list() except it also pulls a list
    of all the revisions of each coresponding uid.
    
    Patch adds getRevisionedContactUidList gdbus api and adds
    ->get_revisioned_uid_list() on the EBookBackend and
    EBookBackendSync classes.
    
    EBookBackendSync provides a default implementation which uses
    e_book_backend_sync_get_contact_list() to get the appropriate
    uids and revisions.

diff --git a/addressbook/libebook/e-book.c b/addressbook/libebook/e-book.c
index 14a56b5..6977a83 100644
--- a/addressbook/libebook/e-book.c
+++ b/addressbook/libebook/e-book.c
@@ -2197,6 +2197,63 @@ e_book_get_contact_uids (EBook       *book,
 	}
 }
 
+
+
+/**
+ * e_book_get_revisioned_contact_uids:
+ * @book: an #EBook
+ * @query: an #EBookQuery
+ * @uids: a #GList pointer, will be set to the list of contact uids
+ * @revs: a #GList pointer, will be set to the list of contact revisions
+ * @error: a #GError to set on failure
+ *
+ * Query @book with @query, setting @uids to the list of uids of contacts which
+ * matched and @revs to the respective revisions. On failed, @error will be set and %FALSE returned.
+ *
+ * Returns: %TRUE on success, %FALSE otherwise
+ **/
+gboolean
+e_book_get_revisioned_contact_uids (EBook       *book,
+				    EBookQuery  *query,
+				    GList      **uids,
+				    GList      **revs,
+				    GError     **error)
+{
+	GError *err = NULL;
+	gchar **uid_list = NULL, **rev_list = NULL;
+	gchar *sexp;
+
+	e_return_error_if_fail (E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG);
+	e_return_error_if_fail (book->priv->gdbus_book, E_BOOK_ERROR_REPOSITORY_OFFLINE);
+
+	sexp = e_book_query_to_string (query);
+
+	e_gdbus_book_call_get_revisioned_contact_uid_list_sync (book->priv->gdbus_book, 
+								sexp, &uid_list, &rev_list, NULL, &err);
+
+	g_free (sexp);
+
+	if (!err) {
+		GList *l = NULL;
+		gchar **i = uid_list;
+		while (*i != NULL) {
+			l = g_list_prepend (l, *i++);
+		}
+		*uids = g_list_reverse (l);
+
+		l = NULL;
+		i = rev_list;
+		while (*i != NULL) {
+			l = g_list_prepend (l, *i++);
+		}
+		*revs = g_list_reverse (l);
+
+		return TRUE;
+	} else {
+		return unwrap_gerror (err, error);
+	}
+}
+
 static void
 get_contacts_reply (GObject *gdbus_book, GAsyncResult *res, gpointer user_data)
 {
@@ -2273,6 +2330,47 @@ get_contact_uids_reply (GObject *gdbus_book, GAsyncResult *res, gpointer user_da
 	g_slice_free (AsyncData, data);
 }
 
+static void
+get_revisioned_contact_uids_reply (GObject *gdbus_book, GAsyncResult *res, gpointer user_data)
+{
+	gchar **uids = NULL;
+	gchar **revs = NULL;
+	GError *err = NULL, *error = NULL;
+	AsyncData *data = user_data;
+	GList *uid_list = NULL, *rev_list = NULL;
+	EBookDualListAsyncCallback excb = data->excallback;
+	
+	e_gdbus_book_call_get_revisioned_contact_uid_list_finish (E_GDBUS_BOOK (gdbus_book), 
+								  &uids, &revs, res, &error);
+
+	unwrap_gerror (error, &err);
+	
+	if (!error && uids) {
+		gchar **i = uids;
+
+		while (*i != NULL) {
+			uid_list = g_list_prepend (uid_list, *i++);
+		}
+
+		i = revs;
+		while (*i != NULL) {
+			rev_list = g_list_prepend (rev_list, *i++);
+		}
+
+		uid_list = g_list_reverse (uid_list);
+		rev_list = g_list_reverse (rev_list);
+	}
+	
+	if (excb)
+		excb (data->book, err, uid_list, rev_list, data->closure);
+
+	if (err)
+		g_error_free (err);
+
+	g_object_unref (data->book);
+	g_slice_free (AsyncData, data);
+}
+
 #ifndef E_BOOK_DISABLE_DEPRECATED
 /**
  * e_book_async_get_contacts:
@@ -2393,6 +2491,46 @@ e_book_get_contact_uids_async (EBook                 *book,
 	return TRUE;
 }
 
+/**
+ * e_book_get_revisioned_contact_uids_async:
+ * @book: an #EBook
+ * @query: an #EBookQuery
+ * @cb: a function to call when the operation finishes
+ * @closure: data to pass to callback function
+ *
+ * Query @book with @query and get a list of uids and
+ * a list of their revisions.
+ *
+ * Returns: %FALSE on success, %TRUE otherwise
+ **/
+gboolean  
+e_book_get_revisioned_contact_uids_async (EBook                 *book,
+					  EBookQuery            *query,
+					  EBookDualListAsyncCallback cb,
+					  gpointer               closure)
+{
+  	AsyncData *data;
+	gchar *sexp;
+
+	e_return_ex_async_error_dual_val_if_fail (E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG);
+	e_return_ex_async_error_dual_val_if_fail (book->priv->gdbus_book, E_BOOK_ERROR_REPOSITORY_OFFLINE);
+	e_return_ex_async_error_dual_val_if_fail (query, E_BOOK_ERROR_INVALID_ARG);
+
+	sexp = e_book_query_to_string (query);
+
+	data = g_slice_new0 (AsyncData);
+	data->book = g_object_ref (book);
+	data->excallback = cb;
+	data->closure = closure;
+
+	e_gdbus_book_call_get_revisioned_contact_uid_list (book->priv->gdbus_book, sexp, NULL, 
+							   get_revisioned_contact_uids_reply, data);
+
+	g_free (sexp);
+
+	return TRUE;
+}
+
 static GList *
 parse_changes_array (GVariant *var_changes)
 {
diff --git a/addressbook/libebook/e-book.h b/addressbook/libebook/e-book.h
index ab57b3d..a25298a 100644
--- a/addressbook/libebook/e-book.h
+++ b/addressbook/libebook/e-book.h
@@ -85,6 +85,13 @@ typedef void (*EBookContactAsyncCallback)  (EBook *book, const GError *error, EC
 typedef void (*EBookListAsyncCallback)     (EBook *book, const GError *error, GList *list, gpointer closure);
 
 /**
+ * EBookDualListAsyncCallback:
+ *
+ * Since: 2.32
+ **/
+typedef void (*EBookDualListAsyncCallback)     (EBook *book, const GError *error, GList *list_a, GList *list_b, gpointer closure);
+
+/**
  * EBookBookViewAsyncCallback:
  *
  * Since: 2.32
@@ -354,11 +361,17 @@ gboolean e_book_get_contacts               (EBook       *book,
 					    GList      **contacts,
 					    GError     **error);
 
-gboolean e_book_get_contact_uids               (EBook       *book,
+gboolean e_book_get_contact_uids           (EBook       *book,
 					    EBookQuery  *query,
 					    GList      **uids,
 					    GError     **error);
 
+gboolean e_book_get_revisioned_contact_uids(EBook       *book,
+					    EBookQuery  *query,
+					    GList      **uids,
+					    GList      **revs,
+					    GError     **error);
+
 #ifndef E_BOOK_DISABLE_DEPRECATED
 guint     e_book_async_get_contacts        (EBook             *book,
 					    EBookQuery        *query,
@@ -376,6 +389,11 @@ gboolean  e_book_get_contact_uids_async     (EBook                 *book,
 					     EBookListAsyncCallback cb,
 					     gpointer               closure);
 
+gboolean  e_book_get_revisioned_contact_uids_async (EBook                     *book,
+						    EBookQuery                *query,
+						    EBookDualListAsyncCallback cb,
+						    gpointer                   closure);
+
 /* Needed for syncing */
 gboolean e_book_get_changes                (EBook       *book,
 					    const gchar *changeid,
diff --git a/addressbook/libebook/e-error.h b/addressbook/libebook/e-error.h
index 0a4d453..0a25fd6 100644
--- a/addressbook/libebook/e-error.h
+++ b/addressbook/libebook/e-error.h
@@ -83,3 +83,19 @@
       return 0;							\
     }								\
   } G_STMT_END
+
+#define e_return_ex_async_error_dual_val_if_fail(expr, error) G_STMT_START { \
+    if G_LIKELY (expr) {} else {					\
+      GError *err;							\
+      g_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,			\
+             "file %s: line %d (%s): assertion `%s' failed",		\
+             __FILE__, __LINE__, G_STRFUNC, #expr);			\
+      err = g_error_new (E_BOOK_ERROR, (error),				\
+		"file %s: line %d (%s): assertion `%s' failed",		\
+		__FILE__, __LINE__, G_STRFUNC, #expr);			\
+      cb (book, err, NULL, NULL, closure);				\
+      g_error_free (err);						\
+      return 0;								\
+    }									\
+  } G_STMT_END
+
diff --git a/addressbook/libedata-book/e-book-backend-sync.c b/addressbook/libedata-book/e-book-backend-sync.c
index bda9a1d..bccf58f 100644
--- a/addressbook/libedata-book/e-book-backend-sync.c
+++ b/addressbook/libedata-book/e-book-backend-sync.c
@@ -271,6 +271,78 @@ e_book_backend_sync_get_contact_uid_list (EBookBackendSync *backend,
 }
 
 /**
+ * e_book_backend_sync_get_revisioned_contact_uid_list:
+ * @backend: an #EBookBackendSync
+ * @book: an #EDataBook
+ * @opid: the unique ID of the operation
+ * @query: an s-expression of the query to perform
+ * @contact_uids: a pointer to a location to store the resulting list of UID strings
+ * @contact_revs: a pointer to a location to store the resulting list of revision strings
+ * @error: #GError to set, when something fails
+ *
+ * Gets a list of contact uids and thier revisions from @book. The lists and their elements must be freed
+ * by the caller.
+ **/
+void
+e_book_backend_sync_get_revisioned_contact_uid_list (EBookBackendSync *backend,
+						     EDataBook *book,
+						     guint32 opid,
+						     const gchar *query,
+						     GList **contact_uids,
+						     GList **contact_revs,
+						     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 (contact_uids, E_DATA_BOOK_STATUS_INVALID_ARG);
+
+	if (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_revisioned_contact_uid_list_sync != NULL) {
+		(* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_revisioned_contact_uid_list_sync) (backend, book, opid, query, contact_uids, contact_revs, error);
+	} else {
+		/* inefficient fallback code */
+		GList *vcards = NULL;
+		GError *local_error = NULL;
+
+		e_book_backend_sync_get_contact_list (backend, book, opid, query, &vcards, &local_error);
+
+		if (local_error) {
+			g_propagate_error (error, local_error);
+		} else {
+			GList *v;
+
+			*contact_uids = NULL;
+
+			for (v = vcards; v; v = v->next) {
+				EVCard *card = e_vcard_new_from_string (v->data);
+				EVCardAttribute *uid_attr, *rev_attr;
+
+				if (!card)
+					continue;
+
+				uid_attr = e_vcard_get_attribute (card, EVC_UID);
+				rev_attr = e_vcard_get_attribute (card, EVC_REV);
+
+				if (uid_attr && rev_attr) {
+					*contact_uids = g_list_prepend (*contact_uids, 
+									e_vcard_attribute_get_value (uid_attr));
+					*contact_revs = g_list_prepend (*contact_revs, 
+									e_vcard_attribute_get_value (rev_attr));
+				}
+
+				g_object_unref (card);
+			}
+
+			*contact_uids = g_list_reverse (*contact_uids);
+			*contact_revs = g_list_reverse (*contact_revs);
+		}
+
+		g_list_foreach (vcards, (GFunc) g_free, NULL);
+		g_list_free (vcards);
+	}
+}
+
+/**
  * e_book_backend_sync_get_changes:
  * @backend: an #EBookBackendSync
  * @book: an #EDataBook
@@ -507,6 +579,8 @@ _e_book_backend_get_contact_list (EBookBackend *backend,
 	e_book_backend_sync_get_contact_list (E_BOOK_BACKEND_SYNC (backend), book, opid, query, &cards, &error);
 
 	e_data_book_respond_get_contact_list (book, opid, error, cards);
+
+	g_list_free (cards);
 }
 
 static void
@@ -521,6 +595,27 @@ _e_book_backend_get_contact_uid_list (EBookBackend *backend,
 	e_book_backend_sync_get_contact_uid_list (E_BOOK_BACKEND_SYNC (backend), book, opid, query, &uids, &error);
 
 	e_data_book_respond_get_contact_uid_list (book, opid, error, uids);
+
+	g_list_free (uids);
+}
+
+static void
+_e_book_backend_get_revisioned_contact_uid_list (EBookBackend *backend,
+						 EDataBook    *book,
+						 guint32       opid,
+						 const gchar   *query)
+{
+	GError *error = NULL;
+	GList *uids = NULL;
+	GList *revs = NULL;
+
+	e_book_backend_sync_get_revisioned_contact_uid_list (E_BOOK_BACKEND_SYNC (backend), 
+							     book, opid, query, &uids, &revs, &error);
+
+	e_data_book_respond_get_revisioned_contact_uid_list (book, opid, error, uids, revs);
+
+	g_list_free (uids);
+	g_list_free (revs);
 }
 
 static void
@@ -649,6 +744,7 @@ e_book_backend_sync_class_init (EBookBackendSyncClass *klass)
 	backend_class->get_contact = _e_book_backend_get_contact;
 	backend_class->get_contact_list = _e_book_backend_get_contact_list;
 	backend_class->get_contact_uid_list = _e_book_backend_get_contact_uid_list;
+	backend_class->get_revisioned_contact_uid_list = _e_book_backend_get_revisioned_contact_uid_list;
 	backend_class->get_changes = _e_book_backend_get_changes;
 	backend_class->authenticate_user = _e_book_backend_authenticate_user;
 	backend_class->get_required_fields = _e_book_backend_get_required_fields;
diff --git a/addressbook/libedata-book/e-book-backend-sync.h b/addressbook/libedata-book/e-book-backend-sync.h
index e092884..e58d4a8 100644
--- a/addressbook/libedata-book/e-book-backend-sync.h
+++ b/addressbook/libedata-book/e-book-backend-sync.h
@@ -48,6 +48,9 @@ struct _EBookBackendSyncClass {
 	void (*get_contact_uid_list_sync) (EBookBackendSync *backend, EDataBook *book,
 							     guint32 opid,
 							     const gchar *query, GList **contact_uids, GError **perror);
+	void (*get_revisioned_contact_uid_list_sync) (EBookBackendSync *backend, EDataBook *book,
+						      guint32 opid, const gchar *query, 
+						      GList **contact_uids, GList **contact_revs, GError **perror);
 	void (*get_changes_sync) (EBookBackendSync *backend, EDataBook *book,
 						    guint32 opid,
 						    const gchar *change_id, GList **changes, GError **perror);
@@ -88,6 +91,7 @@ void e_book_backend_sync_modify_contact  (EBookBackendSync *backend, EDataBook *
 void e_book_backend_sync_get_contact (EBookBackendSync *backend, EDataBook *book, guint32 opid, const gchar *id, gchar **vcard, GError **perror);
 void e_book_backend_sync_get_contact_list (EBookBackendSync *backend, EDataBook *book, guint32 opid, const gchar *query, GList **contacts, GError **perror);
 void e_book_backend_sync_get_contact_uid_list (EBookBackendSync *backend, EDataBook *book, guint32 opid, const gchar *query, GList **contacts, GError **perror);
+void e_book_backend_sync_get_revisioned_contact_uid_list (EBookBackendSync *backend, EDataBook *book, guint32 opid, const gchar *query, GList **contact_uids, GList **contact_revs, GError **perror);
 void e_book_backend_sync_get_changes (EBookBackendSync *backend, EDataBook *book, guint32 opid, const gchar *change_id, GList **changes, GError **perror);
 void e_book_backend_sync_authenticate_user (EBookBackendSync *backend, EDataBook *book, guint32 opid, const gchar *user, const gchar *passwd, const gchar *auth_method, GError **perror);
 
diff --git a/addressbook/libedata-book/e-book-backend.c b/addressbook/libedata-book/e-book-backend.c
index 7a15c3f..539253d 100644
--- a/addressbook/libedata-book/e-book-backend.c
+++ b/addressbook/libedata-book/e-book-backend.c
@@ -505,6 +505,28 @@ e_book_backend_get_contact_uid_list (EBookBackend *backend,
 	(* E_BOOK_BACKEND_GET_CLASS (backend)->get_contact_uid_list) (backend, book, opid, query);
 }
 
+void
+e_book_backend_get_revisioned_contact_uid_list (EBookBackend             *backend,
+						EDataBook                *book,
+						guint32                   opid,
+						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);
+
+	if (E_BOOK_BACKEND_GET_CLASS (backend)->get_contact_uid_list)
+		(* E_BOOK_BACKEND_GET_CLASS (backend)->get_revisioned_contact_uid_list) (backend, book, opid, query);
+	else {
+		GError *error = e_data_book_create_error_fmt
+			(E_DATA_BOOK_STATUS_NOT_SUPPORTED,
+			 "Backend '%s' does not implement get_revisioned_contact_uid_list()",
+			 G_OBJECT_TYPE_NAME (backend));
+
+		e_data_book_respond_get_revisioned_contact_uid_list (book, opid, error, NULL, NULL);
+	}
+}
+
 /**
  * e_book_backend_start_book_view:
  * @backend: an #EBookBackend
diff --git a/addressbook/libedata-book/e-book-backend.h b/addressbook/libedata-book/e-book-backend.h
index bae5d29..1a7a8a1 100644
--- a/addressbook/libedata-book/e-book-backend.h
+++ b/addressbook/libedata-book/e-book-backend.h
@@ -60,6 +60,7 @@ struct _EBookBackendClass {
 	void (*get_contact) (EBookBackend *backend, EDataBook *book, guint32 opid, const gchar *id);
 	void (*get_contact_list) (EBookBackend *backend, EDataBook *book, guint32 opid, const gchar *query);
 	void (*get_contact_uid_list) (EBookBackend *backend, EDataBook *book, guint32 opid, const gchar *query);
+	void (*get_revisioned_contact_uid_list) (EBookBackend *backend, EDataBook *book, guint32 opid, const gchar *query);
 	void (*start_book_view) (EBookBackend *backend, EDataBookView *book_view);
 	void (*stop_book_view) (EBookBackend *backend, EDataBookView *book_view);
 	void (*get_changes) (EBookBackend *backend, EDataBook *book, guint32 opid, const gchar *change_id);
@@ -140,6 +141,10 @@ void        e_book_backend_get_contact_uid_list     (EBookBackend             *b
 						     EDataBook                *book,
 						     guint32                   opid,
 						     const gchar               *query);
+void        e_book_backend_get_revisioned_contact_uid_list (EBookBackend             *backend,
+						     EDataBook                *book,
+						     guint32                   opid,
+						     const gchar               *query);
 void        e_book_backend_get_changes              (EBookBackend             *backend,
 						     EDataBook                *book,
 						     guint32                   opid,
diff --git a/addressbook/libedata-book/e-data-book.c b/addressbook/libedata-book/e-data-book.c
index 179dd40..2a08ecf 100644
--- a/addressbook/libedata-book/e-data-book.c
+++ b/addressbook/libedata-book/e-data-book.c
@@ -46,6 +46,7 @@ struct _EDataBookPrivate
 };
 
 static void return_error_and_list (EGdbusBook *gdbus_object, void (* complete_func) (EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar * const *out_array), guint32 opid, GError *error, const gchar *error_fmt, GList *list, gboolean free_data);
+static void return_error_and_list_list (EGdbusBook *gdbus_object, void (* complete_func) (EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar * const *out_array_a, const gchar * const *out_array_b), guint32 opid, GError *error, const gchar *error_fmt, GList *list_a, GList *list_b, gboolean free_data);
 static void data_book_return_error (GDBusMethodInvocation *invocation, const GError *error, const gchar *error_fmt);
 
 static GThreadPool *op_pool = NULL;
@@ -57,6 +58,7 @@ typedef enum {
 	OP_GET_CONTACT,
 	OP_GET_CONTACTS,
 	OP_GET_CONTACT_UIDS,
+	OP_GET_REVISIONED_CONTACT_UIDS,
 	OP_MODIFY_CONTACT,
 	OP_REMOVE_CONTACTS,
 	OP_GET_CHANGES,
@@ -83,6 +85,7 @@ typedef struct {
 		gchar *uid;
 		/* OP_GET_CONTACTS */
 		/* OP_GET_CONTACT_UIDS */
+		/* OP_GET_REVISIONED_CONTACT_UIDS */
 		gchar *query;
 		/* OP_MODIFY_CONTACT */
 		gchar **vcards;
@@ -130,6 +133,10 @@ operation_thread (gpointer data, gpointer user_data)
 		e_book_backend_get_contact_uid_list (backend, op->book, op->id, op->d.query);
 		g_free (op->d.query);
 		break;
+	case OP_GET_REVISIONED_CONTACT_UIDS:
+		e_book_backend_get_revisioned_contact_uid_list (backend, op->book, op->id, op->d.query);
+		g_free (op->d.query);
+		break;
 	case OP_MODIFY_CONTACT:
 		e_book_backend_modify_contact (backend, op->book, op->id, op->d.vcard);
 		g_free (op->d.vcard);
@@ -475,6 +482,35 @@ e_data_book_respond_get_contact_uid_list (EDataBook *book, guint32 opid, GError
 	return_error_and_list (book->priv->gdbus_object, e_gdbus_book_complete_get_contact_uid_list, opid, error, _("Cannot get contact uid list: %s"), uids, TRUE);
 }
 
+
+static gboolean
+impl_Book_getRevisionedContactUidList (EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar *query, EDataBook *book)
+{
+	OperationData *op;
+
+	if (query == NULL || query[0] == '\0') {
+		GError *error = e_data_book_create_error (E_DATA_BOOK_STATUS_INVALID_QUERY, NULL);
+		/* Translators: The '%s' is replaced with a detailed error message */
+		data_book_return_error (invocation, error, _("Empty query: %s"));
+		g_error_free (error);
+		return TRUE;
+	}
+
+	op = op_new (OP_GET_REVISIONED_CONTACT_UIDS, book, invocation);
+	op->d.query = g_strdup (query);
+	g_thread_pool_push (op_pool, op, NULL);
+
+	return TRUE;
+}
+
+void
+e_data_book_respond_get_revisioned_contact_uid_list (EDataBook *book, guint32 opid, GError *error, GList *uids, GList *revs)
+{
+	/* Translators: The '%s' is replaced with a detailed error message */
+	return_error_and_list_list (book->priv->gdbus_object, e_gdbus_book_complete_get_revisioned_contact_uid_list, opid, error, _("Cannot get contact uid list: %s"), uids, revs, TRUE);
+}
+
+
 static gboolean
 impl_Book_authenticateUser (EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar *IN_user, const gchar *IN_passwd, const gchar *IN_auth_method, EDataBook *book)
 {
@@ -880,6 +916,47 @@ return_error_and_list (EGdbusBook *gdbus_object, void (* complete_func) (EGdbusB
 	}
 }
 
+static void
+return_error_and_list_list (EGdbusBook *gdbus_object, void (* complete_func) (EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar * const *out_array_a, const gchar * const *out_array_b), guint32 opid, GError *error, const gchar *error_fmt, GList *list_a, GList *list_b, gboolean free_data)
+{
+	GDBusMethodInvocation *invocation = opid_fetch (opid);
+
+	g_return_if_fail (error_fmt != NULL);
+	g_return_if_fail (complete_func != NULL);
+
+	if (error) {
+		data_book_return_error (invocation, error, error_fmt);
+		g_error_free (error);
+	} else {
+		gchar **array_a, **array_b;
+		GList *l;
+		gint   i;
+
+		array_a = g_new0 (gchar *, g_list_length (list_a) + 1);
+		array_b = g_new0 (gchar *, g_list_length (list_b) + 1);
+
+		for (l = list_a, i = 0; l != NULL; l = l->next) {
+			array_a[i++] = l->data;
+		}
+
+		for (l = list_b, i = 0; l != NULL; l = l->next) {
+			array_b[i++] = l->data;
+		}
+
+		complete_func (gdbus_object, invocation,
+			       (const gchar * const *) array_a,
+			       (const gchar * const *) array_b);
+
+		if (free_data) {
+			g_strfreev (array_a);
+			g_strfreev (array_b);
+		} else {
+			g_free (array_a);
+			g_free (array_b);
+		}
+	}
+}
+
 /**
  * e_data_book_register_gdbus_object:
  *
@@ -915,6 +992,7 @@ e_data_book_init (EDataBook *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-uid-list", G_CALLBACK (impl_Book_getContactUidList), ebook);
+	g_signal_connect (gdbus_object, "handle-get-revisioned-contact-uid-list", G_CALLBACK (impl_Book_getRevisionedContactUidList), 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 b57f81d..c51e05a 100644
--- a/addressbook/libedata-book/e-data-book.h
+++ b/addressbook/libedata-book/e-data-book.h
@@ -159,6 +159,11 @@ void                    e_data_book_respond_get_contact_uid_list (EDataBook *boo
 							          guint32 opid,
 							          GError *error,
 							          GList *uids);
+void                    e_data_book_respond_get_revisioned_contact_uid_list (EDataBook *book, 
+									     guint32 opid, 
+									     GError *error, 
+									     GList *uids, 
+									     GList *revs);
 void                    e_data_book_respond_get_changes    (EDataBook *book,
 							    guint32 opid,
 							    GError *error,
diff --git a/addressbook/libegdbus/e-gdbus-egdbusbook.c b/addressbook/libegdbus/e-gdbus-egdbusbook.c
index 92a92c3..21dc75c 100644
--- a/addressbook/libegdbus/e-gdbus-egdbusbook.c
+++ b/addressbook/libegdbus/e-gdbus-egdbusbook.c
@@ -73,6 +73,7 @@ enum
   __GET_CONTACT_METHOD,
   __GET_CONTACT_LIST_METHOD,
   __GET_CONTACT_UID_LIST_METHOD,
+  __GET_REVISIONED_CONTACT_UID_LIST_METHOD,
   __AUTHENTICATE_USER_METHOD,
   __ADD_CONTACT_METHOD,
   __REMOVE_CONTACTS_METHOD,
@@ -266,6 +267,7 @@ e_gdbus_book_default_init (EGdbusBookIface *iface)
   g_hash_table_insert (_method_name_to_id, (gpointer) "getContact", GUINT_TO_POINTER (__GET_CONTACT_METHOD));
   g_hash_table_insert (_method_name_to_id, (gpointer) "getContactList", GUINT_TO_POINTER (__GET_CONTACT_LIST_METHOD));
   g_hash_table_insert (_method_name_to_id, (gpointer) "getContactUidList", GUINT_TO_POINTER (__GET_CONTACT_UID_LIST_METHOD));
+  g_hash_table_insert (_method_name_to_id, (gpointer) "getRevisionedContactUidList", GUINT_TO_POINTER (__GET_REVISIONED_CONTACT_UID_LIST_METHOD));
   g_hash_table_insert (_method_name_to_id, (gpointer) "authenticateUser", GUINT_TO_POINTER (__AUTHENTICATE_USER_METHOD));
   g_hash_table_insert (_method_name_to_id, (gpointer) "addContact", GUINT_TO_POINTER (__ADD_CONTACT_METHOD));
   g_hash_table_insert (_method_name_to_id, (gpointer) "removeContacts", GUINT_TO_POINTER (__REMOVE_CONTACTS_METHOD));
@@ -530,6 +532,34 @@ e_gdbus_book_default_init (EGdbusBookIface *iface)
                   G_TYPE_DBUS_METHOD_INVOCATION,
                   G_TYPE_STRING);
     
+
+  /**
+   * EGdbusBook::handle-get-revisioned-contact-uid-list:
+   * @object: The exported object emitting the signal.
+   * @invocation: A #GDBusMethodInvocation object that can be used to return a value or error.
+   * @query: Parameter.
+   *
+   * On exported objects, this signal is emitted when a remote process (identified by @invocation) invokes the <literal>getRevisionedContactUidList</literal> D-Bus method on @object. Use e_gdbus_book_complete_get_revisioned_contact_uid_list() to return a value or g_dbus_method_invocation_return_error() to return an error.
+   *
+   * The signal is emitted in the thread-default main loop of the thread that e_gdbus_book_register_object() was called from.
+   *
+   * On proxies, this signal is never emitted.
+   *
+   * Returns: %TRUE if you want to handle the method call (will stop further handlers from being called), %FALSE otherwise.
+   */
+  signals[__GET_REVISIONED_CONTACT_UID_LIST_METHOD] =
+    g_signal_new ("handle-get-revisioned-contact-uid-list",
+                  G_TYPE_FROM_INTERFACE (iface),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (EGdbusBookIface, handle_get_revisioned_contact_uid_list),
+                  g_signal_accumulator_true_handled,
+                  NULL,
+                  _e_gdbus_gdbus_cclosure_marshaller_BOOLEAN__OBJECT_STRING,
+                  G_TYPE_BOOLEAN,
+                  2,
+                  G_TYPE_DBUS_METHOD_INVOCATION,
+                  G_TYPE_STRING);
+
   /**
    * EGdbusBook::handle-authenticate-user:
    * @object: The exported object emitting the signal.
@@ -1519,6 +1549,135 @@ _out:
   return _ret;
 }
 
+
+
+/**
+ * e_gdbus_book_call_get_revisioned_contact_uid_list:
+ * @proxy: A #EGdbusBook.
+ * @in_query: Method parameter.
+ * @cancellable: A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't care about the result of the method invocation.
+ * @user_data: Data to pass to @callback.
+ *
+ * Invokes the <literal>org.gnome.evolution.dataserver.addressbook.Book.getRevisionedContactUidList</literal>
+ * D-Bus method on the remote object represented by @proxy.
+ *
+ * This is an asynchronous method. When the operation is finished,
+ * callback will be invoked in the thread-default main loop of the
+ * thread you are calling this method from. You can then call
+ * e_gdbus_book_call_get_revisioned_contact_uid_list_finish() to get the result of the operation.
+ *
+ * See e_gdbus_book_call_get_revisioned_contact_uid_list_sync() for the synchronous version of this method.
+ */
+void e_gdbus_book_call_get_revisioned_contact_uid_list (
+        EGdbusBook *proxy,
+        const gchar *in_query,
+        GCancellable *cancellable,
+        GAsyncReadyCallback callback,
+        gpointer user_data)
+{
+  GVariant *_params;
+  _params = g_variant_new ("(s)",
+                           in_query);
+  g_dbus_proxy_call (G_DBUS_PROXY (proxy),
+                     "getRevisionedContactUidList",
+                     _params,
+                     G_DBUS_CALL_FLAGS_NONE,
+                     -1,
+                     cancellable,
+                     callback,
+                     user_data);
+}
+
+/**
+ * e_gdbus_book_call_get_revisioned_contact_uid_list_finish:
+ * @proxy: A #EGdbusBook.
+ * @out_uids: Return location for out uid array parameter or %NULL. Free with g_strfreev().
+ * @out_revs: Return location for out revision array parameter or %NULL. Free with g_strfreev().
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to e_gdbus_book_call_get_contact_uid_list().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes invoking the <literal>org.gnome.evolution.dataserver.addressbook.Book.getRevisionedContactUidList</literal>
+ * D-Bus method on the remote object represented by @proxy.
+ *
+ * Returns: %TRUE if the call succeeded, %FALSE if @error is set.
+ */
+gboolean e_gdbus_book_call_get_revisioned_contact_uid_list_finish (
+        EGdbusBook *proxy,
+        gchar ***out_uids,
+        gchar ***out_revs,
+        GAsyncResult *res,
+        GError **error)
+{
+  gboolean _ret = FALSE;
+  GVariant *_result;
+  _result = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error);
+  if (_result == NULL)
+    goto _out;
+  {
+    g_variant_get (_result,
+                   "(^as^as)",
+                   out_uids,
+		   out_revs);
+  }
+  g_variant_unref (_result);
+  _ret = TRUE;
+_out:
+  return _ret;
+}
+
+/**
+ * e_gdbus_book_call_get_revisioned_contact_uid_list_sync:
+ * @proxy: A #EGdbusBook.
+ * @in_query: Method parameter.
+ * @out_uids: Return location for out uid array parameter or %NULL. Free with g_strfreev().
+ * @out_revs: Return location for out revision array parameter or %NULL. Free with g_strfreev().
+ * @cancellable: A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously invokes the <literal>org.gnome.evolution.dataserver.addressbook.Book.getRevisionedContactUidList</literal>
+ * D-Bus method on the remote object represented by @proxy.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * e_gdbus_book_call_get_revisioned_contact_uid_list() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the call succeeded, %FALSE if @error is set.
+ */
+gboolean e_gdbus_book_call_get_revisioned_contact_uid_list_sync (
+        EGdbusBook *proxy,
+        const gchar *in_query,
+        gchar ***out_uids,
+        gchar ***out_revs,
+        GCancellable *cancellable,
+        GError **error)
+{
+  gboolean _ret = FALSE;
+  GVariant *_params;
+  GVariant *_result;
+  _params = g_variant_new ("(s)",
+                           in_query);
+  _result = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy),
+                                   "getRevisionedContactUidList",
+                                   _params,
+                                   G_DBUS_CALL_FLAGS_NONE,
+                                   -1,
+                                   cancellable,
+                                   error);
+  if (_result == NULL)
+    goto _out;
+  {
+    g_variant_get (_result,
+                   "(^as^as)",
+                   out_uids,
+		   out_revs);
+  }
+  g_variant_unref (_result);
+  _ret = TRUE;
+_out:
+  return _ret;
+}
+
+
 /**
  * e_gdbus_book_call_authenticate_user:
  * @proxy: A #EGdbusBook.
@@ -3022,6 +3181,34 @@ void e_gdbus_book_complete_get_contact_uid_list (
 }
 
 /**
+ * e_gdbus_book_complete_get_revisioned_contact_uid_list:
+ * @object: A #EGdbusBook.
+ * @invocation: A #GDBusMethodInvocation.
+ * @out_uids: uid array to return.
+ * @out_revs: revision array to return.
+ *
+ * Completes handling the <literal>org.gnome.evolution.dataserver.addressbook.Book.getRevisionedContactUidList</literal>
+ * D-Bus method invocation by returning a value.
+ *
+ * If you want to return an error, use g_dbus_method_invocation_return_error()
+ * or similar instead.
+ *
+ * This method will free @invocation, you cannot use it afterwards.
+ */
+void e_gdbus_book_complete_get_revisioned_contact_uid_list (
+        EGdbusBook *object,
+        GDBusMethodInvocation *invocation,
+        const gchar * const *out_uids,
+        const gchar * const *out_revs)
+{
+  GVariant *_params;
+  _params = g_variant_new ("(^as^as)",
+                           out_uids,
+			   out_revs);
+  g_dbus_method_invocation_return_value (invocation, _params);
+}
+
+/**
  * e_gdbus_book_complete_authenticate_user:
  * @object: A #EGdbusBook.
  * @invocation: A #GDBusMethodInvocation.
@@ -3555,6 +3742,49 @@ static const GDBusMethodInfo e_gdbus_book_method_getContactUidList =
   (GDBusAnnotationInfo **) NULL,
 };
 
+static const GDBusArgInfo e_gdbus_book_method_in_getRevisionedContactUidList_query =
+{
+  -1,
+  (gchar *) "query",
+  (gchar *) "s",
+  (GDBusAnnotationInfo **) NULL,
+};
+static const GDBusArgInfo * const e_gdbus_book_method_in_getRevisionedContactUidList_arg_pointers[] =
+{
+  &e_gdbus_book_method_in_getRevisionedContactUidList_query,
+  NULL
+};
+
+static const GDBusArgInfo e_gdbus_book_method_out_getRevisionedContactUidList_uids =
+{
+  -1,
+  (gchar *) "uids",
+  (gchar *) "as",
+  (GDBusAnnotationInfo **) NULL,
+};
+static const GDBusArgInfo e_gdbus_book_method_out_getRevisionedContactUidList_revs =
+{
+  -1,
+  (gchar *) "revs",
+  (gchar *) "as",
+  (GDBusAnnotationInfo **) NULL,
+};
+static const GDBusArgInfo * const e_gdbus_book_method_out_getRevisionedContactUidList_arg_pointers[] =
+{
+  &e_gdbus_book_method_out_getRevisionedContactUidList_uids,
+  &e_gdbus_book_method_out_getRevisionedContactUidList_revs,
+  NULL
+};
+
+static const GDBusMethodInfo e_gdbus_book_method_getRevisionedContactUidList =
+{
+  -1,
+  (gchar *) "getRevisionedContactUidList",
+  (GDBusArgInfo **) &e_gdbus_book_method_in_getRevisionedContactUidList_arg_pointers,
+  (GDBusArgInfo **) &e_gdbus_book_method_out_getRevisionedContactUidList_arg_pointers,
+  (GDBusAnnotationInfo **) NULL,
+};
+
 static const GDBusArgInfo e_gdbus_book_method_in_authenticateUser_user =
 {
   -1,
@@ -3872,6 +4102,7 @@ static const GDBusMethodInfo * const e_gdbus_book_method_info_pointers[] =
   &e_gdbus_book_method_getContact,
   &e_gdbus_book_method_getContactList,
   &e_gdbus_book_method_getContactUidList,
+  &e_gdbus_book_method_getRevisionedContactUidList,
   &e_gdbus_book_method_authenticateUser,
   &e_gdbus_book_method_addContact,
   &e_gdbus_book_method_removeContacts,
@@ -3968,6 +4199,7 @@ handle_method_call (GDBusConnection       *connection,
 
     case __GET_CONTACT_LIST_METHOD:
     case __GET_CONTACT_UID_LIST_METHOD:
+    case __GET_REVISIONED_CONTACT_UID_LIST_METHOD:
       {
         EGdbusBook *object = E_GDBUS_BOOK (user_data);
         gboolean handled;
diff --git a/addressbook/libegdbus/e-gdbus-egdbusbook.h b/addressbook/libegdbus/e-gdbus-egdbusbook.h
index ae056c1..247b2d6 100644
--- a/addressbook/libegdbus/e-gdbus-egdbusbook.h
+++ b/addressbook/libegdbus/e-gdbus-egdbusbook.h
@@ -35,6 +35,7 @@ typedef struct _EGdbusBook EGdbusBook; /* Dummy typedef */
  * @handle_remove: Handler for the #EGdbusBook::handle-remove signal.
  * @handle_get_revision: Handler for the #EGdbusBook::handle-get-revision signal.
  * @handle_get_contact: Handler for the #EGdbusBook::handle-get-contact signal.
+ * @handle_get_revisioned_contact_list: Handler for the #EGdbusBook::handle-get-revisioned-contact-list signal.
  * @handle_get_contact_list: Handler for the #EGdbusBook::handle-get-contact-list signal.
  * @handle_get_contact_uid_list: Handler for the #EGdbusBook::handle-get-contact-uid-list signal.
  * @handle_authenticate_user: Handler for the #EGdbusBook::handle-authenticate-user signal.
@@ -210,6 +211,10 @@ struct _EGdbusBookIface
         EGdbusBook *object,
         GDBusMethodInvocation *invocation,
         const gchar *in_query);
+  gboolean (*handle_get_revisioned_contact_uid_list) (
+        EGdbusBook *object,
+        GDBusMethodInvocation *invocation,
+        const gchar *in_query);
   gboolean (*handle_authenticate_user) (
         EGdbusBook *object,
         GDBusMethodInvocation *invocation,
@@ -373,6 +378,28 @@ gboolean e_gdbus_book_call_get_contact_uid_list_sync (
         GCancellable *cancellable,
         GError **error);
 
+void e_gdbus_book_call_get_revisioned_contact_uid_list (
+        EGdbusBook *proxy,
+        const gchar *in_query,
+        GCancellable *cancellable,
+        GAsyncReadyCallback callback,
+        gpointer user_data);
+
+gboolean e_gdbus_book_call_get_revisioned_contact_uid_list_finish (
+        EGdbusBook *proxy,
+        gchar ***out_uids,
+        gchar ***out_revs,
+        GAsyncResult *res,
+        GError **error);
+
+gboolean e_gdbus_book_call_get_revisioned_contact_uid_list_sync (
+        EGdbusBook *proxy,
+        const gchar *in_query,
+        gchar ***out_uids,
+        gchar ***out_revs,
+        GCancellable *cancellable,
+        GError **error);
+
 void e_gdbus_book_call_authenticate_user (
         EGdbusBook *proxy,
         const gchar *in_user,
@@ -628,6 +655,12 @@ void e_gdbus_book_complete_get_contact_uid_list (
         GDBusMethodInvocation *invocation,
         const gchar * const *out_uids);
 
+void e_gdbus_book_complete_get_revisioned_contact_uid_list (
+        EGdbusBook *object,
+        GDBusMethodInvocation *invocation,
+        const gchar * const *out_uids,
+        const gchar * const *out_revs);
+
 void e_gdbus_book_complete_authenticate_user (
         EGdbusBook *object,
         GDBusMethodInvocation *invocation);
