commit bf19cd6d2f425d75c524466bc415273199e435fd
Author: Tristan Van Berkom <tristan.van.berkom@gmail.com>
Date:   Fri Jul 22 19:58:20 2011 -0400

    Added e_book_client_get_revision().
    
    Adds e_book_client_get_revision() async/sync api to access an
    opaque revision string which will be NULL for a backend that
    does not implement it. Adds all the needed gdbus machinery and
    implements a revision in the file backend.
    
    The revision string is a timestamp + integer (the integer
    is incremented every revision bump to avoid any races while
    multiple revisions can occur inside one second).
    
    Bug: https://bugzilla.gnome.org/show_bug.cgi?id=652175

diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c
index 3a87056..b1ea9ff 100644
--- a/addressbook/backends/file/e-book-backend-file.c
+++ b/addressbook/backends/file/e-book-backend-file.c
@@ -61,6 +61,8 @@
 #define E_BOOK_BACKEND_FILE_VERSION_NAME "PAS-DB-VERSION"
 #define E_BOOK_BACKEND_FILE_VERSION "0.2"
 
+#define E_BOOK_BACKEND_FILE_REVISION_NAME "PAS-DB-REVISION"
+
 #define PAS_ID_PREFIX "pas-id-"
 
 #define SQLITEDB_EMAIL_ID    "addressbook@localbackend.com"
@@ -76,16 +78,12 @@ G_DEFINE_TYPE (EBookBackendFile, e_book_backend_file, E_TYPE_BOOK_BACKEND_SYNC)
 struct _EBookBackendFilePrivate {
 	gchar     *dirname;
 	gchar     *filename;
+	gchar     *revision;
+	gint       rev_counter;
 	DB        *file_db;
 	DB_ENV    *env;
 
 	EBookBackendSqliteDB *sqlitedb;
-
-	/* for future use */
-	gpointer reserved1;
-	gpointer reserved2;
-	gpointer reserved3;
-	gpointer reserved4;
 };
 
 G_LOCK_DEFINE_STATIC (db_environments);
@@ -212,6 +210,7 @@ build_sqlitedb (EBookBackendFilePrivate *bfpriv)
 	GSList         *contacts = NULL;
 	GError         *error = NULL;
 	gboolean        skipped_version = FALSE;
+	gboolean        skipped_revision = FALSE;
 
 	if (!db) {
 		g_warning (G_STRLOC ": Not opened yet");
@@ -230,13 +229,21 @@ build_sqlitedb (EBookBackendFilePrivate *bfpriv)
 	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 (skipped_version || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
+		gboolean skip = FALSE;
+
+		/* don't include the version and revision in the list of cards */
+		if (!skipped_version && !strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
+			skipped_version = TRUE;
+			skip = TRUE;
+		} else if (!skipped_revision && !strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME)) {
+			skipped_revision = TRUE;
+			skip = TRUE;
+		}
+
+		if (!skip) {
 			EContact *contact = create_contact (id_dbt.data, vcard_dbt.data);
 
 			contacts = g_slist_prepend (contacts, contact);
-		} else {
-			skipped_version = TRUE;
 		}
 
 		db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT);
@@ -282,6 +289,67 @@ e_book_backend_file_create_unique_id (void)
 	return g_strdup_printf (PAS_ID_PREFIX "%08lX%08X", time(NULL), c++);
 }
 
+static gchar *
+e_book_backend_new_revision (EBookBackendFile *bf)
+{
+	gchar time_string[100] = {0};
+	const struct tm *tm = NULL;
+	time_t t;
+
+	t = time (NULL);
+	tm = gmtime (&t);
+	if (tm)
+		strftime (time_string, 100, "%Y-%m-%dT%H:%M:%SZ", tm);
+
+	return g_strdup_printf ("%s(%d)", time_string, bf->priv->rev_counter++);
+}
+
+/* For now just bump the revision and set it in the DB every
+ * time the revision bumps, this is the safest approach and
+ * its unclear so far if bumping the revision string for 
+ * every DB modification is going to really be an overhead.
+ */
+static void
+e_book_backend_bump_revision (EBookBackendFile *bf)
+{
+	DB   *db = bf->priv->file_db;
+	DBT  revision_name_dbt, revision_dbt;
+	gint  db_error;
+
+	g_free (bf->priv->revision);
+	bf->priv->revision = e_book_backend_new_revision (bf);
+
+	string_to_dbt (E_BOOK_BACKEND_FILE_REVISION_NAME, &revision_name_dbt);
+	string_to_dbt (bf->priv->revision,                &revision_dbt);
+	db_error = db->put (db, NULL, &revision_name_dbt, &revision_dbt, 0);
+
+	if (db_error != 0)
+		g_warning (G_STRLOC ": db->put failed while bumping the revision string: %s",
+			   db_strerror (db_error));
+}
+
+static void
+e_book_backend_file_load_revision (EBookBackendFile *bf)
+{
+	DB   *db = bf->priv->file_db;
+	DBT  version_name_dbt, version_dbt;
+	gint  db_error;
+
+	string_to_dbt (E_BOOK_BACKEND_FILE_REVISION_NAME, &version_name_dbt);
+	memset (&version_dbt, 0, sizeof (version_dbt));
+	version_dbt.flags = DB_DBT_MALLOC;
+
+	db_error = db->get (db, NULL, &version_name_dbt, &version_dbt, 0);
+	if (db_error == 0) {
+		/* success */
+		bf->priv->revision = version_dbt.data;
+	}
+	else {
+		/* key was not in file */
+		bf->priv->revision = e_book_backend_new_revision (bf);
+	}
+}
+
 static void
 set_revision (EContact *contact)
 {
@@ -426,6 +494,8 @@ e_book_backend_file_create_contacts (EBookBackendSync *backend,
 			g_warning ("Failed to add contacts to summary: %s", error->message);
 			g_error_free (error);
 		}
+
+		e_book_backend_bump_revision (bf);
 	}
 }
 
@@ -509,6 +579,8 @@ e_book_backend_file_remove_contacts (EBookBackendSync *backend,
 		*ids = NULL;
 		e_util_free_string_slist (removed_ids);
 	}
+
+	e_book_backend_bump_revision (bf);
 }
 
 static void
@@ -639,6 +711,8 @@ e_book_backend_file_modify_contacts (EBookBackendSync *backend,
 	}
 
 	e_util_free_string_slist (ids);
+
+	e_book_backend_bump_revision (bf);
 }
 
 static void
@@ -747,9 +821,11 @@ e_book_backend_file_get_contact_list (EBookBackendSync *backend,
 
 		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)) {
+			/* don't include the version or revision 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)) &&
+			    (id_dbt.size != strlen (E_BOOK_BACKEND_FILE_REVISION_NAME) + 1
+			     || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME))) {
 
 				if ((!search_needed) || (card_sexp != NULL && e_book_backend_sexp_match_vcard  (card_sexp, vcard_dbt.data))) {
 					contact_list = g_slist_prepend (contact_list, vcard_dbt.data);
@@ -838,9 +914,11 @@ e_book_backend_file_get_contact_list_uids (EBookBackendSync *backend,
 
 		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)) {
+			/* don't include the version or revision 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)) &&
+			    (id_dbt.size != strlen (E_BOOK_BACKEND_FILE_REVISION_NAME) + 1
+			     || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_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));
@@ -1027,8 +1105,9 @@ book_view_thread (gpointer data)
 					break;
 
 				/* don't include the version in the list of cards */
-				if (strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
-					notify_update_vcard (book_view, allcontacts,
+				if (strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME) &&
+				    strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME)) {
+					notify_update_vcard (book_view, allcontacts, 
 							     id_dbt.data, vcard_dbt.data);
 				}
 
@@ -1196,8 +1275,10 @@ e_book_backend_file_upgrade_db (EBookBackendFile *bf,
 		db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST);
 
 		while (db_error == 0) {
-			if (id_dbt.size != strlen (E_BOOK_BACKEND_FILE_VERSION_NAME) + 1
-			    || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
+			if ((id_dbt.size != strlen (E_BOOK_BACKEND_FILE_VERSION_NAME) + 1
+			     || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) &&
+			    (id_dbt.size != strlen (E_BOOK_BACKEND_FILE_REVISION_NAME) + 1
+			     || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME))) {
 				EContact *contact;
 
 				contact = create_contact (id_dbt.data, vcard_dbt.data);
@@ -1552,6 +1633,8 @@ e_book_backend_file_open (EBookBackendSync *backend,
 		}
 	}
 
+	e_book_backend_file_load_revision (bf);
+
 	e_book_backend_notify_online (E_BOOK_BACKEND (backend), TRUE);
 	e_book_backend_notify_readonly (E_BOOK_BACKEND (backend), readonly);
 	e_book_backend_notify_opened (E_BOOK_BACKEND (backend), NULL /* Success */);
@@ -1681,6 +1764,14 @@ e_book_backend_file_sync (EBookBackend *backend)
 	}
 }
 
+static void
+e_book_backend_file_get_revision (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable)
+{
+	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
+
+	e_data_book_respond_get_revision (book, opid, NULL, bf->priv->revision);
+}
+
 typedef struct {
 	EContact         *contact;
 	EBookBackendFile *bf;
@@ -1775,6 +1866,7 @@ e_book_backend_file_finalize (GObject *object)
 
 	g_free (bf->priv->filename);
 	g_free (bf->priv->dirname);
+	g_free (bf->priv->revision);
 
 	g_free (bf->priv);
 
@@ -1843,6 +1935,7 @@ e_book_backend_file_class_init (EBookBackendFileClass *klass)
 	backend_class->start_book_view		= e_book_backend_file_start_book_view;
 	backend_class->stop_book_view		= e_book_backend_file_stop_book_view;
 	backend_class->sync			= e_book_backend_file_sync;
+	backend_class->get_revision		= e_book_backend_file_get_revision;
 	backend_class->notify_update            = e_book_backend_file_notify_update;
 
 	sync_class->open_sync			= e_book_backend_file_open;
diff --git a/addressbook/libebook/e-book-client.c b/addressbook/libebook/e-book-client.c
index eb72a2b..24df4c5 100644
--- a/addressbook/libebook/e-book-client.c
+++ b/addressbook/libebook/e-book-client.c
@@ -2646,6 +2646,83 @@ e_book_client_get_view_sync (EBookClient *client,
 	return complete_get_view (client, res, view_path, view, error);
 }
 
+/**
+ * e_book_client_get_revision:
+ * @client: an #EBookClient
+ * @cancellable: a #GCancellable; can be %NULL
+ * @callback: callback to call when a result is ready
+ * @user_data: user data for the @callback
+ *
+ * Query @client for an opaque revision string representing
+ * the current state of the addressbook. The call is finished 
+ * by e_book_client_get_view_finish() from the @callback.
+ *
+ *
+ * Since: 3.2
+ **/
+void
+e_book_client_get_revision (EBookClient *client, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
+{
+	e_client_proxy_call_void (E_CLIENT (client), cancellable, callback, user_data,
+				  e_book_client_get_revision,
+				  e_gdbus_book_call_get_revision,
+				  NULL, NULL, e_gdbus_book_call_get_revision_finish, NULL, NULL);
+}
+
+/**
+ * e_book_client_get_revision_finish:
+ * @client: an #EBookClient
+ * @result: a #GAsyncResult
+ * @revision: (out) (transfer full): an opaque revision string if the backend supports it, otherwise %NULL.
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Finishes previous call of e_book_client_get_revision().
+ * If the call is successful and the backend in use reports opaque
+ * revision strings, then the @revision is set to newly allocated opaque
+ * revision string which should be freed with g_free().
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.2
+ **/
+gboolean
+e_book_client_get_revision_finish (EBookClient *client, GAsyncResult *result, gchar **revision, GError **error)
+{
+	g_return_val_if_fail (revision != NULL, FALSE);
+
+	return e_client_proxy_call_finish_string (E_CLIENT (client), result, revision, error, 
+						  e_book_client_get_revision);
+}
+
+/**
+ * e_book_client_get_revision_sync:
+ * @client: an #EBookClient
+ * @revision: (out) (transfer full): a location to store an opaque revision string
+ * @cancellable: a #GCancellable; can be %NULL
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Query @client for an opaque revision string representing
+ * the current state of the addressbook.
+ *
+ * Since: 3.2
+ **/
+gboolean
+e_book_client_get_revision_sync (EBookClient *client, gchar **revision, GCancellable *cancellable, GError **error)
+{
+	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 (revision != NULL, FALSE);
+
+	if (!client->priv->gdbus_book) {
+		set_proxy_gone_error (error);
+		return FALSE;
+	}
+
+	return e_client_proxy_call_sync_void__string (E_CLIENT (client), revision, cancellable, error, 
+						      e_gdbus_book_call_get_revision_sync);
+}
+
 static GDBusProxy *
 book_client_get_dbus_proxy (EClient *client)
 {
diff --git a/addressbook/libebook/e-book-client.h b/addressbook/libebook/e-book-client.h
index 3ebac76..e61d163 100644
--- a/addressbook/libebook/e-book-client.h
+++ b/addressbook/libebook/e-book-client.h
@@ -180,6 +180,10 @@ void		e_book_client_get_view				(EBookClient *client, const gchar *sexp, GCancel
 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);
 
+void		e_book_client_get_revision			(EBookClient *client, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+gboolean	e_book_client_get_revision_finish		(EBookClient *client, GAsyncResult *result, gchar **revision, GError **error);
+gboolean	e_book_client_get_revision_sync			(EBookClient *client, gchar **revision, GCancellable *cancellable, GError **error);
+
 G_END_DECLS
 
 #endif /* E_BOOK_CLIENT_H */
diff --git a/addressbook/libedata-book/e-book-backend.c b/addressbook/libedata-book/e-book-backend.c
index 0ef4fc5..bbc43db 100644
--- a/addressbook/libedata-book/e-book-backend.c
+++ b/addressbook/libedata-book/e-book-backend.c
@@ -731,6 +731,32 @@ e_book_backend_remove_book_view (EBookBackend *backend,
 	g_mutex_unlock (backend->priv->views_mutex);
 }
 
+
+/**
+ * e_book_backend_get_revision:
+ * @backend: an #EBookBackend
+ * @book: an #EDataBook
+ * @opid: the ID to use for this operation
+ *
+ * Executes a 'get revision' request specified by @opid on @book
+ * using @backend.
+ **/
+void
+e_book_backend_get_revision (EBookBackend  *backend,
+			     EDataBook     *book,
+			     guint32        opid,
+			     GCancellable  *cancellable)
+{
+	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
+	g_return_if_fail (E_IS_DATA_BOOK (book));
+
+	if (E_BOOK_BACKEND_GET_CLASS (backend)->get_revision)
+		(* E_BOOK_BACKEND_GET_CLASS (backend)->get_revision) (backend, book, opid, cancellable);
+	else
+		/* If the backend does not implement, successfully return a NULL string */
+		e_data_book_respond_get_revision (book, opid, NULL, NULL);
+}
+
 /**
  * e_book_backend_add_client:
  * @backend: An addressbook backend.
diff --git a/addressbook/libedata-book/e-book-backend.h b/addressbook/libedata-book/e-book-backend.h
index 214aa4c..4c405f4 100644
--- a/addressbook/libedata-book/e-book-backend.h
+++ b/addressbook/libedata-book/e-book-backend.h
@@ -146,6 +146,7 @@ struct _EBookBackendClass {
 	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    (* get_revision)                (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable); 
 
 	void	(* start_book_view)		(EBookBackend *backend, EDataBookView *book_view);
 	void	(* stop_book_view)		(EBookBackend *backend, EDataBookView *book_view);
@@ -183,6 +184,7 @@ void		e_book_backend_modify_contacts	(EBookBackend *backend, EDataBook *book, gu
 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_get_revision     (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable);
 
 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 fa72231..bf00be1 100644
--- a/addressbook/libedata-book/e-data-book.c
+++ b/addressbook/libedata-book/e-data-book.c
@@ -65,6 +65,7 @@ typedef enum {
 	OP_GET_CONTACT,
 	OP_GET_CONTACTS,
 	OP_GET_CONTACTS_UIDS,
+	OP_GET_REVISION,
 	OP_AUTHENTICATE,
 	OP_ADD_CONTACTS,
 	OP_REMOVE_CONTACTS,
@@ -173,6 +174,9 @@ operation_thread (gpointer data,
 		e_book_backend_modify_contacts (backend, op->book, op->id, op->cancellable, op->d.vcards);
 		e_util_free_string_slist (op->d.vcards);
 		break;
+	case OP_GET_REVISION:
+		e_book_backend_get_revision (backend, op->book, op->id, op->cancellable);
+		break;
 	case OP_REMOVE_CONTACTS:
 		e_book_backend_remove_contacts (backend, op->book, op->id, op->cancellable, op->d.ids);
 		e_util_free_string_slist (op->d.ids);
@@ -613,6 +617,19 @@ impl_Book_get_contact_list_uids (EGdbusBook *object,
 }
 
 static gboolean
+impl_Book_get_revision (EGdbusBook *object, GDBusMethodInvocation *invocation, EDataBook *book)
+{
+	OperationData *op;
+
+	op = op_new (OP_GET_REVISION, book);
+
+	e_gdbus_book_complete_get_revision (book->priv->gdbus_object, invocation, op->id);
+	e_operation_pool_push (ops_pool, op);
+
+	return TRUE;
+}
+
+static gboolean
 impl_Book_add_contacts (EGdbusBook *object,
                        GDBusMethodInvocation *invocation,
                        const gchar * const *in_vcards,
@@ -1012,6 +1029,24 @@ e_data_book_respond_get_contact_list_uids (EDataBook *book,
 }
 
 void
+e_data_book_respond_get_revision (EDataBook *book, guint32 opid, GError *error, const gchar *revision)
+{
+	gchar *gdbus_revision = NULL;
+
+	op_complete (book, opid);
+
+	/* Translators: This is prefix to a detailed error message */
+	g_prefix_error (&error, "%s", _("Cannot get revision of addressbook: "));
+
+	e_gdbus_book_emit_get_revision_done (book->priv->gdbus_object, opid, error, e_util_ensure_gdbus_string (revision, &gdbus_revision));
+
+	if (error)
+		g_error_free (error);
+
+	g_free (gdbus_revision);
+}
+
+void
 e_data_book_respond_create_contacts (EDataBook *book,
                                      guint32 opid,
                                      GError *error,
@@ -1390,6 +1425,9 @@ e_data_book_init (EDataBook *ebook)
 		gdbus_object, "handle-get-contact-list-uids",
 		G_CALLBACK (impl_Book_get_contact_list_uids), ebook);
 	g_signal_connect (
+		gdbus_object, "handle-get-revision",
+		G_CALLBACK (impl_Book_get_revision), ebook);
+	g_signal_connect (
 		gdbus_object, "handle-authenticate-user",
 		G_CALLBACK (impl_Book_authenticate_user), ebook);
 	g_signal_connect (
diff --git a/addressbook/libedata-book/e-data-book.h b/addressbook/libedata-book/e-data-book.h
index 2bd6d65..c418ef8 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_modify_contacts		(EDataBook *book, guint32 opid, GErro
 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_respond_get_revision		(EDataBook *book, guint32 opid, GError *error, const gchar *revision);
 
 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 5dd4b37..c06084b 100644
--- a/addressbook/libegdbus/e-gdbus-book.c
+++ b/addressbook/libegdbus/e-gdbus-book.c
@@ -65,6 +65,8 @@ enum
 	__SET_BACKEND_PROPERTY_DONE_SIGNAL,
 	__GET_VIEW_METHOD,
 	__GET_VIEW_DONE_SIGNAL,
+	__GET_REVISION_METHOD,
+	__GET_REVISION_DONE_SIGNAL,
 	__AUTHENTICATE_USER_METHOD,
 	__CANCEL_OPERATION_METHOD,
 	__CANCEL_ALL_METHOD,
@@ -151,6 +153,8 @@ E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID (GDBUS_BOOK_INTERFACE_NAME,
                                                       set_backend_property)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRING (GDBUS_BOOK_INTERFACE_NAME,
                                                         get_view)
+E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRING	(GDBUS_BOOK_INTERFACE_NAME,
+							 get_revision)
 
 static void
 e_gdbus_book_default_init (EGdbusBookIface *iface)
@@ -182,6 +186,7 @@ e_gdbus_book_default_init (EGdbusBookIface *iface)
 	E_INIT_GDBUS_METHOD_ASYNC_STRING__STRING(EGdbusBookIface, "get_backend_property",	get_backend_property, __GET_BACKEND_PROPERTY_METHOD, __GET_BACKEND_PROPERTY_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_ASYNC_STRV__VOID	(EGdbusBookIface, "set_backend_property",	set_backend_property, __SET_BACKEND_PROPERTY_METHOD, __SET_BACKEND_PROPERTY_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_ASYNC_STRING__STRING(EGdbusBookIface, "get_view",			get_view, __GET_VIEW_METHOD, __GET_VIEW_DONE_SIGNAL)
+	E_INIT_GDBUS_METHOD_ASYNC_VOID__STRING  (EGdbusBookIface, "get_revision",		get_revision, __GET_REVISION_METHOD, __GET_REVISION_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_STRV		(EGdbusBookIface, "authenticate_user",		authenticate_user, __AUTHENTICATE_USER_METHOD)
 	E_INIT_GDBUS_METHOD_UINT		(EGdbusBookIface, "cancel_operation",		cancel_operation, __CANCEL_OPERATION_METHOD)
 	E_INIT_GDBUS_METHOD_VOID		(EGdbusBookIface, "cancel_all",			cancel_all, __CANCEL_ALL_METHOD)
@@ -562,6 +567,26 @@ e_gdbus_book_call_get_view_sync (GDBusProxy *proxy,
 }
 
 void
+e_gdbus_book_call_get_revision (GDBusProxy *proxy, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
+{
+	e_gdbus_proxy_call_void ("get_revision", e_gdbus_book_call_get_revision, E_GDBUS_ASYNC_OP_KEEPER (proxy), cancellable, callback, user_data);
+}
+
+gboolean
+e_gdbus_book_call_get_revision_finish (GDBusProxy *proxy, GAsyncResult *result, gchar **out_revision, GError **error)
+{
+	return e_gdbus_proxy_finish_call_string (E_GDBUS_ASYNC_OP_KEEPER (proxy), result, out_revision, error, e_gdbus_book_call_get_revision);
+}
+
+gboolean
+e_gdbus_book_call_get_revision_sync (GDBusProxy *proxy, gchar **out_revision, GCancellable *cancellable, GError **error)
+{
+  return e_gdbus_proxy_call_sync_void__string (proxy, out_revision, cancellable, error,
+		e_gdbus_book_call_get_revision,
+		e_gdbus_book_call_get_revision_finish);
+}
+
+void
 e_gdbus_book_call_authenticate_user (GDBusProxy *proxy,
                                      const gchar * const *in_credentials,
                                      GCancellable *cancellable,
@@ -709,6 +734,9 @@ DECLARE_EMIT_DONE_SIGNAL_0 (set_backend_property,
 DECLARE_EMIT_DONE_SIGNAL_1 (get_view,
                             __GET_VIEW_DONE_SIGNAL,
                             const gchar *)
+DECLARE_EMIT_DONE_SIGNAL_1 (get_revision,
+			    __GET_REVISION_DONE_SIGNAL,
+			    const gchar *)
 
 void
 e_gdbus_book_emit_backend_error (EGdbusBook *object,
@@ -776,6 +804,7 @@ E_DECLARE_GDBUS_ASYNC_METHOD_1			(book, modify_contacts, vcard, "as")
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(book, get_backend_property, prop_name, "s", prop_value, "s")
 E_DECLARE_GDBUS_ASYNC_METHOD_1			(book, set_backend_property, prop_name_value, "as")
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(book, get_view, query, "s", view, "s")
+E_DECLARE_GDBUS_ASYNC_METHOD_0_WITH_RETURN	(book, get_revision, revision, "s")
 
 E_DECLARE_GDBUS_SYNC_METHOD_1			(book, authenticate_user, credentials, "as")
 E_DECLARE_GDBUS_SYNC_METHOD_1			(book, cancel_operation, opid, "u")
@@ -798,6 +827,7 @@ static const GDBusMethodInfo * const e_gdbus_book_method_info_pointers[] =
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, get_backend_property),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, set_backend_property),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, get_view),
+	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, get_revision),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, authenticate_user),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, cancel_operation),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, cancel_all),
@@ -826,6 +856,7 @@ static const GDBusSignalInfo * const e_gdbus_book_signal_info_pointers[] =
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, get_backend_property_done),
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, set_backend_property_done),
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, get_view_done),
+	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, get_revision_done),
 	NULL
 };
 
@@ -1048,6 +1079,7 @@ e_gdbus_book_proxy_init (EGdbusBookProxy *proxy)
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRING (get_backend_property);
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID	  (set_backend_property);
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRING (get_view);
+	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRING (get_revision);
 }
 
 static void
diff --git a/addressbook/libegdbus/e-gdbus-book.h b/addressbook/libegdbus/e-gdbus-book.h
index 9a6d851..1bef56a 100644
--- a/addressbook/libegdbus/e-gdbus-book.h
+++ b/addressbook/libegdbus/e-gdbus-book.h
@@ -156,6 +156,9 @@ struct _EGdbusBookIface
 	gboolean (*handle_get_view)		(EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar *in_query);
 	void	 (*get_view_done)		(EGdbusBook *object, guint arg_opid, const GError *arg_error, gchar **out_view);
 
+	gboolean (*handle_get_revision)		(EGdbusBook *object, GDBusMethodInvocation *invocation);
+	void	 (*get_revision_done)		(EGdbusBook *object, guint arg_opid, const GError *arg_error, gchar **out_revision);
+
 	gboolean (*handle_authenticate_user)	(EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar * const *in_credentials);
 	gboolean (*handle_cancel_operation)	(EGdbusBook *object, GDBusMethodInvocation *invocation, guint in_opid);
 	gboolean (*handle_cancel_all)		(EGdbusBook *object, GDBusMethodInvocation *invocation);
@@ -216,6 +219,10 @@ void		e_gdbus_book_call_get_view (GDBusProxy *proxy, const gchar *in_query, GCan
 gboolean	e_gdbus_book_call_get_view_finish (GDBusProxy *proxy, GAsyncResult *result, gchar **out_view_path, GError **error);
 gboolean	e_gdbus_book_call_get_view_sync (GDBusProxy *proxy, const gchar *in_query, gchar **out_view_path, GCancellable *cancellable, GError **error);
 
+void		e_gdbus_book_call_get_revision (GDBusProxy *proxy, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+gboolean	e_gdbus_book_call_get_revision_finish (GDBusProxy *proxy, GAsyncResult *result, gchar **out_revision, GError **error);
+gboolean	e_gdbus_book_call_get_revision_sync (GDBusProxy *proxy, gchar **out_revision, GCancellable *cancellable, GError **error);
+
 void		e_gdbus_book_call_authenticate_user (GDBusProxy *proxy, const gchar * const *in_credentials, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 gboolean	e_gdbus_book_call_authenticate_user_finish (GDBusProxy *proxy, GAsyncResult *result, GError **error);
 gboolean	e_gdbus_book_call_authenticate_user_sync (GDBusProxy *proxy, const gchar * const *in_credentials, GCancellable *cancellable, GError **error);
@@ -245,6 +252,7 @@ gboolean	e_gdbus_book_call_close_sync (GDBusProxy *proxy, GCancellable *cancella
 #define e_gdbus_book_complete_get_backend_property		e_gdbus_complete_async_method
 #define e_gdbus_book_complete_set_backend_property		e_gdbus_complete_async_method
 #define e_gdbus_book_complete_get_view				e_gdbus_complete_async_method
+#define e_gdbus_book_complete_get_revision			e_gdbus_complete_async_method
 #define e_gdbus_book_complete_authenticate_user			e_gdbus_complete_sync_method_void
 #define e_gdbus_book_complete_cancel_operation			e_gdbus_complete_sync_method_void
 #define e_gdbus_book_complete_cancel_all			e_gdbus_complete_sync_method_void
@@ -262,6 +270,7 @@ void e_gdbus_book_emit_modify_contacts_done		(EGdbusBook *object, guint arg_opid
 void e_gdbus_book_emit_get_backend_property_done	(EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar *out_prop_value);
 void e_gdbus_book_emit_set_backend_property_done	(EGdbusBook *object, guint arg_opid, const GError *arg_error);
 void e_gdbus_book_emit_get_view_done			(EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar *out_view);
+void e_gdbus_book_emit_get_revision_done		(EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar *out_revision);
 
 /* D-Bus Signal Emission Helpers */
 void e_gdbus_book_emit_backend_error	(EGdbusBook *object, const gchar *arg_message);
