commit 039d4a351e434da9c2f1b5c56b50384996947e7b
Author: Tristan Van Berkom <tristan.van.berkom@gmail.com>
Date:   Sun Jul 17 17:57:07 2011 -0400

    Added e_book_get_revision().
    
    e_book_get_revision() returns an opaque revision string on the
    entire database backend. If the backend implements ->get_revision()
    then every revision string returned will be guarunteed to have
    changed if the database has been modified in any way since the
    last time the revision was consulted, or similarly it will be
    guarunteed to have not changed if the database has not changed
    in any way since the last call to e_book_get_revision().

diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c
index e2a6118..4928437 100644
--- a/addressbook/backends/file/e-book-backend-file.c
+++ b/addressbook/backends/file/e-book-backend-file.c
@@ -62,9 +62,12 @@
 #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 SUMMARY_FLUSH_TIMEOUT 5000
 
+
 #define EDB_ERROR(_code) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, NULL)
 #define EDB_ERROR_EX(_code, _msg) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, _msg)
 
@@ -79,6 +82,8 @@ struct _EBookBackendFilePrivate {
 	DB_ENV    *env;
 	EBookBackendSummary *summary;
 	guint     id;
+
+	gchar    *revision;
 };
 
 typedef enum {
@@ -683,6 +688,68 @@ e_book_backend_file_create_next_id (EBookBackendFile *bf)
 	return g_strdup_printf (PAS_ID_PREFIX "%04X", bf->priv->id);
 }
 
+
+static gchar *
+e_book_backend_new_revision (const gchar *current_revision)
+{
+	guint64 revision;
+
+	if (!current_revision)
+		return g_strdup ("0");
+
+	revision = g_ascii_strtoull (current_revision, NULL, 10);
+	revision++;
+
+	return g_strdup_printf ("%" G_GUINT64_FORMAT, revision);
+}
+
+/* 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;
+	gchar *tmp = bf->priv->revision;
+
+	bf->priv->revision = e_book_backend_new_revision (tmp);
+	g_free (tmp);
+
+	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 (NULL);
+	}
+}
+
 static void
 set_revision (EContact *contact)
 {
@@ -802,6 +869,8 @@ e_book_backend_file_create_contact (EBookBackendSync *backend,
 
 	if (do_create (bf, vcard, contact, perror)) {
 		e_book_backend_summary_add_contact (bf->priv->summary, *contact);
+
+		e_book_backend_bump_revision (bf);
 	}
 }
 
@@ -853,6 +922,8 @@ e_book_backend_file_remove_contacts (EBookBackendSync *backend,
 		gchar *id = l->data;
 		e_book_backend_summary_remove_contact (bf->priv->summary, id);
 	}
+
+	e_book_backend_bump_revision (bf);
 }
 
 static void
@@ -932,6 +1003,8 @@ e_book_backend_file_modify_contact (EBookBackendSync *backend,
 	g_free (vcard_with_rev);
 
 	db_error_to_gerror (db_error, perror);
+
+	e_book_backend_bump_revision (bf);
 }
 
 static void
@@ -1974,10 +2047,14 @@ e_book_backend_file_load_source (EBookBackend           *backend,
 
 	/* Resolve the photo directory here */
 	dirname = e_book_backend_file_extract_path_from_source (source, GET_PATH_PHOTO_DIR);
-	if (!only_if_exists && !create_directory (dirname, perror))
+	if (!only_if_exists && !create_directory (dirname, perror)) {
+		g_free (dirname);
 		return;
+	}
 	bf->priv->photo_dirname = dirname;
 
+	e_book_backend_file_load_revision (bf);
+
 	e_book_backend_set_is_loaded (backend, TRUE);
 	e_book_backend_set_is_writable (backend, writable);
 }
@@ -2109,6 +2186,14 @@ e_book_backend_file_sync (EBookBackend *backend)
 	}
 }
 
+static void
+e_book_backend_file_get_revision (EBookBackend *backend, EDataBook *book, guint32 opid)
+{
+	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
+
+	e_data_book_respond_get_revision (book, opid, NULL, bf->priv->revision);
+}
+
 /**
  * e_book_backend_file_new:
  */
@@ -2157,6 +2242,7 @@ e_book_backend_file_finalize (GObject *object)
 	g_free (bf->priv->dirname);
 	g_free (bf->priv->summary_filename);
 	g_free (bf->priv->photo_dirname);
+	g_free (bf->priv->revision);
 
 	g_free (bf->priv);
 
@@ -2225,6 +2311,7 @@ e_book_backend_file_class_init (EBookBackendFileClass *klass)
 	backend_class->cancel_operation			= e_book_backend_file_cancel_operation;
 	backend_class->set_mode				= e_book_backend_file_set_mode;
 	backend_class->sync				= e_book_backend_file_sync;
+	backend_class->get_revision                     = e_book_backend_file_get_revision;
 	sync_class->remove_sync				= e_book_backend_file_remove;
 	sync_class->create_contact_sync			= e_book_backend_file_create_contact;
 	sync_class->remove_contacts_sync		= e_book_backend_file_remove_contacts;
diff --git a/addressbook/libebook/e-book.c b/addressbook/libebook/e-book.c
index b8c67dd..14a56b5 100644
--- a/addressbook/libebook/e-book.c
+++ b/addressbook/libebook/e-book.c
@@ -1235,6 +1235,105 @@ e_book_authenticate_user_async (EBook              *book,
 }
 
 /**
+ * e_book_get_revision:
+ * @book: an #EBook
+ * @revision: (out) (transfer: full): Return location for the opaque revision string
+ * @error: a #GError to set on failure
+ *
+ * Gets the opaque revision string for @book, revision strings
+ * can be used to check if the entire database has changed state
+ * at all between two checkpoints without itterating through
+ * the actual contacts and checking for changes.
+ *
+ * Not every backend reports a revision string, if @revision is 
+ * set to %NULL then the caller must assume that the backend 
+ * does not provide any revision string and compare the database 
+ * changes as usual.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise
+ *
+ * Since: 2.32
+ **/
+gboolean
+e_book_get_revision (EBook       *book,
+		     gchar      **revision,
+		     GError     **error)
+{
+	GError *err = NULL;
+
+	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);
+	e_return_error_if_fail (revision && !(*revision), E_BOOK_ERROR_INVALID_ARG);
+
+	e_gdbus_book_call_get_revision_sync (book->priv->gdbus_book, revision, NULL, &err);
+
+	return unwrap_gerror (err, error);
+}
+
+static void
+get_revision_reply (GObject *gdbus_book, GAsyncResult *res, gpointer user_data)
+{
+	gchar                 *revision = NULL;
+	GError                *err = NULL, *error = NULL;
+	AsyncData             *data = user_data;
+	EBookIdAsyncCallback   excb = data->excallback;
+
+	e_gdbus_book_call_get_revision_finish (E_GDBUS_BOOK (gdbus_book), &revision, res, &error);
+
+	unwrap_gerror (error, &err);
+
+	/* Protect against garbage return values on error */
+	if (error)
+		revision = NULL;
+
+	if (excb)
+		excb (data->book, err, err ? NULL : revision, data->closure);
+
+	if (err)
+		g_error_free (err);
+
+	g_free (revision);
+	g_object_unref (data->book);
+	g_slice_free (AsyncData, data);
+}
+
+/**
+ * e_book_get_revision_async:
+ * @book: an #EBook
+ * @cb: function to call when operation finishes
+ * @closure: data to pass to callback function
+ *
+ * Retrieves the opaque revision string of @book if
+ * @book implements revision strings, otherwise it
+ * will report %NULL as the revision.
+ *
+ * Returns: %FALSE if successful, %TRUE otherwise
+ *
+ * Since: 2.32
+ **/
+gboolean
+e_book_get_revision_async (EBook                *book,
+			   EBookIdAsyncCallback  cb,
+			   gpointer              closure)
+{
+	AsyncData *data;
+
+	e_return_ex_async_error_val_if_fail (E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG);
+	e_return_ex_async_error_val_if_fail (book->priv->gdbus_book, E_BOOK_ERROR_REPOSITORY_OFFLINE);
+
+	data = g_slice_new0 (AsyncData);
+	data->book = g_object_ref (book);
+	data->excallback = cb;
+	data->closure = closure;
+
+	e_gdbus_book_call_get_revision (book->priv->gdbus_book, NULL, get_revision_reply, data);
+
+	return TRUE;
+}
+
+
+
+/**
  * e_book_get_contact:
  * @book: an #EBook
  * @id: a unique string ID specifying the contact
diff --git a/addressbook/libebook/e-book.h b/addressbook/libebook/e-book.h
index c33fb51..ab57b3d 100644
--- a/addressbook/libebook/e-book.h
+++ b/addressbook/libebook/e-book.h
@@ -158,6 +158,14 @@ gboolean e_book_remove_async               (EBook             *book,
 					    EBookAsyncCallback cb,
 					    gpointer           closure);
 
+gboolean e_book_get_revision               (EBook       *book,
+					    gchar      **revision,
+					    GError     **error);
+
+gboolean e_book_get_revision_async         (EBook                *book,
+					    EBookIdAsyncCallback  cb,
+					    gpointer              closure);
+
 gboolean e_book_get_required_fields       (EBook       *book,
 					    GList      **fields,
 					    GError     **error);
diff --git a/addressbook/libedata-book/e-book-backend.c b/addressbook/libedata-book/e-book-backend.c
index af0b9f2..7a15c3f 100644
--- a/addressbook/libedata-book/e-book-backend.c
+++ b/addressbook/libedata-book/e-book-backend.c
@@ -750,6 +750,31 @@ 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)
+{
+	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);
+	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 9cebf95..bae5d29 100644
--- a/addressbook/libedata-book/e-book-backend.h
+++ b/addressbook/libedata-book/e-book-backend.h
@@ -53,7 +53,7 @@ struct _EBookBackendClass {
 	void (*load_source) (EBookBackend *backend, ESource *source, gboolean only_if_exists, GError **error);
 	void (*remove) (EBookBackend *backend, EDataBook *book, guint32 opid);
         gchar *(*get_static_capabilities) (EBookBackend *backend);
-
+	void (*get_revision)    (EBookBackend *backend, EDataBook *book, guint32 opid); 
 	void (*create_contact)  (EBookBackend *backend, EDataBook *book, guint32 opid, const gchar *vcard);
 	void (*remove_contacts) (EBookBackend *backend, EDataBook *book, guint32 opid, GList *id_list);
 	void (*modify_contact)  (EBookBackend *backend, EDataBook *book, guint32 opid, const gchar *vcard);
@@ -96,6 +96,10 @@ gboolean    e_book_backend_add_client               (EBookBackend             *b
 						     EDataBook                *book);
 void        e_book_backend_remove_client            (EBookBackend             *backend,
 						     EDataBook                *book);
+void        e_book_backend_get_revision             (EBookBackend             *backend,
+						     EDataBook                *book,
+						     guint32                   opid);
+
 gboolean    e_book_backend_has_out_of_proc_clients (EBookBackend *backend);
 gchar       *e_book_backend_get_static_capabilities  (EBookBackend             *backend);
 
diff --git a/addressbook/libedata-book/e-data-book.c b/addressbook/libedata-book/e-data-book.c
index 1cf2a97..179dd40 100644
--- a/addressbook/libedata-book/e-data-book.c
+++ b/addressbook/libedata-book/e-data-book.c
@@ -60,6 +60,7 @@ typedef enum {
 	OP_MODIFY_CONTACT,
 	OP_REMOVE_CONTACTS,
 	OP_GET_CHANGES,
+	OP_GET_REVISION,
 } OperationID;
 
 typedef struct {
@@ -142,6 +143,9 @@ operation_thread (gpointer data, gpointer user_data)
 		e_book_backend_get_changes (backend, op->book, op->id, op->d.change_id);
 		g_free (op->d.change_id);
 		break;
+	case OP_GET_REVISION:
+		e_book_backend_get_revision (backend, op->book, op->id);
+		break;
 	}
 
 	g_object_unref (op->book);
@@ -390,6 +394,33 @@ e_data_book_respond_get_contact (EDataBook *book, guint32 opid, GError *error, c
 	}
 }
 
+
+
+static gboolean
+impl_Book_getRevision (EGdbusBook *object, GDBusMethodInvocation *invocation, EDataBook *book)
+{
+	OperationData *op;
+
+	op = op_new (OP_GET_REVISION, book, invocation);
+	g_thread_pool_push (op_pool, op, NULL);
+
+	return TRUE;
+}
+
+void
+e_data_book_respond_get_revision (EDataBook *book, guint32 opid, GError *error, const gchar *revision)
+{
+	GDBusMethodInvocation *invocation = opid_fetch (opid);
+
+	if (error) {
+		/* Translators: The '%s' is replaced with a detailed error message */
+		data_book_return_error (invocation, error, _("Cannot get book revision: %s"));
+		g_error_free (error);
+	} else {
+		e_gdbus_book_complete_get_revision (book->priv->gdbus_object, invocation, revision);
+	}
+}
+
 static gboolean
 impl_Book_getContactList (EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar *query, EDataBook *book)
 {
@@ -880,6 +911,7 @@ e_data_book_init (EDataBook *ebook)
 	gdbus_object = ebook->priv->gdbus_object;
 	g_signal_connect (gdbus_object, "handle-open", G_CALLBACK (impl_Book_open), ebook);
 	g_signal_connect (gdbus_object, "handle-remove", G_CALLBACK (impl_Book_remove), ebook);
+	g_signal_connect (gdbus_object, "handle-get-revision", G_CALLBACK (impl_Book_getRevision), 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);
diff --git a/addressbook/libedata-book/e-data-book.h b/addressbook/libedata-book/e-data-book.h
index 548bd2c..b57f81d 100644
--- a/addressbook/libedata-book/e-data-book.h
+++ b/addressbook/libedata-book/e-data-book.h
@@ -108,6 +108,11 @@ guint			e_data_book_register_gdbus_object (EDataBook *cal, GDBusConnection *conn
 EBookBackend		*e_data_book_get_backend            (EDataBook *book);
 ESource			*e_data_book_get_source             (EDataBook *book);
 
+void                    e_data_book_respond_get_revision   (EDataBook *book,
+							    guint32 opid,
+							    GError *error,
+							    const gchar *revision);
+
 void                    e_data_book_respond_open           (EDataBook *book,
 							    guint32 opid,
 							    GError *error);
diff --git a/addressbook/libegdbus/e-gdbus-egdbusbook.c b/addressbook/libegdbus/e-gdbus-egdbusbook.c
index f875879..92a92c3 100644
--- a/addressbook/libegdbus/e-gdbus-egdbusbook.c
+++ b/addressbook/libegdbus/e-gdbus-egdbusbook.c
@@ -69,6 +69,7 @@ enum
   __AUTH_REQUIRED_SIGNAL,
   __OPEN_METHOD,
   __REMOVE_METHOD,
+  __GET_REVISION_METHOD,
   __GET_CONTACT_METHOD,
   __GET_CONTACT_LIST_METHOD,
   __GET_CONTACT_UID_LIST_METHOD,
@@ -261,6 +262,7 @@ e_gdbus_book_default_init (EGdbusBookIface *iface)
   _property_name_to_gname = g_hash_table_new (g_str_hash, g_str_equal);
   g_hash_table_insert (_method_name_to_id, (gpointer) "open", GUINT_TO_POINTER (__OPEN_METHOD));
   g_hash_table_insert (_method_name_to_id, (gpointer) "remove", GUINT_TO_POINTER (__REMOVE_METHOD));
+  g_hash_table_insert (_method_name_to_id, (gpointer) "getRevision", GUINT_TO_POINTER (__GET_REVISION_METHOD));
   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));
@@ -420,6 +422,34 @@ e_gdbus_book_default_init (EGdbusBookIface *iface)
                   G_TYPE_BOOLEAN,
                   1,
                   G_TYPE_DBUS_METHOD_INVOCATION);
+
+
+  /**
+   * EGdbusBook::handle-get-revision:
+   * @object: The exported object emitting the signal.
+   * @invocation: A #GDBusMethodInvocation object that can be used to return a value or error.
+   *
+   * On exported objects, this signal is emitted when a remote process (identified by @invocation) invokes the <literal>getRevision</literal> D-Bus method on @object. Use e_gdbus_book_complete_get_revision() 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_REVISION_METHOD] =
+    g_signal_new ("handle-get-revision",
+                  G_TYPE_FROM_INTERFACE (iface),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (EGdbusBookIface, handle_get_revision),
+                  g_signal_accumulator_true_handled,
+                  NULL,
+                  _e_gdbus_gdbus_cclosure_marshaller_BOOLEAN__OBJECT,
+                  G_TYPE_BOOLEAN,
+                  1,
+                  G_TYPE_DBUS_METHOD_INVOCATION);
+
+
   /**
    * EGdbusBook::handle-get-contact:
    * @object: The exported object emitting the signal.
@@ -1020,6 +1050,116 @@ _out:
 }
 
 /**
+ * e_gdbus_book_call_get_revision:
+ * @proxy: A #EGdbusBook.
+ * @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.getRevision</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_revision_finish() to get the result of the operation.
+ *
+ * See e_gdbus_book_call_get_revision_sync() for the synchronous version of this method.
+ */
+void e_gdbus_book_call_get_revision (
+        EGdbusBook *proxy,
+        GCancellable *cancellable,
+        GAsyncReadyCallback callback,
+        gpointer user_data)
+{
+  g_dbus_proxy_call (G_DBUS_PROXY (proxy),
+                     "getRevision",
+                     NULL,
+                     G_DBUS_CALL_FLAGS_NONE,
+                     -1,
+                     cancellable,
+                     callback,
+                     user_data);
+}
+
+/**
+ * e_gdbus_book_call_get_revision_finish:
+ * @proxy: A #EGdbusBook.
+ * @out_revision: Return location for out parameter or %NULL. Free with g_free().
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to e_gdbus_book_call_get_revision().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes invoking the <literal>org.gnome.evolution.dataserver.addressbook.Book.getRevision</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_revision_finish (
+        EGdbusBook *proxy,
+        gchar **out_revision,
+        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,
+                   "(s)",
+                   out_revision);
+  }
+  g_variant_unref (_result);
+  _ret = TRUE;
+_out:
+  return _ret;
+}
+
+/**
+ * e_gdbus_book_call_get_revision_sync:
+ * @proxy: A #EGdbusBook.
+ * @out_revision: Return location for out parameter or %NULL. Free with g_free().
+ * @cancellable: A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously invokes the <literal>org.gnome.evolution.dataserver.addressbook.Book.getRevision</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_revision() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the call succeeded, %FALSE if @error is set.
+ */
+gboolean e_gdbus_book_call_get_revision_sync (
+        EGdbusBook *proxy,
+        gchar **out_revision,
+        GCancellable *cancellable,
+        GError **error)
+{
+  gboolean _ret = FALSE;
+  GVariant *_result;
+  _result = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy),
+                                   "getRevision",
+                                   NULL,
+                                   G_DBUS_CALL_FLAGS_NONE,
+                                   -1,
+                                   cancellable,
+                                   error);
+  if (_result == NULL)
+    goto _out;
+  {
+    g_variant_get (_result,
+                   "(s)",
+                   out_revision);
+  }
+  g_variant_unref (_result);
+  _ret = TRUE;
+_out:
+  return _ret;
+}
+
+/**
  * e_gdbus_book_call_get_contact:
  * @proxy: A #EGdbusBook.
  * @in_uid: Method parameter.
@@ -2782,6 +2922,31 @@ void e_gdbus_book_complete_remove (
 }
 
 /**
+ * e_gdbus_book_complete_get_revision:
+ * @object: A #EGdbusBook.
+ * @invocation: A #GDBusMethodInvocation.
+ * @out_revision: Value to return.
+ *
+ * Completes handling the <literal>org.gnome.evolution.dataserver.addressbook.Book.getRevision</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_revision (
+        EGdbusBook *object,
+        GDBusMethodInvocation *invocation,
+        const gchar *out_revision)
+{
+  GVariant *_params;
+  _params = g_variant_new ("(s)",
+                           out_revision);
+  g_dbus_method_invocation_return_value (invocation, _params);
+}
+
+/**
  * e_gdbus_book_complete_get_contact:
  * @object: A #EGdbusBook.
  * @invocation: A #GDBusMethodInvocation.
@@ -3263,6 +3428,28 @@ static const GDBusMethodInfo e_gdbus_book_method_remove =
   (GDBusAnnotationInfo **) NULL,
 };
 
+static const GDBusArgInfo e_gdbus_book_method_out_getRevision_revision =
+{
+  -1,
+  (gchar *) "revision",
+  (gchar *) "s",
+  (GDBusAnnotationInfo **) NULL,
+};
+static const GDBusArgInfo * const e_gdbus_book_method_out_getRevision_arg_pointers[] =
+{
+  &e_gdbus_book_method_out_getRevision_revision,
+  NULL
+};
+
+static const GDBusMethodInfo e_gdbus_book_method_getRevision =
+{
+  -1,
+  (gchar *) "getRevision",
+  (GDBusArgInfo **) NULL,
+  (GDBusArgInfo **) &e_gdbus_book_method_out_getRevision_arg_pointers,
+  (GDBusAnnotationInfo **) NULL,
+};
+
 static const GDBusArgInfo e_gdbus_book_method_in_getContact_uid =
 {
   -1,
@@ -3681,6 +3868,7 @@ static const GDBusMethodInfo * const e_gdbus_book_method_info_pointers[] =
 {
   &e_gdbus_book_method_open,
   &e_gdbus_book_method_remove,
+  &e_gdbus_book_method_getRevision,
   &e_gdbus_book_method_getContact,
   &e_gdbus_book_method_getContactList,
   &e_gdbus_book_method_getContactUidList,
@@ -3750,6 +3938,18 @@ handle_method_call (GDBusConnection       *connection,
       }
       break;
 
+    case __GET_REVISION_METHOD:
+      {
+        EGdbusBook *object = E_GDBUS_BOOK (user_data);
+        gboolean handled;
+        g_signal_emit (object,
+                       signals[method_id],
+                       0, invocation, &handled);
+        if (!handled)
+          goto not_implemented;
+      }
+      break;
+
     case __GET_CONTACT_METHOD:
       {
         EGdbusBook *object = E_GDBUS_BOOK (user_data);
diff --git a/addressbook/libegdbus/e-gdbus-egdbusbook.h b/addressbook/libegdbus/e-gdbus-egdbusbook.h
index 3098d21..ae056c1 100644
--- a/addressbook/libegdbus/e-gdbus-egdbusbook.h
+++ b/addressbook/libegdbus/e-gdbus-egdbusbook.h
@@ -33,6 +33,7 @@ typedef struct _EGdbusBook EGdbusBook; /* Dummy typedef */
  * @auth_required: Handler for the #EGdbusBook::auth-required signal.
  * @handle_open: Handler for the #EGdbusBook::handle-open signal.
  * @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_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.
@@ -194,6 +195,9 @@ struct _EGdbusBookIface
   gboolean (*handle_remove) (
         EGdbusBook *object,
         GDBusMethodInvocation *invocation);
+  gboolean (*handle_get_revision) (
+        EGdbusBook *object,
+        GDBusMethodInvocation *invocation);
   gboolean (*handle_get_contact) (
         EGdbusBook *object,
         GDBusMethodInvocation *invocation,
@@ -291,6 +295,24 @@ gboolean e_gdbus_book_call_remove_sync (
         GCancellable *cancellable,
         GError **error);
 
+void e_gdbus_book_call_get_revision (
+        EGdbusBook *proxy,
+        GCancellable *cancellable,
+        GAsyncReadyCallback callback,
+        gpointer user_data);
+
+gboolean e_gdbus_book_call_get_revision_finish (
+        EGdbusBook *proxy,
+        gchar **out_revision,
+        GAsyncResult *res,
+        GError **error);
+
+gboolean e_gdbus_book_call_get_revision_sync (
+        EGdbusBook *proxy,
+        gchar **out_revision,
+        GCancellable *cancellable,
+        GError **error);
+
 void e_gdbus_book_call_get_contact (
         EGdbusBook *proxy,
         const gchar *in_uid,
@@ -586,6 +608,11 @@ void e_gdbus_book_complete_remove (
         EGdbusBook *object,
         GDBusMethodInvocation *invocation);
 
+void e_gdbus_book_complete_get_revision (
+        EGdbusBook *object,
+        GDBusMethodInvocation *invocation,
+        const gchar *out_revision);
+
 void e_gdbus_book_complete_get_contact (
         EGdbusBook *object,
         GDBusMethodInvocation *invocation,
