[evolution-data-server] e-book-client: Add bulk contact modification methods.



commit fbb03706a9f1b625f65d25a59d3fe50dc277058b
Author: Christophe Dumez <christophe dumez intel com>
Date:   Wed Oct 5 16:22:31 2011 +0300

    e-book-client: Add bulk contact modification methods.
    
    This reduces the number of DBus round-trips and enables
    optimizations in the backend.
    This patch also makes use of Berkeley DB transactions in the file
    backend.

 addressbook/backends/file/e-book-backend-file.c    |  163 +++++++++++++-------
 .../backends/google/e-book-backend-google.c        |   27 +++-
 addressbook/backends/ldap/e-book-backend-ldap.c    |  125 ++++++++-------
 addressbook/backends/vcf/e-book-backend-vcf.c      |   32 +++--
 .../backends/webdav/e-book-backend-webdav.c        |   41 ++++--
 addressbook/libebook/e-book-client.c               |  123 ++++++++++++++-
 addressbook/libebook/e-book-client.h               |    4 +
 addressbook/libebook/e-book.c                      |   21 ++-
 addressbook/libedata-book/e-book-backend-sync.c    |   39 +++---
 addressbook/libedata-book/e-book-backend-sync.h    |    4 +-
 addressbook/libedata-book/e-book-backend.c         |   24 ++--
 addressbook/libedata-book/e-book-backend.h         |    4 +-
 addressbook/libedata-book/e-data-book.c            |   51 ++++---
 addressbook/libedata-book/e-data-book.h            |    2 +-
 addressbook/libegdbus/e-gdbus-book.c               |   50 +++---
 addressbook/libegdbus/e-gdbus-book.h               |   14 +-
 16 files changed, 481 insertions(+), 243 deletions(-)
---
diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c
index bcab4ce..f250249 100644
--- a/addressbook/backends/file/e-book-backend-file.c
+++ b/addressbook/backends/file/e-book-backend-file.c
@@ -511,78 +511,133 @@ e_book_backend_file_remove_contacts (EBookBackendSync *backend,
 }
 
 static void
-e_book_backend_file_modify_contact (EBookBackendSync *backend,
-                                    EDataBook *book,
-                                    GCancellable *cancellable,
-                                    const gchar *vcard,
-                                    EContact **contact,
-                                    GError **perror)
+e_book_backend_file_modify_contacts (EBookBackendSync *backend,
+                                     EDataBook *book,
+                                     GCancellable *cancellable,
+                                     const GSList *vcards,
+                                     GSList **contacts,
+                                     GError **perror)
 {
 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
-	DB             *db = bf->priv->file_db;
-	DBT            id_dbt, vcard_dbt;
-	gint            db_error;
-	const gchar    *id, *lookup_id;
-	gchar          *vcard_with_rev;
+	DB               *db = bf->priv->file_db;
+	DB_ENV           *env = bf->priv->env;
+	DB_TXN           *txn = NULL;
+	gint              db_error;
+	const GSList     *l;
+	GSList           *modified_contacts = NULL;
+	GSList           *ids = NULL;
 
 	if (!db) {
 		g_propagate_error (perror, EDB_NOT_OPENED_ERROR);
 		return;
 	}
 
-	*contact = e_contact_new_from_vcard (vcard);
-	id = e_contact_get_const (*contact, E_CONTACT_UID);
-
-	if (id == NULL) {
-		g_propagate_error (perror, EDB_ERROR_EX (OTHER_ERROR, "No UID in the contact"));
+	/* Begin transaction */
+	db_error = env->txn_begin(env, NULL, &txn, 0);
+	if (db_error != 0) {
+		g_warning (G_STRLOC ": env->txn_begin failed with %s", db_strerror (db_error));
+		db_error_to_gerror (db_error, perror);
 		return;
 	}
 
-	/* update the revision (modified time of contact) */
-	set_revision (*contact);
-	vcard_with_rev = e_vcard_to_string (E_VCARD (*contact), EVC_FORMAT_VCARD_30);
-
-	/* This is disgusting, but for a time cards were added with
-	 * ID's that are no longer used (they contained both the uri
-	 * and the id.) If we recognize it as a uri (file:///...) trim
-	 * off everything before the last '/', and use that as the
-	 * id.*/
-	if (!strncmp (id, "file:///", strlen ("file:///"))) {
-		lookup_id = strrchr (id, '/') + 1;
-	}
-	else
-		lookup_id = id;
+	for (l = vcards; l != NULL; l = l->next) {
+		gchar *id, *lookup_id;
+		gchar *vcard_with_rev;
+		DBT id_dbt, vcard_dbt;
+		EContact *contact;
 
-	string_to_dbt (lookup_id, &id_dbt);
-	string_to_dbt (vcard_with_rev, &vcard_dbt);
+		contact = e_contact_new_from_vcard (l->data);
+		id = e_contact_get (contact, E_CONTACT_UID);
 
-	db_error = db->put (db, NULL, &id_dbt, &vcard_dbt, 0);
+		if (id == NULL) {
+			g_propagate_error (perror, EDB_ERROR_EX (OTHER_ERROR, "No UID in the contact"));
+			break;
+		}
 
-	if (0 == db_error) {
-		db_error = db->sync (db, 0);
+		/* update the revision (modified time of contact) */
+		set_revision (contact);
+		vcard_with_rev = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+
+		/* This is disgusting, but for a time cards were added with
+		 * ID's that are no longer used (they contained both the uri
+		 * and the id.) If we recognize it as a uri (file:///...) trim
+		 * off everything before the last '/', and use that as the
+		 * id.*/
+		if (!strncmp (id, "file:///", strlen ("file:///"))) {
+			lookup_id = strrchr (id, '/') + 1;
+		}
+		else
+			lookup_id = id;
+
+		string_to_dbt (lookup_id, &id_dbt);
+
+		/* Make sure the record to update already exists */
+		memset (&vcard_dbt, 0, sizeof (vcard_dbt));
+		vcard_dbt.flags = DB_DBT_MALLOC;
+		db_error = db->get (db, txn, &id_dbt, &vcard_dbt, 0);
 		if (db_error != 0) {
-			g_warning (G_STRLOC ": db->sync failed with %s", db_strerror (db_error));
-		} else {
-			GError *error = NULL;
-
-			if (!e_book_backend_sqlitedb_remove_contact (bf->priv->sqlitedb,
-								     SQLITEDB_FOLDER_ID,
-								     id, &error)) {
-				g_warning ("Failed to remove contact from the summary: %s", error->message);
-				g_error_free (error);
-			} else if (!e_book_backend_sqlitedb_add_contact (bf->priv->sqlitedb,
-									 SQLITEDB_FOLDER_ID,
-									 *contact, FALSE, &error)) {
-				g_warning ("Failed to add contact to summary: %s", error->message);
-				g_error_free (error);
+			g_warning (G_STRLOC ": db->get failed with %s", db_strerror (db_error));
+			db_error_to_gerror (db_error, perror);
+			/* Abort as soon as a modification fails */
+			break;
+		}
+		g_free (vcard_dbt.data);
+
+		string_to_dbt (vcard_with_rev, &vcard_dbt);
+
+		db_error = db->put (db, txn, &id_dbt, &vcard_dbt, 0);
+		g_free (vcard_with_rev);
+
+		if (db_error != 0) {
+			g_warning (G_STRLOC ": db->put failed with %s", db_strerror (db_error));
+			db_error_to_gerror (db_error, perror);
+			/* Abort as soon as a modification fails */
+			break;
+		}
+
+		modified_contacts = g_slist_prepend (modified_contacts, contact);
+		ids = g_slist_prepend (ids, id);
+	}
+
+	if (db_error == 0) {
+		/* Commit transaction */
+		db_error = txn->commit (txn, 0);
+		if (db_error == 0) {
+			/* Flush cache information to disk */
+			if (db->sync (db, 0) != 0) {
+				g_warning ("db->sync failed with %s", db_strerror (db_error));
 			}
+		} else {
+			g_warning (G_STRLOC ": txn->commit failed with %s", db_strerror (db_error));
+			db_error_to_gerror (db_error, perror);
 		}
 	} else {
-		g_warning (G_STRLOC ": db->put failed with %s", db_strerror(db_error));
+		/* Rollback transaction */
+		txn->abort (txn);
+	}
+
+	if (db_error == 0) {
+		GError *error = NULL;
+		/* Update summary as well */
+		if (!e_book_backend_sqlitedb_remove_contacts (bf->priv->sqlitedb,
+					                      SQLITEDB_FOLDER_ID,
+							      ids, &error)) {
+			g_warning ("Failed to remove contacts from the summary: %s", error->message);
+			g_error_free (error);
+		} else if (!e_book_backend_sqlitedb_add_contacts (bf->priv->sqlitedb,
+								  SQLITEDB_FOLDER_ID,
+								  modified_contacts, FALSE, &error)) {
+			g_warning ("Failed to add contacts to summary: %s", error->message);
+			g_error_free (error);
+		}
+
+		*contacts = g_slist_reverse (modified_contacts);
+	} else {
+		*contacts = NULL;
+		e_util_free_object_slist (modified_contacts);
 	}
-	g_free (vcard_with_rev);
 
-	db_error_to_gerror (db_error, perror);
+	e_util_free_string_slist (ids);
 }
 
 static void
@@ -1569,7 +1624,7 @@ e_book_backend_file_get_backend_property (EBookBackendSync *backend,
 	g_return_val_if_fail (prop_value != NULL, FALSE);
 
 	if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
-		*prop_value = g_strdup ("local,do-initial-query,bulk-adds,bulk-removes,contact-lists");
+		*prop_value = g_strdup ("local,do-initial-query,bulk-adds,bulk-modifies,bulk-removes,contact-lists");
 	} else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
 		*prop_value = g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
 	} else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
@@ -1777,7 +1832,7 @@ e_book_backend_file_class_init (EBookBackendFileClass *klass)
 	sync_class->get_backend_property_sync	= e_book_backend_file_get_backend_property;
 	sync_class->create_contacts_sync	= e_book_backend_file_create_contacts;
 	sync_class->remove_contacts_sync	= e_book_backend_file_remove_contacts;
-	sync_class->modify_contact_sync		= e_book_backend_file_modify_contact;
+	sync_class->modify_contacts_sync	= e_book_backend_file_modify_contacts;
 	sync_class->get_contact_sync		= e_book_backend_file_get_contact;
 	sync_class->get_contact_list_sync	= e_book_backend_file_get_contact_list;
 	sync_class->get_contact_list_uids_sync	= e_book_backend_file_get_contact_list_uids;
diff --git a/addressbook/backends/google/e-book-backend-google.c b/addressbook/backends/google/e-book-backend-google.c
index c6f490d..f475a38 100644
--- a/addressbook/backends/google/e-book-backend-google.c
+++ b/addressbook/backends/google/e-book-backend-google.c
@@ -1573,16 +1573,18 @@ modify_contact_finish (ModifyContactData *data,
 	__debug__ (G_STRFUNC);
 
 	if (gdata_error == NULL) {
+		GSList modified_contacts = {NULL,};
 		/* Add the new entry to the cache */
 		e_contact = cache_add_contact (data->backend, GDATA_ENTRY (new_contact));
-		e_data_book_respond_modify (data->book, data->opid, NULL, e_contact);
+		modified_contacts.data = e_contact;
+		e_data_book_respond_modify_contacts (data->book, data->opid, NULL, &modified_contacts);
 		g_object_unref (e_contact);
 	} else {
 		GError *book_error = NULL;
 
 		/* Report the error. */
 		data_book_error_from_gdata_error (&book_error, gdata_error);
-		e_data_book_respond_modify (data->book, data->opid, book_error, NULL);
+		e_data_book_respond_modify_contacts (data->book, data->opid, book_error, NULL);
 	}
 
 	finish_operation (data->backend, data->opid, gdata_error);
@@ -1760,11 +1762,11 @@ finish:
  * operation responded to in modify_contact_photo_query_cb().
  */
 static void
-e_book_backend_google_modify_contact (EBookBackend *backend,
+e_book_backend_google_modify_contacts (EBookBackend *backend,
                                       EDataBook *book,
                                       guint32 opid,
                                       GCancellable *cancellable,
-                                      const gchar *vcard_str)
+                                      const GSList *vcards)
 {
 	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
 	EContact *contact, *cached_contact;
@@ -1774,13 +1776,24 @@ e_book_backend_google_modify_contact (EBookBackend *backend,
 	GDataEntry *entry = NULL;
 	const gchar *uid;
 	ModifyContactData *data;
+	const gchar *vcard_str = vcards->data;
 
 	__debug__ (G_STRFUNC);
 
 	__debug__ ("Updating: %s", vcard_str);
 
 	if (!e_backend_get_online (E_BACKEND (backend))) {
-		e_data_book_respond_modify (book, opid, EDB_ERROR (OFFLINE_UNAVAILABLE), NULL);
+		e_data_book_respond_modify_contacts (book, opid, EDB_ERROR (OFFLINE_UNAVAILABLE), NULL);
+		return;
+	}
+
+	/* We make the assumption that the vCard list we're passed is always exactly one element long, since we haven't specified "bulk-modifies"
+	 * in our static capability list. This is because there is no clean way to roll back changes in case of an error. */
+	if (vcards->next != NULL) {
+		e_data_book_respond_modify_contacts (book, opid,
+		                                     EDB_ERROR_EX (NOT_SUPPORTED,
+		                                     _("The backend does not support bulk modifications")),
+		                                     NULL);
 		return;
 	}
 
@@ -1797,7 +1810,7 @@ e_book_backend_google_modify_contact (EBookBackend *backend,
 		__debug__ ("Modifying contact failed: Contact with uid %s not found in cache.", uid);
 		g_object_unref (contact);
 
-		e_data_book_respond_modify (book, opid, EDB_ERROR (CONTACT_NOT_FOUND), NULL);
+		e_data_book_respond_modify_contacts (book, opid, EDB_ERROR (CONTACT_NOT_FOUND), NULL);
 		return;
 	}
 
@@ -2564,7 +2577,7 @@ e_book_backend_google_class_init (EBookBackendGoogleClass *klass)
 	backend_class->remove			= e_book_backend_google_remove;
 	backend_class->create_contacts		= e_book_backend_google_create_contacts;
 	backend_class->remove_contacts		= e_book_backend_google_remove_contacts;
-	backend_class->modify_contact		= e_book_backend_google_modify_contact;
+	backend_class->modify_contacts		= e_book_backend_google_modify_contacts;
 	backend_class->get_contact		= e_book_backend_google_get_contact;
 	backend_class->get_contact_list		= e_book_backend_google_get_contact_list;
 	backend_class->get_contact_list_uids	= e_book_backend_google_get_contact_list_uids;
diff --git a/addressbook/backends/ldap/e-book-backend-ldap.c b/addressbook/backends/ldap/e-book-backend-ldap.c
index f10a294..d65e610 100644
--- a/addressbook/backends/ldap/e-book-backend-ldap.c
+++ b/addressbook/backends/ldap/e-book-backend-ldap.c
@@ -1917,24 +1917,25 @@ modify_contact_modify_handler (LDAPOp *op,
 	EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (op->backend);
 	gchar *ldap_error_msg;
 	gint ldap_error;
+	GSList modified_contacts = {NULL,};
 
 	g_static_rec_mutex_lock (&eds_ldap_handler_lock);
 	if (!bl->priv->ldap) {
 		g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
-		e_data_book_respond_modify (op->book,
-					    op->opid,
-					    EDB_ERROR_NOT_CONNECTED (),
-					    NULL);
+		e_data_book_respond_modify_contacts (op->book,
+					             op->opid,
+					             EDB_ERROR_NOT_CONNECTED (),
+					             NULL);
 		ldap_op_finished (op);
 		return;
 	}
 	g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
 
 	if (LDAP_RES_MODIFY != ldap_msgtype (res)) {
-		e_data_book_respond_modify (op->book,
-					    op->opid,
-					    EDB_ERROR_MSG_TYPE (ldap_msgtype (res)),
-					    NULL);
+		e_data_book_respond_modify_contacts (op->book,
+					             op->opid,
+					             EDB_ERROR_MSG_TYPE (ldap_msgtype (res)),
+					             NULL);
 		ldap_op_finished (op);
 		return;
 	}
@@ -1954,10 +1955,11 @@ modify_contact_modify_handler (LDAPOp *op,
 	ldap_memfree (ldap_error_msg);
 
 	/* and lastly respond */
-	e_data_book_respond_modify (op->book,
-				    op->opid,
-				    ldap_error_to_response (ldap_error),
-				    modify_op->contact);
+	modified_contacts.data = modify_op->contact;
+	e_data_book_respond_modify_contacts (op->book,
+				             op->opid,
+				             ldap_error_to_response (ldap_error),
+				             &modified_contacts);
 	ldap_op_finished (op);
 }
 
@@ -1975,8 +1977,8 @@ modify_contact_search_handler (LDAPOp *op,
 	g_static_rec_mutex_lock (&eds_ldap_handler_lock);
 	if (!bl->priv->ldap) {
 		g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
-		e_data_book_respond_modify (op->book, op->opid,
-					    EDB_ERROR_NOT_CONNECTED (), NULL);
+		e_data_book_respond_modify_contacts (op->book, op->opid,
+					             EDB_ERROR_NOT_CONNECTED (), NULL);
 		ldap_op_finished (op);
 		return;
 	}
@@ -1995,10 +1997,10 @@ modify_contact_search_handler (LDAPOp *op,
 		g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
 
 		if (!e) {
-			e_data_book_respond_modify (op->book,
-						    op->opid,
-						    e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR, "%s: NULL returned from ldap_first_entry", G_STRFUNC),
-						    NULL);
+			e_data_book_respond_modify_contacts (op->book,
+						             op->opid,
+						             e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR, "%s: NULL returned from ldap_first_entry", G_STRFUNC),
+						             NULL);
 			ldap_op_finished (op);
 			return;
 		}
@@ -2027,10 +2029,10 @@ modify_contact_search_handler (LDAPOp *op,
 
 		if (ldap_error != LDAP_SUCCESS) {
 			/* more here i'm sure */
-			e_data_book_respond_modify (op->book,
-						    op->opid,
-						    ldap_error_to_response (ldap_error),
-						    NULL);
+			e_data_book_respond_modify_contacts (op->book,
+						             op->opid,
+						             ldap_error_to_response (ldap_error),
+						             NULL);
 			ldap_op_finished (op);
 			return;
 		}
@@ -2082,10 +2084,10 @@ modify_contact_search_handler (LDAPOp *op,
 						e_book_backend_cache_remove_contact (bl->priv->cache, modify_op->id);
 				} else {
 					g_warning ("ldap_rename returned %d\n", ldap_error);
-					e_data_book_respond_modify (op->book,
-								    op->opid,
-								    ldap_error_to_response (ldap_error),
-								    NULL);
+					e_data_book_respond_modify_contacts (op->book,
+								             op->opid,
+								             ldap_error_to_response (ldap_error),
+								             NULL);
 					ldap_op_finished (op);
 					return;
 				}
@@ -2118,10 +2120,10 @@ modify_contact_rename_handler (LDAPOp *op,
 	g_static_rec_mutex_lock (&eds_ldap_handler_lock);
 	if (!bl->priv->ldap) {
 		g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
-		e_data_book_respond_modify (op->book,
-					    op->opid,
-					    EDB_ERROR_NOT_CONNECTED (),
-					    NULL);
+		e_data_book_respond_modify_contacts (op->book,
+					             op->opid,
+					             EDB_ERROR_NOT_CONNECTED (),
+					             NULL);
 		ldap_op_finished (op);
 		return;
 	}
@@ -2130,10 +2132,10 @@ modify_contact_rename_handler (LDAPOp *op,
 	/* was a rename necessary? */
 	if (modify_op->new_id) {
 		if (LDAP_RES_RENAME != ldap_msgtype (res)) {
-			e_data_book_respond_modify (op->book,
-						    op->opid,
-						    EDB_ERROR_MSG_TYPE (ldap_msgtype (res)),
-						    NULL);
+			e_data_book_respond_modify_contacts (op->book,
+						             op->opid,
+						             EDB_ERROR_MSG_TYPE (ldap_msgtype (res)),
+						             NULL);
 			ldap_op_finished (op);
 			return;
 		}
@@ -2153,10 +2155,10 @@ modify_contact_rename_handler (LDAPOp *op,
 		ldap_memfree (ldap_error_msg);
 
 		if (ldap_error != LDAP_SUCCESS) {
-			e_data_book_respond_modify (op->book,
-						    op->opid,
-						    ldap_error_to_response (ldap_error),
-						    NULL);
+			e_data_book_respond_modify_contacts (op->book,
+						             op->opid,
+						             ldap_error_to_response (ldap_error),
+						             NULL);
 			ldap_op_finished (op);
 			return;
 		}
@@ -2228,19 +2230,19 @@ modify_contact_rename_handler (LDAPOp *op,
 					   modify_contact_msgid);
 		} else {
 			g_warning ("ldap_modify_ext returned %d\n", ldap_error);
-			e_data_book_respond_modify (op->book,
-						    op->opid,
-						    ldap_error_to_response (ldap_error),
-						    NULL);
+			e_data_book_respond_modify_contacts (op->book,
+						             op->opid,
+						             ldap_error_to_response (ldap_error),
+						             NULL);
 			ldap_op_finished (op);
 			return;
 		}
 	} else {
-		e_data_book_respond_modify (op->book,
-					    op->opid,
-					    e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
-						"%s: Unhandled result type %d returned", G_STRFUNC, ldap_msgtype (res)),
-					    NULL);
+		e_data_book_respond_modify_contacts (op->book,
+					             op->opid,
+					             e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
+						     "%s: Unhandled result type %d returned", G_STRFUNC, ldap_msgtype (res)),
+					             NULL);
 		ldap_op_finished (op);
 	}
 }
@@ -2263,27 +2265,38 @@ modify_contact_dtor (LDAPOp *op)
 }
 
 static void
-e_book_backend_ldap_modify_contact (EBookBackend *backend,
+e_book_backend_ldap_modify_contacts (EBookBackend *backend,
                                     EDataBook *book,
                                     guint32 opid,
                                     GCancellable *cancellable,
-                                    const gchar *vcard)
+                                    const GSList *vcards)
 {
 	LDAPModifyOp *modify_op = g_new0 (LDAPModifyOp, 1);
 	EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
 	gint ldap_error;
 	gint modify_contact_msgid;
 	EDataBookView *book_view;
+	gchar *vcard = vcards->data;
 
 	if (!e_backend_get_online (E_BACKEND (backend))) {
-		e_data_book_respond_modify (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
+		e_data_book_respond_modify_contacts (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
+		return;
+	}
+
+	/* We make the assumption that the vCard list we're passed is always exactly one element long, since we haven't specified "bulk-modifies"
+	 * in our static capability list. This is because there is no clean way to roll back changes in case of an error. */
+	if (vcards->next != NULL) {
+		e_data_book_respond_modify_contacts (book, opid,
+		                                     EDB_ERROR_EX (NOT_SUPPORTED,
+		                                     _("The backend does not support bulk modifications")),
+		                                     NULL);
 		return;
 	}
 
 	g_static_rec_mutex_lock (&eds_ldap_handler_lock);
 	if (!bl->priv->ldap) {
 		g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
-		e_data_book_respond_modify (book, opid, EDB_ERROR_NOT_CONNECTED (), NULL);
+		e_data_book_respond_modify_contacts (book, opid, EDB_ERROR_NOT_CONNECTED (), NULL);
 		g_free (modify_op);
 		return;
 	}
@@ -2315,10 +2328,10 @@ e_book_backend_ldap_modify_contact (EBookBackend *backend,
 			     book_view, opid, modify_contact_msgid,
 			     modify_contact_search_handler, modify_contact_dtor);
 	} else {
-		e_data_book_respond_modify (book,
-					    opid,
-					    ldap_error_to_response (ldap_error),
-					    NULL);
+		e_data_book_respond_modify_contacts (book,
+					             opid,
+					             ldap_error_to_response (ldap_error),
+					             NULL);
 		modify_contact_dtor ((LDAPOp *) modify_op);
 	}
 }
@@ -5684,7 +5697,7 @@ e_book_backend_ldap_class_init (EBookBackendLDAPClass *klass)
 
 	parent_class->create_contacts		= e_book_backend_ldap_create_contacts;
 	parent_class->remove_contacts		= e_book_backend_ldap_remove_contacts;
-	parent_class->modify_contact		= e_book_backend_ldap_modify_contact;
+	parent_class->modify_contacts		= e_book_backend_ldap_modify_contacts;
 	parent_class->get_contact		= e_book_backend_ldap_get_contact;
 	parent_class->get_contact_list		= e_book_backend_ldap_get_contact_list;
 	parent_class->get_contact_list_uids	= e_book_backend_ldap_get_contact_list_uids;
diff --git a/addressbook/backends/vcf/e-book-backend-vcf.c b/addressbook/backends/vcf/e-book-backend-vcf.c
index 9318409..06969a6 100644
--- a/addressbook/backends/vcf/e-book-backend-vcf.c
+++ b/addressbook/backends/vcf/e-book-backend-vcf.c
@@ -351,20 +351,30 @@ e_book_backend_vcf_remove_contacts (EBookBackendSync *backend,
 }
 
 static void
-e_book_backend_vcf_modify_contact (EBookBackendSync *backend,
-                                   EDataBook *book,
-                                   GCancellable *cancellable,
-                                   const gchar *vcard,
-                                   EContact **contact,
-                                   GError **perror)
+e_book_backend_vcf_modify_contacts (EBookBackendSync *backend,
+                                    EDataBook *book,
+                                    GCancellable *cancellable,
+                                    const GSList *vcards,
+                                    GSList **modified_contacts,
+                                    GError **perror)
 {
 	EBookBackendVCF *bvcf = E_BOOK_BACKEND_VCF (backend);
 	GList *elem;
 	const gchar *id;
+	EContact *contact;
+
+	/* We make the assumption that the vCard list we're passed is always exactly one element long, since we haven't specified "bulk-modifies"
+	 * in our static capability list. */
+	if (vcards->next != NULL) {
+		g_propagate_error (perror,
+		                   EDB_ERROR_EX (NOT_SUPPORTED,
+		                   _("The backend does not support bulk modifications")));
+		return;
+	}
 
 	/* create a new ecard from the request data */
-	*contact = e_contact_new_from_vcard (vcard);
-	id = e_contact_get_const (*contact, E_CONTACT_UID);
+	contact = e_contact_new_from_vcard (vcards->data);
+	id = e_contact_get_const (contact, E_CONTACT_UID);
 
 	g_mutex_lock (bvcf->priv->mutex);
 	elem = g_hash_table_lookup (bvcf->priv->contacts, id);
@@ -375,12 +385,14 @@ e_book_backend_vcf_modify_contact (EBookBackendSync *backend,
 	}
 
 	g_free (elem->data);
-	elem->data = g_strdup (vcard);
+	elem->data = g_strdup (vcards->data);
 	bvcf->priv->dirty = TRUE;
 	if (!bvcf->priv->flush_timeout_tag)
 		bvcf->priv->flush_timeout_tag = g_timeout_add (FILE_FLUSH_TIMEOUT,
 							       vcf_flush_file, bvcf);
 	g_mutex_unlock (bvcf->priv->mutex);
+
+	*modified_contacts = g_slist_append (*modified_contacts, contact);
 }
 
 static void
@@ -759,7 +771,7 @@ e_book_backend_vcf_class_init (EBookBackendVCFClass *klass)
 	sync_class->get_backend_property_sync	= e_book_backend_vcf_get_backend_property;
 	sync_class->create_contacts_sync	= e_book_backend_vcf_create_contacts;
 	sync_class->remove_contacts_sync	= e_book_backend_vcf_remove_contacts;
-	sync_class->modify_contact_sync		= e_book_backend_vcf_modify_contact;
+	sync_class->modify_contacts_sync	= e_book_backend_vcf_modify_contacts;
 	sync_class->get_contact_sync		= e_book_backend_vcf_get_contact;
 	sync_class->get_contact_list_sync	= e_book_backend_vcf_get_contact_list;
 	sync_class->authenticate_user_sync	= e_book_backend_vcf_authenticate_user;
diff --git a/addressbook/backends/webdav/e-book-backend-webdav.c b/addressbook/backends/webdav/e-book-backend-webdav.c
index d595674..5f7ab57 100644
--- a/addressbook/backends/webdav/e-book-backend-webdav.c
+++ b/addressbook/backends/webdav/e-book-backend-webdav.c
@@ -455,50 +455,62 @@ e_book_backend_webdav_remove_contacts (EBookBackend *backend,
 }
 
 static void
-e_book_backend_webdav_modify_contact (EBookBackend *backend,
-                                      EDataBook *book,
-                                      guint32 opid,
-                                      GCancellable *cancellable,
-                                      const gchar *vcard)
+e_book_backend_webdav_modify_contacts (EBookBackend *backend,
+                                       EDataBook *book,
+                                       guint32 opid,
+                                       GCancellable *cancellable,
+                                       const GSList *vcards)
 {
 	EBookBackendWebdav        *webdav  = E_BOOK_BACKEND_WEBDAV (backend);
 	EBookBackendWebdavPrivate *priv    = webdav->priv;
-	EContact                  *contact = e_contact_new_from_vcard (vcard);
+	EContact                  *contact;
+	GSList                     modified_contacts = {NULL,};
 	const gchar                *uid;
 	const gchar                *etag;
 	guint status;
 	gchar *status_reason = NULL;
+	const gchar *vcard = vcards->data;
 
 	if (!e_backend_get_online (E_BACKEND (backend))) {
 		e_data_book_respond_create_contacts (book, opid,
 				EDB_ERROR (REPOSITORY_OFFLINE), NULL);
-		g_object_unref (contact);
+		return;
+	}
+
+	/* We make the assumption that the vCard list we're passed is always exactly one element long, since we haven't specified "bulk-modifies"
+	 * in our static capability list. This is because there is no clean way to roll back changes in case of an error. */
+	if (vcards->next != NULL) {
+		e_data_book_respond_modify_contacts (book, opid,
+		                                     EDB_ERROR_EX (NOT_SUPPORTED,
+		                                     _("The backend does not support bulk modifications")),
+		                                     NULL);
 		return;
 	}
 
 	/* modify contact */
+	contact = e_contact_new_from_vcard (vcard);
 	status = upload_contact (webdav, contact, &status_reason);
 	if (status != 201 && status != 204) {
 		g_object_unref (contact);
 		if (status == 401 || status == 407) {
-			e_data_book_respond_remove_contacts (book, opid, webdav_handle_auth_request (webdav), NULL);
+			e_data_book_respond_modify_contacts (book, opid, webdav_handle_auth_request (webdav), NULL);
 			g_free (status_reason);
 			return;
 		}
 		/* data changed on server while we were editing */
 		if (status == 412) {
 			/* too bad no special error code in evolution for this... */
-			e_data_book_respond_modify (book, opid,
+			e_data_book_respond_modify_contacts (book, opid,
 					e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
-						"Contact on server changed -> not modifying"),
+					"Contact on server changed -> not modifying"),
 					NULL);
 			g_free (status_reason);
 			return;
 		}
 
-		e_data_book_respond_modify (book, opid,
+		e_data_book_respond_modify_contacts (book, opid,
 				e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
-					"Modify contact failed with HTTP status: %d (%s)", status, status_reason),
+				"Modify contact failed with HTTP status: %d (%s)", status, status_reason),
 				NULL);
 		g_free (status_reason);
 		return;
@@ -523,7 +535,8 @@ e_book_backend_webdav_modify_contact (EBookBackend *backend,
 	}
 	e_book_backend_cache_add_contact (priv->cache, contact);
 
-	e_data_book_respond_modify (book, opid, EDB_ERROR (SUCCESS), contact);
+	modified_contacts.data = contact;
+	e_data_book_respond_modify_contacts (book, opid, EDB_ERROR (SUCCESS), &modified_contacts);
 
 	g_object_unref (contact);
 }
@@ -1467,7 +1480,7 @@ e_book_backend_webdav_class_init (EBookBackendWebdavClass *klass)
 
 	backend_class->create_contacts		= e_book_backend_webdav_create_contacts;
 	backend_class->remove_contacts		= e_book_backend_webdav_remove_contacts;
-	backend_class->modify_contact		= e_book_backend_webdav_modify_contact;
+	backend_class->modify_contacts		= e_book_backend_webdav_modify_contacts;
 	backend_class->get_contact		= e_book_backend_webdav_get_contact;
 	backend_class->get_contact_list		= e_book_backend_webdav_get_contact_list;
 	backend_class->get_contact_list_uids	= e_book_backend_webdav_get_contact_list_uids;
diff --git a/addressbook/libebook/e-book-client.c b/addressbook/libebook/e-book-client.c
index 9d6acfd..d882b02 100644
--- a/addressbook/libebook/e-book-client.c
+++ b/addressbook/libebook/e-book-client.c
@@ -1559,15 +1559,18 @@ e_book_client_modify_contact (EBookClient *client,
                               gpointer user_data)
 {
 	gchar *vcard, *gdbus_vcard = NULL;
+	const gchar *strv[2];
 
 	g_return_if_fail (contact != NULL);
 	g_return_if_fail (E_IS_CONTACT (contact));
 
 	vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+	strv[0] = e_util_ensure_gdbus_string (vcard, &gdbus_vcard);
+	strv[1] = NULL;
 
-	e_client_proxy_call_string (E_CLIENT (client), e_util_ensure_gdbus_string (vcard, &gdbus_vcard), cancellable, callback, user_data, e_book_client_modify_contact,
-			e_gdbus_book_call_modify_contact,
-			e_gdbus_book_call_modify_contact_finish, NULL, NULL, NULL, NULL);
+	e_client_proxy_call_strv (E_CLIENT (client), strv, cancellable, callback, user_data, e_book_client_modify_contact,
+			e_gdbus_book_call_modify_contacts,
+			e_gdbus_book_call_modify_contacts_finish, NULL, NULL, NULL, NULL);
 
 	g_free (vcard);
 	g_free (gdbus_vcard);
@@ -1614,6 +1617,7 @@ e_book_client_modify_contact_sync (EBookClient *client,
 {
 	gboolean res;
 	gchar *vcard, *gdbus_vcard = NULL;
+	const gchar *strv[2];
 
 	g_return_val_if_fail (client != NULL, FALSE);
 	g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
@@ -1625,8 +1629,10 @@ e_book_client_modify_contact_sync (EBookClient *client,
 	}
 
 	vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+	strv[0] = e_util_ensure_gdbus_string (vcard, &gdbus_vcard);
+	strv[1] = NULL;
 
-	res = e_client_proxy_call_sync_string__void (E_CLIENT (client), e_util_ensure_gdbus_string (vcard, &gdbus_vcard), cancellable, error, e_gdbus_book_call_modify_contact_sync);
+	res = e_client_proxy_call_sync_strv__void (E_CLIENT (client), strv, cancellable, error, e_gdbus_book_call_modify_contacts_sync);
 
 	g_free (vcard);
 	g_free (gdbus_vcard);
@@ -1635,6 +1641,115 @@ e_book_client_modify_contact_sync (EBookClient *client,
 }
 
 /**
+ * e_book_client_modify_contacts:
+ * @client: an #EBookClient
+ * @contacts: a #GSList of #EContact objects
+ * @cancellable: a #GCancellable; can be %NULL
+ * @callback: callback to call when a result is ready
+ * @user_data: user data for the @callback
+ *
+ * Applies the changes made to @contacts to the stored versions in @client.
+ * The call is finished by e_book_client_modify_contacts_finish()
+ * from the @callback.
+ *
+ * Since: 3.4
+ **/
+void
+e_book_client_modify_contacts (EBookClient *client,
+                               /* const */ GSList *contacts,
+                               GCancellable *cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
+{
+	gchar **array;
+	const GSList *l;
+	gint i = 0;
+
+	g_return_if_fail (contacts != NULL);
+
+	array = g_new0 (gchar *, g_slist_length (contacts) + 1);
+	for (l = contacts; l != NULL; l = l->next) {
+		gchar *vcard = e_vcard_to_string (E_VCARD (l->data), EVC_FORMAT_VCARD_30);
+		array[i++] = e_util_utf8_make_valid (vcard);
+		g_free (vcard);
+	}
+
+	e_client_proxy_call_strv (E_CLIENT (client), (const gchar * const *) array, cancellable, callback, user_data, e_book_client_modify_contacts,
+			e_gdbus_book_call_modify_contacts,
+			e_gdbus_book_call_modify_contacts_finish, NULL, NULL, NULL, NULL);
+
+	g_strfreev (array);
+}
+
+/**
+ * e_book_client_modify_contacts_finish:
+ * @client: an #EBookClient
+ * @result: a #GAsyncResult
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Finishes previous call of e_book_client_modify_contacts().
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.4
+ **/
+gboolean
+e_book_client_modify_contacts_finish (EBookClient *client,
+                                      GAsyncResult *result,
+                                      GError **error)
+{
+	return e_client_proxy_call_finish_void (E_CLIENT (client), result, error, e_book_client_modify_contacts);
+}
+
+/**
+ * e_book_client_modify_contacts_sync:
+ * @client: an #EBookClient
+ * @contacts: a #GSList of #EContact objects
+ * @cancellable: a #GCancellable; can be %NULL
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Applies the changes made to @contacts to the stored versions in @client.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.4
+ **/
+gboolean
+e_book_client_modify_contacts_sync (EBookClient *client,
+                                    /* const */ GSList *contacts,
+                                    GCancellable *cancellable,
+                                    GError **error)
+{
+	gboolean res;
+	gint i = 0;
+	gchar **array;
+	const GSList *l;
+
+	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 (contacts != NULL, FALSE);
+
+	if (!client->priv->gdbus_book) {
+		set_proxy_gone_error (error);
+		return FALSE;
+	}
+
+	array = g_new0 (gchar *, g_slist_length (contacts) + 1);
+	for (l = contacts; l != NULL; l = l->next) {
+		gchar *vcard = e_vcard_to_string (E_VCARD (l->data), EVC_FORMAT_VCARD_30);
+		array[i++] = e_util_utf8_make_valid (vcard);
+		g_free (vcard);
+	}
+
+	res = e_client_proxy_call_sync_strv__void (E_CLIENT (client), (const gchar * const*) array, cancellable, error, e_gdbus_book_call_modify_contacts_sync);
+
+	g_strfreev (array);
+
+	return res;
+}
+
+/**
  * e_book_client_remove_contact:
  * @client: an #EBookClient
  * @contact: an #EContact
diff --git a/addressbook/libebook/e-book-client.h b/addressbook/libebook/e-book-client.h
index 5482bde..3ebac76 100644
--- a/addressbook/libebook/e-book-client.h
+++ b/addressbook/libebook/e-book-client.h
@@ -148,6 +148,10 @@ void		e_book_client_modify_contact			(EBookClient *client, /* const */ EContact
 gboolean	e_book_client_modify_contact_finish		(EBookClient *client, GAsyncResult *result, GError **error);
 gboolean	e_book_client_modify_contact_sync		(EBookClient *client, /* const */ EContact *contact, GCancellable *cancellable, GError **error);
 
+void		e_book_client_modify_contacts			(EBookClient *client, /* const */ GSList *contacts, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+gboolean	e_book_client_modify_contacts_finish		(EBookClient *client, GAsyncResult *result, GError **error);
+gboolean	e_book_client_modify_contacts_sync		(EBookClient *client, /* const */ GSList *contacts, GCancellable *cancellable, GError **error);
+
 void		e_book_client_remove_contact			(EBookClient *client, /* const */ EContact *contact, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 gboolean	e_book_client_remove_contact_finish		(EBookClient *client, GAsyncResult *result, GError **error);
 gboolean	e_book_client_remove_contact_sync		(EBookClient *client, /* const */ EContact *contact, GCancellable *cancellable, GError **error);
diff --git a/addressbook/libebook/e-book.c b/addressbook/libebook/e-book.c
index 6598177..fe15e58 100644
--- a/addressbook/libebook/e-book.c
+++ b/addressbook/libebook/e-book.c
@@ -599,6 +599,7 @@ e_book_commit_contact (EBook *book,
 {
 	GError *err = NULL;
 	gchar *vcard, *gdbus_vcard = NULL;
+	const gchar *strv[2];
 
 	g_return_val_if_fail (E_IS_BOOK (book), FALSE);
 	g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
@@ -607,7 +608,10 @@ e_book_commit_contact (EBook *book,
 		book->priv->gdbus_book, E_BOOK_ERROR_REPOSITORY_OFFLINE);
 
 	vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
-	e_gdbus_book_call_modify_contact_sync (book->priv->gdbus_book, e_util_ensure_gdbus_string (vcard, &gdbus_vcard), NULL, &err);
+	strv[0] = e_util_ensure_gdbus_string (vcard, &gdbus_vcard);
+	strv[1] = NULL;
+
+	e_gdbus_book_call_modify_contacts_sync (book->priv->gdbus_book, strv, NULL, &err);
 	g_free (vcard);
 	g_free (gdbus_vcard);
 
@@ -615,7 +619,7 @@ e_book_commit_contact (EBook *book,
 }
 
 static void
-modify_contact_reply (GObject *gdbus_book,
+modify_contacts_reply (GObject *gdbus_book,
                       GAsyncResult *res,
                       gpointer user_data)
 {
@@ -624,7 +628,7 @@ modify_contact_reply (GObject *gdbus_book,
 	EBookAsyncCallback excb = data->excallback;
 	EBookCallback cb = data->callback;
 
-	e_gdbus_book_call_modify_contact_finish (G_DBUS_PROXY (gdbus_book), res, &error);
+	e_gdbus_book_call_modify_contacts_finish (G_DBUS_PROXY (gdbus_book), res, &error);
 
 	unwrap_gerror (error, &err);
 
@@ -663,6 +667,7 @@ e_book_async_commit_contact (EBook *book,
 {
 	gchar *vcard, *gdbus_vcard = NULL;
 	AsyncData *data;
+	const gchar *strv[2];
 
 	g_return_val_if_fail (E_IS_BOOK (book), FALSE);
 	g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
@@ -677,7 +682,9 @@ e_book_async_commit_contact (EBook *book,
 	data->callback = cb;
 	data->closure = closure;
 
-	e_gdbus_book_call_modify_contact (book->priv->gdbus_book, e_util_ensure_gdbus_string (vcard, &gdbus_vcard), NULL, modify_contact_reply, data);
+	strv[0] = e_util_ensure_gdbus_string (vcard, &gdbus_vcard);
+	strv[1] = NULL;
+	e_gdbus_book_call_modify_contacts (book->priv->gdbus_book, strv, NULL, modify_contacts_reply, data);
 
 	g_free (vcard);
 	g_free (gdbus_vcard);
@@ -709,6 +716,7 @@ e_book_commit_contact_async (EBook *book,
 {
 	gchar *vcard, *gdbus_vcard = NULL;
 	AsyncData *data;
+	const gchar *strv[2];
 
 	g_return_val_if_fail (E_IS_BOOK (book), FALSE);
 	g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
@@ -723,7 +731,10 @@ e_book_commit_contact_async (EBook *book,
 	data->excallback = cb;
 	data->closure = closure;
 
-	e_gdbus_book_call_modify_contact (book->priv->gdbus_book, e_util_ensure_gdbus_string (vcard, &gdbus_vcard), NULL, modify_contact_reply, data);
+	strv[0] = e_util_ensure_gdbus_string (vcard, &gdbus_vcard);
+	strv[1] = NULL;
+
+	e_gdbus_book_call_modify_contacts (book->priv->gdbus_book, strv, NULL, modify_contacts_reply, data);
 
 	g_free (vcard);
 	g_free (gdbus_vcard);
diff --git a/addressbook/libedata-book/e-book-backend-sync.c b/addressbook/libedata-book/e-book-backend-sync.c
index 7ee5635..579b123 100644
--- a/addressbook/libedata-book/e-book-backend-sync.c
+++ b/addressbook/libedata-book/e-book-backend-sync.c
@@ -235,32 +235,32 @@ e_book_backend_sync_remove_contacts (EBookBackendSync *backend,
 }
 
 /**
- * e_book_backend_sync_modify_contact:
+ * e_book_backend_sync_modify_contacts:
  * @backend: an #EBookBackendSync
  * @book: an #EDataBook
  * @cancellable: a #GCancellable for the operation
- * @vcard: the string representation of a contact
- * @contact: a pointer to a location to store the resulting #EContact
+ * @vcards: the string representations of contacts
+ * @contacts: a pointer to a location to store the resulting #EContact objects
  * @error: #GError to set, when something fails
  *
- * Modifies the contact specified by the ID embedded in @vcard, to
- * reflect the full contents of @vcard.
+ * Modifies the contacts specified by the IDs embedded in @vcards, to
+ * reflect the full contents of @vcards.
  **/
 void
-e_book_backend_sync_modify_contact (EBookBackendSync *backend,
+e_book_backend_sync_modify_contacts (EBookBackendSync *backend,
                                     EDataBook *book,
                                     GCancellable *cancellable,
-                                    const gchar *vcard,
-                                    EContact **contact,
+                                    const GSList *vcards,
+                                    GSList **contacts,
                                     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 (vcard, E_DATA_BOOK_STATUS_INVALID_ARG);
-	e_return_data_book_error_if_fail (contact, E_DATA_BOOK_STATUS_INVALID_ARG);
-	e_return_data_book_error_if_fail (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->modify_contact_sync, E_DATA_BOOK_STATUS_NOT_SUPPORTED);
+	e_return_data_book_error_if_fail (vcards, E_DATA_BOOK_STATUS_INVALID_ARG);
+	e_return_data_book_error_if_fail (contacts, E_DATA_BOOK_STATUS_INVALID_ARG);
+	e_return_data_book_error_if_fail (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->modify_contacts_sync, E_DATA_BOOK_STATUS_NOT_SUPPORTED);
 
-	(* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->modify_contact_sync) (backend, book, cancellable, vcard, contact, error);
+	(* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->modify_contacts_sync) (backend, book, cancellable, vcards, contacts, error);
 }
 
 /**
@@ -519,21 +519,20 @@ book_backend_remove_contacts (EBookBackend *backend,
 }
 
 static void
-book_backend_modify_contact (EBookBackend *backend,
+book_backend_modify_contacts (EBookBackend *backend,
                              EDataBook *book,
                              guint32 opid,
                              GCancellable *cancellable,
-                             const gchar *vcard)
+                             const GSList *vcards)
 {
 	GError *error = NULL;
-	EContact *contact = NULL;
+	GSList *modified_contacts = NULL;
 
-	e_book_backend_sync_modify_contact (E_BOOK_BACKEND_SYNC (backend), book, cancellable, vcard, &contact, &error);
+	e_book_backend_sync_modify_contacts (E_BOOK_BACKEND_SYNC (backend), book, cancellable, vcards, &modified_contacts, &error);
 
-	e_data_book_respond_modify (book, opid, error, contact);
+	e_data_book_respond_modify_contacts (book, opid, error, modified_contacts);
 
-	if (contact)
-		g_object_unref (contact);
+	e_util_free_object_slist (modified_contacts);
 }
 
 static void
@@ -670,7 +669,7 @@ e_book_backend_sync_class_init (EBookBackendSyncClass *klass)
 	backend_class->set_backend_property	= book_backend_set_backend_property;
 	backend_class->create_contacts		= book_backend_create_contacts;
 	backend_class->remove_contacts		= book_backend_remove_contacts;
-	backend_class->modify_contact		= book_backend_modify_contact;
+	backend_class->modify_contacts		= book_backend_modify_contacts;
 	backend_class->get_contact		= book_backend_get_contact;
 	backend_class->get_contact_list		= book_backend_get_contact_list;
 	backend_class->get_contact_list_uids	= book_backend_get_contact_list_uids;
diff --git a/addressbook/libedata-book/e-book-backend-sync.h b/addressbook/libedata-book/e-book-backend-sync.h
index 621cdb3..de27624 100644
--- a/addressbook/libedata-book/e-book-backend-sync.h
+++ b/addressbook/libedata-book/e-book-backend-sync.h
@@ -35,7 +35,7 @@ struct _EBookBackendSyncClass {
 	gboolean (*set_backend_property_sync)	(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *prop_name, const gchar *prop_value, GError **error);
 	void (*create_contacts_sync)		(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const GSList *vcards, GSList **added_contacts, GError **error);
 	void (*remove_contacts_sync)		(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const GSList *id_list, GSList **removed_ids, GError **error);
-	void (*modify_contact_sync)		(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *vcard, EContact **contact, GError **error);
+	void (*modify_contacts_sync)		(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const GSList *vcards, GSList **modified_contacts, GError **error);
 	void (*get_contact_sync)		(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *id, gchar **vcard, GError **error);
 	void (*get_contact_list_sync)		(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *query, GSList **contacts, GError **error);
 	void (*get_contact_list_uids_sync)	(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *query, GSList **contacts_uids, GError **error);
@@ -54,7 +54,7 @@ gboolean	e_book_backend_sync_get_backend_property (EBookBackendSync *backend, ED
 gboolean	e_book_backend_sync_set_backend_property (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *prop_name, const gchar *prop_value, GError **error);
 void		e_book_backend_sync_create_contacts	(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const GSList *vcards, GSList **added_contacts, GError **error);
 void		e_book_backend_sync_remove_contacts	(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const GSList *id_list, GSList **removed_ids, GError **error);
-void		e_book_backend_sync_modify_contact	(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *vcard, EContact **contact, GError **error);
+void		e_book_backend_sync_modify_contacts	(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const GSList *vcards, GSList **modified_contacts, GError **error);
 void		e_book_backend_sync_get_contact		(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *id, gchar **vcard, GError **error);
 void		e_book_backend_sync_get_contact_list	(EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *query, GSList **contacts, GError **error);
 void		e_book_backend_sync_get_contact_list_uids (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *query, GSList **contacts_uids, GError **error);
diff --git a/addressbook/libedata-book/e-book-backend.c b/addressbook/libedata-book/e-book-backend.c
index 5b69194..0ef4fc5 100644
--- a/addressbook/libedata-book/e-book-backend.c
+++ b/addressbook/libedata-book/e-book-backend.c
@@ -493,35 +493,35 @@ e_book_backend_remove_contacts (EBookBackend *backend,
 }
 
 /**
- * e_book_backend_modify_contact:
+ * e_book_backend_modify_contacts:
  * @backend: an #EBookBackend
  * @book: an #EDataBook
  * @opid: the ID to use for this operation
  * @cancellable: a #GCancellable for the operation
- * @vcard: the VCard to update
+ * @vcards: the VCards to update
  *
- * Executes a 'modify contact' request specified by @opid on @book
+ * Executes a 'modify contacts' request specified by @opid on @book
  * using @backend.
- * This might be finished with e_data_book_respond_modify().
+ * This might be finished with e_data_book_respond_modify_contacts().
  **/
 void
-e_book_backend_modify_contact (EBookBackend *backend,
+e_book_backend_modify_contacts (EBookBackend *backend,
                                EDataBook *book,
                                guint32 opid,
                                GCancellable *cancellable,
-                               const gchar *vcard)
+                               const GSList *vcards)
 {
 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
 	g_return_if_fail (E_IS_DATA_BOOK (book));
-	g_return_if_fail (vcard);
-	g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->modify_contact);
+	g_return_if_fail (vcards);
+	g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->modify_contacts);
 
 	if (e_book_backend_is_opening (backend))
-		e_data_book_respond_modify (book, opid, EDB_OPENING_ERROR, NULL);
+		e_data_book_respond_modify_contacts (book, opid, EDB_OPENING_ERROR, NULL);
 	else if (!e_book_backend_is_opened (backend))
-		e_data_book_respond_modify (book, opid, EDB_NOT_OPENED_ERROR, NULL);
+		e_data_book_respond_modify_contacts (book, opid, EDB_NOT_OPENED_ERROR, NULL);
 	else
-		(* E_BOOK_BACKEND_GET_CLASS (backend)->modify_contact) (backend, book, opid, cancellable, vcard);
+		(* E_BOOK_BACKEND_GET_CLASS (backend)->modify_contacts) (backend, book, opid, cancellable, vcards);
 }
 
 /**
@@ -1017,7 +1017,7 @@ e_book_backend_sync (EBookBackend *backend)
  * Notifies all of @backend's book views about the new or modified
  * contacts @contact.
  *
- * e_data_book_respond_create() and e_data_book_respond_modify() call this
+ * e_data_book_respond_create_contacts() and e_data_book_respond_modify_contacts() call this
  * function for you. You only need to call this from your backend if
  * contacts are created or modified by another (non-PAS-using) client.
  **/
diff --git a/addressbook/libedata-book/e-book-backend.h b/addressbook/libedata-book/e-book-backend.h
index 7e047d9..214aa4c 100644
--- a/addressbook/libedata-book/e-book-backend.h
+++ b/addressbook/libedata-book/e-book-backend.h
@@ -142,7 +142,7 @@ struct _EBookBackendClass {
 	void	(* refresh)			(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable);
 	void	(* create_contacts)		(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const GSList *vcards);
 	void	(* remove_contacts)		(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const GSList *id_list);
-	void	(* modify_contact)		(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *vcard);
+	void	(* modify_contacts)		(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const GSList *vcards);
 	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);
@@ -179,7 +179,7 @@ void		e_book_backend_remove		(EBookBackend *backend, EDataBook *book, guint32 op
 void		e_book_backend_refresh		(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable);
 void		e_book_backend_create_contacts	(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const GSList *vcards);
 void		e_book_backend_remove_contacts	(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const GSList *id_list);
-void		e_book_backend_modify_contact	(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *vcard);
+void		e_book_backend_modify_contacts	(EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const GSList *vcards);
 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);
diff --git a/addressbook/libedata-book/e-data-book.c b/addressbook/libedata-book/e-data-book.c
index 3c0b5d2..fa72231 100644
--- a/addressbook/libedata-book/e-data-book.c
+++ b/addressbook/libedata-book/e-data-book.c
@@ -68,7 +68,7 @@ typedef enum {
 	OP_AUTHENTICATE,
 	OP_ADD_CONTACTS,
 	OP_REMOVE_CONTACTS,
-	OP_MODIFY_CONTACT,
+	OP_MODIFY_CONTACTS,
 	OP_GET_BACKEND_PROPERTY,
 	OP_SET_BACKEND_PROPERTY,
 	OP_GET_VIEW,
@@ -93,9 +93,8 @@ typedef struct {
 		/* OP_REMOVE_CONTACTS */
 		GSList *ids;
 		/* OP_ADD_CONTACT */
+		/* OP_MODIFY_CONTACTS */
 		GSList *vcards;
-		/* OP_MODIFY_CONTACT */
-		gchar *vcard;
 		/* OP_GET_VIEW */
 		/* OP_GET_CONTACTS */
 		/* OP_GET_CONTACTS_UIDS */
@@ -170,9 +169,9 @@ operation_thread (gpointer data,
 		e_book_backend_get_contact_list_uids (backend, op->book, op->id, op->cancellable, op->d.query);
 		g_free (op->d.query);
 		break;
-	case OP_MODIFY_CONTACT:
-		e_book_backend_modify_contact (backend, op->book, op->id, op->cancellable, op->d.vcard);
-		g_free (op->d.vcard);
+	case OP_MODIFY_CONTACTS:
+		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_REMOVE_CONTACTS:
 		e_book_backend_remove_contacts (backend, op->book, op->id, op->cancellable, op->d.ids);
@@ -639,25 +638,25 @@ impl_Book_add_contacts (EGdbusBook *object,
 }
 
 static gboolean
-impl_Book_modify_contact (EGdbusBook *object,
-                          GDBusMethodInvocation *invocation,
-                          const gchar *in_vcard,
-                          EDataBook *book)
+impl_Book_modify_contacts (EGdbusBook *object,
+                           GDBusMethodInvocation *invocation,
+                           const gchar * const *in_vcards,
+                           EDataBook *book)
 {
 	OperationData *op;
 
-	if (in_vcard == NULL) {
+	if (in_vcards == NULL || !*in_vcards) {
 		GError *error = e_data_book_create_error (E_DATA_BOOK_STATUS_INVALID_QUERY, NULL);
 		/* Translators: This is prefix to a detailed error message */
-		data_book_return_error (invocation, error, _("Cannot modify contact: "));
+		data_book_return_error (invocation, error, _("Cannot modify contacts: "));
 		g_error_free (error);
 		return TRUE;
 	}
 
-	op = op_new (OP_MODIFY_CONTACT, book);
-	op->d.vcard = g_strdup (in_vcard);
+	op = op_new (OP_MODIFY_CONTACTS, book);
+	op->d.vcards = e_util_strv_to_slist (in_vcards);
 
-	e_gdbus_book_complete_modify_contact (book->priv->gdbus_object, invocation, op->id);
+	e_gdbus_book_complete_modify_contacts (book->priv->gdbus_object, invocation, op->id);
 	e_operation_pool_push (ops_pool, op);
 
 	return TRUE;
@@ -1051,22 +1050,26 @@ e_data_book_respond_create_contacts (EDataBook *book,
 }
 
 void
-e_data_book_respond_modify (EDataBook *book,
-                            guint32 opid,
-                            GError *error,
-                            const EContact *contact)
+e_data_book_respond_modify_contacts (EDataBook *book,
+                                     guint32 opid,
+                                     GError *error,
+                                     const GSList *contacts)
 {
 	op_complete (book, opid);
 
 	/* Translators: This is prefix to a detailed error message */
-	g_prefix_error (&error, "%s", _("Cannot modify contact: "));
+	g_prefix_error (&error, "%s", _("Cannot modify contacts: "));
 
-	e_gdbus_book_emit_modify_contact_done (book->priv->gdbus_object, opid, error);
+	e_gdbus_book_emit_modify_contacts_done (book->priv->gdbus_object, opid, error);
 
 	if (error) {
 		g_error_free (error);
 	} else {
-		e_book_backend_notify_update (e_data_book_get_backend (book), contact);
+		const GSList *l;
+
+		for (l = contacts; l != NULL; l = l->next)
+			e_book_backend_notify_update (e_data_book_get_backend (book), l->data);
+
 		e_book_backend_notify_complete (e_data_book_get_backend (book));
 	}
 }
@@ -1396,8 +1399,8 @@ e_data_book_init (EDataBook *ebook)
 		gdbus_object, "handle-remove-contacts",
 		G_CALLBACK (impl_Book_remove_contacts), ebook);
 	g_signal_connect (
-		gdbus_object, "handle-modify-contact",
-		G_CALLBACK (impl_Book_modify_contact), ebook);
+		gdbus_object, "handle-modify-contacts",
+		G_CALLBACK (impl_Book_modify_contacts), ebook);
 	g_signal_connect (
 		gdbus_object, "handle-get-backend-property",
 		G_CALLBACK (impl_Book_get_backend_property), ebook);
diff --git a/addressbook/libedata-book/e-data-book.h b/addressbook/libedata-book/e-data-book.h
index 0b900c5..2bd6d65 100644
--- a/addressbook/libedata-book/e-data-book.h
+++ b/addressbook/libedata-book/e-data-book.h
@@ -138,7 +138,7 @@ void		e_data_book_respond_get_backend_property	(EDataBook *book, guint32 opid, G
 void		e_data_book_respond_set_backend_property	(EDataBook *book, guint32 opid, GError *error);
 void		e_data_book_respond_create_contacts		(EDataBook *book, guint32 opid, GError *error, const GSList *contacts);
 void		e_data_book_respond_remove_contacts		(EDataBook *book, guint32 opid, GError *error, const GSList *ids);
-void		e_data_book_respond_modify			(EDataBook *book, guint32 opid, GError *error, const EContact *contact);
+void		e_data_book_respond_modify_contacts		(EDataBook *book, guint32 opid, GError *error, const GSList *contacts);
 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);
diff --git a/addressbook/libegdbus/e-gdbus-book.c b/addressbook/libegdbus/e-gdbus-book.c
index 7b0f5fa..5dd4b37 100644
--- a/addressbook/libegdbus/e-gdbus-book.c
+++ b/addressbook/libegdbus/e-gdbus-book.c
@@ -57,8 +57,8 @@ enum
 	__ADD_CONTACTS_DONE_SIGNAL,
 	__REMOVE_CONTACTS_METHOD,
 	__REMOVE_CONTACTS_DONE_SIGNAL,
-	__MODIFY_CONTACT_METHOD,
-	__MODIFY_CONTACT_DONE_SIGNAL,
+	__MODIFY_CONTACTS_METHOD,
+	__MODIFY_CONTACTS_DONE_SIGNAL,
 	__GET_BACKEND_PROPERTY_METHOD,
 	__GET_BACKEND_PROPERTY_DONE_SIGNAL,
 	__SET_BACKEND_PROPERTY_METHOD,
@@ -144,7 +144,7 @@ E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRV (GDBUS_BOOK_INTERFACE_NAME,
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID (GDBUS_BOOK_INTERFACE_NAME,
                                                       remove_contacts)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID (GDBUS_BOOK_INTERFACE_NAME,
-                                                      modify_contact)
+                                                      modify_contacts)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRING (GDBUS_BOOK_INTERFACE_NAME,
                                                         get_backend_property)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID (GDBUS_BOOK_INTERFACE_NAME,
@@ -178,7 +178,7 @@ e_gdbus_book_default_init (EGdbusBookIface *iface)
 	E_INIT_GDBUS_METHOD_ASYNC_STRING__STRV	(EGdbusBookIface, "get_contact_list_uids",	get_contact_list_uids, __GET_CONTACT_LIST_UIDS_METHOD, __GET_CONTACT_LIST_UIDS_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_ASYNC_STRV__STRV    (EGdbusBookIface, "add_contacts",		add_contacts, __ADD_CONTACTS_METHOD, __ADD_CONTACTS_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_ASYNC_STRV__VOID	(EGdbusBookIface, "remove_contacts",		remove_contacts, __REMOVE_CONTACTS_METHOD, __REMOVE_CONTACTS_DONE_SIGNAL)
-	E_INIT_GDBUS_METHOD_ASYNC_STRING__VOID	(EGdbusBookIface, "modify_contact",		modify_contact, __MODIFY_CONTACT_METHOD, __MODIFY_CONTACT_DONE_SIGNAL)
+	E_INIT_GDBUS_METHOD_ASYNC_STRV__VOID	(EGdbusBookIface, "modify_contacts",		modify_contacts, __MODIFY_CONTACTS_METHOD, __MODIFY_CONTACTS_DONE_SIGNAL)
 	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)
@@ -425,32 +425,32 @@ e_gdbus_book_call_remove_contacts_sync (GDBusProxy *proxy,
 }
 
 void
-e_gdbus_book_call_modify_contact (GDBusProxy *proxy,
-                                  const gchar *in_vcard,
-                                  GCancellable *cancellable,
-                                  GAsyncReadyCallback callback,
-                                  gpointer user_data)
+e_gdbus_book_call_modify_contacts (GDBusProxy *proxy,
+                                   const gchar * const *in_vcards,
+                                   GCancellable *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer user_data)
 {
-	e_gdbus_proxy_call_string ("modify_contact", e_gdbus_book_call_modify_contact, E_GDBUS_ASYNC_OP_KEEPER (proxy), in_vcard, cancellable, callback, user_data);
+	e_gdbus_proxy_call_strv ("modify_contacts", e_gdbus_book_call_modify_contacts, E_GDBUS_ASYNC_OP_KEEPER (proxy), in_vcards, cancellable, callback, user_data);
 }
 
 gboolean
-e_gdbus_book_call_modify_contact_finish (GDBusProxy *proxy,
+e_gdbus_book_call_modify_contacts_finish (GDBusProxy *proxy,
                                          GAsyncResult *result,
                                          GError **error)
 {
-	return e_gdbus_proxy_finish_call_void (E_GDBUS_ASYNC_OP_KEEPER (proxy), result, error, e_gdbus_book_call_modify_contact);
+	return e_gdbus_proxy_finish_call_void (E_GDBUS_ASYNC_OP_KEEPER (proxy), result, error, e_gdbus_book_call_modify_contacts);
 }
 
 gboolean
-e_gdbus_book_call_modify_contact_sync (GDBusProxy *proxy,
-                                       const gchar *in_vcard,
-                                       GCancellable *cancellable,
-                                       GError **error)
+e_gdbus_book_call_modify_contacts_sync (GDBusProxy *proxy,
+                                        const gchar * const *in_vcards,
+                                        GCancellable *cancellable,
+                                        GError **error)
 {
-	return e_gdbus_proxy_call_sync_string__void (proxy, in_vcard, cancellable, error,
-		e_gdbus_book_call_modify_contact,
-		e_gdbus_book_call_modify_contact_finish);
+	return e_gdbus_proxy_call_sync_strv__void (proxy, in_vcards, cancellable, error,
+		e_gdbus_book_call_modify_contacts,
+		e_gdbus_book_call_modify_contacts_finish);
 }
 
 void
@@ -699,8 +699,8 @@ DECLARE_EMIT_DONE_SIGNAL_1 (add_contacts,
                             const gchar * const *)
 DECLARE_EMIT_DONE_SIGNAL_0 (remove_contacts,
                             __REMOVE_CONTACTS_DONE_SIGNAL)
-DECLARE_EMIT_DONE_SIGNAL_0 (modify_contact,
-                            __MODIFY_CONTACT_DONE_SIGNAL)
+DECLARE_EMIT_DONE_SIGNAL_0 (modify_contacts,
+                            __MODIFY_CONTACTS_DONE_SIGNAL)
 DECLARE_EMIT_DONE_SIGNAL_1 (get_backend_property,
                             __GET_BACKEND_PROPERTY_DONE_SIGNAL,
                             const gchar *)
@@ -772,7 +772,7 @@ E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(book, get_contact_list, query, "s",
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(book, get_contact_list_uids, query, "s", uids, "as")
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(book, add_contacts, vcards, "as", uids, "as")
 E_DECLARE_GDBUS_ASYNC_METHOD_1			(book, remove_contacts, list, "as")
-E_DECLARE_GDBUS_ASYNC_METHOD_1			(book, modify_contact, vcard, "s")
+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")
@@ -794,7 +794,7 @@ static const GDBusMethodInfo * const e_gdbus_book_method_info_pointers[] =
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, get_contact_list_uids),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, add_contacts),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, remove_contacts),
-	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, modify_contact),
+	&E_DECLARED_GDBUS_METHOD_INFO_NAME (book, modify_contacts),
 	&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),
@@ -822,7 +822,7 @@ static const GDBusSignalInfo * const e_gdbus_book_signal_info_pointers[] =
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, get_contact_list_uids_done),
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, add_contacts_done),
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, remove_contacts_done),
-	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, modify_contact_done),
+	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, modify_contacts_done),
 	&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),
@@ -1044,7 +1044,7 @@ e_gdbus_book_proxy_init (EGdbusBookProxy *proxy)
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRV   (get_contact_list_uids);
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRV   (add_contacts);
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID   (remove_contacts);
-	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID   (modify_contact);
+	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID   (modify_contacts);
 	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);
diff --git a/addressbook/libegdbus/e-gdbus-book.h b/addressbook/libegdbus/e-gdbus-book.h
index 417c1f1..9a6d851 100644
--- a/addressbook/libegdbus/e-gdbus-book.h
+++ b/addressbook/libegdbus/e-gdbus-book.h
@@ -144,8 +144,8 @@ struct _EGdbusBookIface
 	gboolean (*handle_remove_contacts)	(EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar * const *in_list);
 	void	 (*remove_contacts_done)	(EGdbusBook *object, guint arg_opid, const GError *arg_error);
 
-	gboolean (*handle_modify_contact)	(EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar *in_vcard);
-	void	 (*modify_contact_done)		(EGdbusBook *object, guint arg_opid, const GError *arg_error);
+	gboolean (*handle_modify_contacts)	(EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar * const *in_vcards);
+	void	 (*modify_contacts_done)	(EGdbusBook *object, guint arg_opid, const GError *arg_error);
 
 	gboolean (*handle_get_backend_property)	(EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar *in_prop_name);
 	void	 (*get_backend_property_done)	(EGdbusBook *object, guint arg_opid, const GError *arg_error, gchar **out_prop_value);
@@ -198,9 +198,9 @@ void		e_gdbus_book_call_remove_contacts (GDBusProxy *proxy, const gchar * const
 gboolean	e_gdbus_book_call_remove_contacts_finish (GDBusProxy *proxy, GAsyncResult *result, GError **error);
 gboolean	e_gdbus_book_call_remove_contacts_sync (GDBusProxy *proxy, const gchar * const *in_list, GCancellable *cancellable, GError **error);
 
-void		e_gdbus_book_call_modify_contact (GDBusProxy *proxy, const gchar *in_vcard, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
-gboolean	e_gdbus_book_call_modify_contact_finish (GDBusProxy *proxy, GAsyncResult *result, GError **error);
-gboolean	e_gdbus_book_call_modify_contact_sync (GDBusProxy *proxy, const gchar *in_vcard, GCancellable *cancellable, GError **error);
+void		e_gdbus_book_call_modify_contacts (GDBusProxy *proxy, const gchar * const *in_vcards, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+gboolean	e_gdbus_book_call_modify_contacts_finish (GDBusProxy *proxy, GAsyncResult *result, GError **error);
+gboolean	e_gdbus_book_call_modify_contacts_sync (GDBusProxy *proxy, const gchar * const *in_vcards, GCancellable *cancellable, GError **error);
 
 void		e_gdbus_book_call_get_backend_property (GDBusProxy *proxy, const gchar *in_prop_name, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 gboolean	e_gdbus_book_call_get_backend_property_finish (GDBusProxy *proxy, GAsyncResult *result, gchar **out_prop_value, GError **error);
@@ -241,7 +241,7 @@ gboolean	e_gdbus_book_call_close_sync (GDBusProxy *proxy, GCancellable *cancella
 #define e_gdbus_book_complete_get_contact_list_uids		e_gdbus_complete_async_method
 #define e_gdbus_book_complete_add_contacts			e_gdbus_complete_async_method
 #define e_gdbus_book_complete_remove_contacts			e_gdbus_complete_async_method
-#define e_gdbus_book_complete_modify_contact			e_gdbus_complete_async_method
+#define e_gdbus_book_complete_modify_contacts			e_gdbus_complete_async_method
 #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
@@ -258,7 +258,7 @@ void e_gdbus_book_emit_get_contact_list_done		(EGdbusBook *object, guint arg_opi
 void e_gdbus_book_emit_get_contact_list_uids_done	(EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar * const *out_uids);
 void e_gdbus_book_emit_add_contacts_done		(EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar *const *out_uids);
 void e_gdbus_book_emit_remove_contacts_done		(EGdbusBook *object, guint arg_opid, const GError *arg_error);
-void e_gdbus_book_emit_modify_contact_done		(EGdbusBook *object, guint arg_opid, const GError *arg_error);
+void e_gdbus_book_emit_modify_contacts_done		(EGdbusBook *object, guint arg_opid, const GError *arg_error);
 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);



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