[evolution-data-server] Bug 670457: Add bulk methods to ECalClient



commit 4a3f2b0930e236580f9044217d77536170f48ab5
Author: Christophe Dumez <christophe dumez intel com>
Date:   Tue Feb 21 09:38:43 2012 +0200

    Bug 670457: Add bulk methods to ECalClient
    
    Add e_cal_client_create_objects*() / e_cal_client_modify_objects*() /
    e_cal_client_remove_objects*() bulk methods to ECalClient.

 calendar/backends/caldav/e-cal-backend-caldav.c    |  252 ++++----
 .../backends/contacts/e-cal-backend-contacts.c     |   16 +-
 calendar/backends/file/e-cal-backend-file.c        |  713 +++++++++++---------
 calendar/backends/http/e-cal-backend-http.c        |   57 +-
 calendar/libecal/e-cal-client.c                    |  476 +++++++++++++-
 calendar/libecal/e-cal-client.h                    |   12 +
 calendar/libecal/e-cal-util.h                      |   27 +
 calendar/libecal/e-cal.c                           |   35 +-
 calendar/libedata-cal/e-cal-backend-sync.c         |  203 +++---
 calendar/libedata-cal/e-cal-backend-sync.h         |   12 +-
 calendar/libedata-cal/e-cal-backend.c              |   92 ++--
 calendar/libedata-cal/e-cal-backend.h              |   12 +-
 calendar/libedata-cal/e-data-cal.c                 |  201 +++---
 calendar/libedata-cal/e-data-cal.h                 |    6 +-
 calendar/libegdbus/e-gdbus-cal.c                   |  271 +++++----
 calendar/libegdbus/e-gdbus-cal.h                   |   50 +-
 configure.ac                                       |    6 +-
 libedataserver/e-data-server-util.c                |   20 +
 libedataserver/e-data-server-util.h                |    1 +
 tests/libecal/client/Makefile.am                   |    3 +
 tests/libecal/client/test-client-bulk-methods.c    |  253 +++++++
 21 files changed, 1823 insertions(+), 895 deletions(-)
---
diff --git a/calendar/backends/caldav/e-cal-backend-caldav.c b/calendar/backends/caldav/e-cal-backend-caldav.c
index 323d228..8df9121 100644
--- a/calendar/backends/caldav/e-cal-backend-caldav.c
+++ b/calendar/backends/caldav/e-cal-backend-caldav.c
@@ -3562,21 +3562,29 @@ get_ecalcomp_master_from_cache_or_fallback (ECalBackendCalDAV *cbdav,
 
 /* a busy_lock is supposed to be locked already, when calling this function */
 static void
-do_create_object (ECalBackendCalDAV *cbdav,
-                  const gchar *in_calobj,
-                  gchar **uid,
-                  ECalComponent **new_component,
-                  GError **perror)
+do_create_objects (ECalBackendCalDAV *cbdav,
+                   const GSList *in_calobjs,
+                   GSList **uids,
+                   GSList **new_components,
+                   GError **perror)
 {
 	ECalComponent            *comp;
 	gboolean                  online, did_put = FALSE;
 	struct icaltimetype current;
 	icalcomponent *icalcomp;
+	const gchar *in_calobj = in_calobjs->data;
 	const gchar *comp_uid;
 
 	if (!check_state (cbdav, &online, perror))
 		return;
 
+	/* We make the assumption that the in_calobjs list we're passed is always exactly one element long, since we haven't specified "bulk-adds"
+	 * in our static capability list. This simplifies a lot of the logic, especially around asynchronous results. */
+	if (in_calobjs->next != NULL) {
+		g_propagate_error (perror, e_data_cal_create_error (UnsupportedMethod, _("CalDAV does not support bulk additions")));
+		return;
+	}
+
 	comp = e_cal_component_new_from_string (in_calobj);
 
 	if (comp == NULL) {
@@ -3639,11 +3647,11 @@ do_create_object (ECalBackendCalDAV *cbdav,
 	}
 
 	if (did_put) {
-		if (uid)
-			*uid = g_strdup (comp_uid);
+		if (uids)
+			*uids = g_slist_prepend (*uids, g_strdup (comp_uid));
 
-		if (new_component)
-			*new_component = get_ecalcomp_master_from_cache_or_fallback (cbdav, comp_uid, NULL, comp);
+		if (new_components)
+			*new_components = g_slist_prepend(*new_components, get_ecalcomp_master_from_cache_or_fallback (cbdav, comp_uid, NULL, comp));
 	}
 
 	g_object_unref (comp);
@@ -3651,12 +3659,12 @@ do_create_object (ECalBackendCalDAV *cbdav,
 
 /* a busy_lock is supposed to be locked already, when calling this function */
 static void
-do_modify_object (ECalBackendCalDAV *cbdav,
-                  const gchar *calobj,
-                  CalObjModType mod,
-                  ECalComponent **old_component,
-                  ECalComponent **new_component,
-                  GError **error)
+do_modify_objects (ECalBackendCalDAV *cbdav,
+                   const GSList *calobjs,
+                   CalObjModType mod,
+                   GSList **old_components,
+                   GSList **new_components,
+                   GError **error)
 {
 	ECalComponent            *comp;
 	icalcomponent            *cache_comp;
@@ -3664,13 +3672,21 @@ do_modify_object (ECalBackendCalDAV *cbdav,
 	ECalComponentId		 *id;
 	struct icaltimetype current;
 	gchar *href = NULL, *etag = NULL;
+	const gchar *calobj = calobjs->data;
 
-	if (new_component)
-		*new_component = NULL;
+	if (new_components)
+		*new_components = NULL;
 
 	if (!check_state (cbdav, &online, error))
 		return;
 
+	/* We make the assumption that the calobjs list we're passed is always exactly one element long, since we haven't specified "bulk-modifies"
+	 * in our static capability list. This simplifies a lot of the logic, especially around asynchronous results. */
+	if (calobjs->next != NULL) {
+		g_propagate_error (error, e_data_cal_create_error (UnsupportedMethod, _("CalDAV does not support bulk modifications")));
+		return;
+	}
+
 	comp = e_cal_component_new_from_string (calobj);
 
 	if (comp == NULL) {
@@ -3712,8 +3728,8 @@ do_modify_object (ECalBackendCalDAV *cbdav,
 		/*ecalcomp_set_synch_state (comp, ECALCOMP_LOCALLY_MODIFIED);*/
 	}
 
-	if (old_component) {
-		*old_component = NULL;
+	if (old_components) {
+		*old_components = NULL;
 
 		if (e_cal_component_is_instance (comp)) {
 			/* set detached instance as the old object, if any */
@@ -3721,17 +3737,17 @@ do_modify_object (ECalBackendCalDAV *cbdav,
 
 			/* This will give a reference to 'old_component' */
 			if (old_instance) {
-				*old_component = e_cal_component_clone (old_instance);
+				*old_components = g_slist_prepend (*old_components, e_cal_component_clone (old_instance));
 				g_object_unref (old_instance);
 			}
 		}
 
-		if (!*old_component) {
+		if (!*old_components) {
 			icalcomponent *master = get_master_comp (cbdav, cache_comp);
 
 			if (master) {
 				/* set full component as the old object */
-				*old_component = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master));
+				*old_components = g_slist_prepend (*old_components, e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master)));
 			}
 		}
 	}
@@ -3743,8 +3759,8 @@ do_modify_object (ECalBackendCalDAV *cbdav,
 			icalcomponent *new_comp = e_cal_component_get_icalcomponent (comp);
 
 			/* new object is only this instance */
-			if (new_component)
-				*new_component = e_cal_component_clone (comp);
+			if (new_components)
+				*new_components = g_slist_prepend (*new_components, e_cal_component_clone (comp));
 
 			/* add the detached instance */
 			if (icalcomponent_isa (cache_comp) == ICAL_VCALENDAR_COMPONENT) {
@@ -3798,11 +3814,9 @@ do_modify_object (ECalBackendCalDAV *cbdav,
 	}
 
 	if (did_put) {
-		if (new_component && !*new_component) {
+		if (new_components && !*new_components) {
 			/* read the comp from cache again, as some servers can modify it on put */
-			*new_component = get_ecalcomp_master_from_cache_or_fallback (cbdav, id->uid, id->rid, NULL);
-
-			g_warn_if_fail (*new_component != NULL);
+			*new_components = g_slist_prepend (*new_components, get_ecalcomp_master_from_cache_or_fallback (cbdav, id->uid, id->rid, NULL));
 		}
 	}
 
@@ -3815,24 +3829,32 @@ do_modify_object (ECalBackendCalDAV *cbdav,
 
 /* a busy_lock is supposed to be locked already, when calling this function */
 static void
-do_remove_object (ECalBackendCalDAV *cbdav,
-                  const gchar *uid,
-                  const gchar *rid,
-                  CalObjModType mod,
-                  ECalComponent **old_component,
-                  ECalComponent **new_component,
-                  GError **perror)
+do_remove_objects (ECalBackendCalDAV *cbdav,
+                   const GSList *ids,
+                   CalObjModType mod,
+                   GSList **old_components,
+                   GSList **new_components,
+                   GError **perror)
 {
 	icalcomponent            *cache_comp;
 	gboolean                  online;
 	gchar *href = NULL, *etag = NULL;
+	const gchar *uid = ((ECalComponentId *)ids->data)->uid;
+	const gchar *rid = ((ECalComponentId *)ids->data)->rid;
 
-	if (new_component)
-		*new_component = NULL;
+	if (new_components)
+		*new_components = NULL;
 
 	if (!check_state (cbdav, &online, perror))
 		return;
 
+	/* We make the assumption that the ids list we're passed is always exactly one element long, since we haven't specified "bulk-removes"
+	 * in our static capability list. This simplifies a lot of the logic, especially around asynchronous results. */
+	if (ids->next != NULL) {
+		g_propagate_error (perror, e_data_cal_create_error (UnsupportedMethod, _("CalDAV does not support bulk removals")));
+		return;
+	}
+
 	cache_comp = get_comp_from_cache (cbdav, uid, NULL, &href, &etag);
 
 	if (cache_comp == NULL) {
@@ -3840,17 +3862,16 @@ do_remove_object (ECalBackendCalDAV *cbdav,
 		return;
 	}
 
-	if (old_component) {
+	if (old_components) {
 		ECalComponent *old = e_cal_backend_store_get_component (cbdav->priv->store, uid, rid);
 
 		if (old) {
-			*old_component = e_cal_component_clone (old);
+			*old_components = g_slist_prepend (*old_components, e_cal_component_clone (old));
 			g_object_unref (old);
 		} else {
 			icalcomponent *master = get_master_comp (cbdav, cache_comp);
-
 			if (master) {
-				*old_component = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master));
+				*old_components = g_slist_prepend (*old_components, e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master)));
 			}
 		}
 	}
@@ -3861,11 +3882,11 @@ do_remove_object (ECalBackendCalDAV *cbdav,
 		if (rid && *rid) {
 			/* remove one instance from the component */
 			if (remove_instance (cbdav, cache_comp, icaltime_from_string (rid), mod, mod != CALOBJ_MOD_ONLY_THIS)) {
-				if (new_component) {
+				if (new_components) {
 					icalcomponent *master = get_master_comp (cbdav, cache_comp);
-
-					if (master)
-						*new_component = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master));
+					if (master) {
+						*new_components = g_slist_prepend (*new_components, e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master)));
+					}
 				}
 			} else {
 				/* this was the last instance, thus delete whole component */
@@ -4022,69 +4043,72 @@ process_object (ECalBackendCalDAV *cbdav,
 		is_declined = e_cal_backend_user_declined (e_cal_component_get_icalcomponent (ecomp));
 		if (is_in_cache) {
 			if (!is_declined) {
-				ECalComponent *new_component = NULL, *old_component = NULL;
-
-				do_modify_object (cbdav, new_obj_str, mod,
-						  &old_component, &new_component, &err);
-				if (!err) {
-					if (!old_component)
-						e_cal_backend_notify_component_created (backend, new_component);
-					else
-						e_cal_backend_notify_component_modified (backend, old_component, new_component);
+				GSList *new_components = NULL, *old_components = NULL;
+				GSList new_obj_strs = {0,};
+
+				new_obj_strs.data = new_obj_str;
+				do_modify_objects (cbdav, &new_obj_strs, mod,
+						  &old_components, &new_components, &err);
+				if (!err && new_components && new_components->data) {
+					if (!old_components || !old_components->data) {
+						e_cal_backend_notify_component_created (backend, new_components->data);
+					} else {
+						e_cal_backend_notify_component_modified (backend, old_components->data, new_components->data);
+					}
 				}
 
-				if (new_component)
-					g_object_unref (new_component);
-				if (old_component)
-					g_object_unref (old_component);
+				e_util_free_nullable_object_slist (old_components);
+				e_util_free_nullable_object_slist (new_components);
 			} else {
-				ECalComponent *new_component = NULL, *old_component = NULL;
-
-				do_remove_object (cbdav, id->uid, id->rid, mod, &old_component, &new_component, &err);
-				if (!err) {
-					if (new_component) {
-						e_cal_backend_notify_component_modified (backend, old_component, new_component);
+				GSList *new_components = NULL, *old_components = NULL;
+				GSList ids = {0,};
+
+				ids.data = id;
+				do_remove_objects (cbdav, &ids, mod, &old_components, &new_components, &err);
+				if (!err && old_components && old_components->data) {
+					if (new_components && new_components->data) {
+						e_cal_backend_notify_component_modified (backend, old_components->data, new_components->data);
 					} else {
-						e_cal_backend_notify_component_removed (backend, id, old_component, NULL);
+						e_cal_backend_notify_component_removed (backend, id, old_components->data, NULL);
 					}
 				}
 
-				if (new_component)
-					g_object_unref (new_component);
-				if (old_component)
-					g_object_unref (old_component);
+				e_util_free_nullable_object_slist (old_components);
+				e_util_free_nullable_object_slist (new_components);
 			}
 		} else if (!is_declined) {
-			ECalComponent *new_component = NULL;
+			GSList *new_components = NULL;
+			GSList new_objs = {0,};
 
-			do_create_object (cbdav, new_obj_str, NULL, &new_component, &err);
+			new_objs.data = new_obj_str;
+
+			do_create_objects (cbdav, &new_objs, NULL, &new_components, &err);
 
 			if (!err) {
-				e_cal_backend_notify_component_created (backend, new_component);
+				if (new_components && new_components->data)
+					e_cal_backend_notify_component_created (backend, new_components->data);
 			}
 
-			if (new_component)
-				g_object_unref (new_component);
-
+			e_util_free_nullable_object_slist (new_components);
 		}
 		break;
 	case ICAL_METHOD_CANCEL:
 		if (is_in_cache) {
-			ECalComponent *new_component = NULL, *old_component = NULL;
-
-			do_remove_object (cbdav, id->uid, id->rid, CALOBJ_MOD_THIS, &old_component, &new_component, &err);
-			if (!err) {
-				if (new_component) {
-					e_cal_backend_notify_component_modified (backend, old_component, new_component);
+			GSList *new_components = NULL, *old_components = NULL;
+			GSList ids = {0,};
+
+			ids.data = id;
+			do_remove_objects (cbdav, &ids, CALOBJ_MOD_THIS, &old_components, &new_components, &err);
+			if (!err && old_components && old_components->data) {
+				if (new_components && new_components->data) {
+					e_cal_backend_notify_component_modified (backend, old_components->data, new_components->data);
 				} else {
-					e_cal_backend_notify_component_removed (backend, id, old_component, NULL);
+					e_cal_backend_notify_component_removed (backend, id, old_components->data, NULL);
 				}
 			}
 
-			if (new_component)
-				g_object_unref (new_component);
-			if (old_component)
-				g_object_unref (old_component);
+			e_util_free_nullable_object_slist (old_components);
+			e_util_free_nullable_object_slist (new_components);
 		} else {
 			err = EDC_ERROR (ObjectNotFound);
 		}
@@ -4203,57 +4227,55 @@ _func_name _params							\
 }
 
 caldav_busy_stub (
-        caldav_create_object,
+        caldav_create_objects,
                   (ECalBackendSync *backend,
                   EDataCal *cal,
                   GCancellable *cancellable,
-                  const gchar *in_calobj,
-                  gchar **uid,
-                  ECalComponent **new_component,
+                  const GSList *in_calobjs,
+                  GSList **uids,
+                  GSList **new_components,
                   GError **perror),
-        do_create_object,
+        do_create_objects,
                   (cbdav,
-                  in_calobj,
-                  uid,
-                  new_component,
+                  in_calobjs,
+                  uids,
+                  new_components,
                   perror))
 
 caldav_busy_stub (
-        caldav_modify_object,
+        caldav_modify_objects,
                   (ECalBackendSync *backend,
                   EDataCal *cal,
                   GCancellable *cancellable,
-                  const gchar *calobj,
+                  const GSList *calobjs,
                   CalObjModType mod,
-                  ECalComponent **old_component,
-                  ECalComponent **new_component,
+                  GSList **old_components,
+                  GSList **new_components,
                   GError **perror),
-        do_modify_object,
+        do_modify_objects,
                   (cbdav,
-                  calobj,
+                  calobjs,
                   mod,
-                  old_component,
-                  new_component,
+                  old_components,
+                  new_components,
                   perror))
 
 caldav_busy_stub (
-        caldav_remove_object,
+        caldav_remove_objects,
                   (ECalBackendSync *backend,
                   EDataCal *cal,
                   GCancellable *cancellable,
-                  const gchar *uid,
-                  const gchar *rid,
+                  const GSList *ids,
                   CalObjModType mod,
-                  ECalComponent **old_component,
-                  ECalComponent **new_component,
+                  GSList **old_components,
+                  GSList **new_components,
                   GError **perror),
-        do_remove_object,
+        do_remove_objects,
                   (cbdav,
-                  uid,
-                  rid,
+                  ids,
                   mod,
-                  old_component,
-                  new_component,
+                  old_components,
+                  new_components,
                   perror))
 
 caldav_busy_stub (
@@ -4862,9 +4884,9 @@ e_cal_backend_caldav_class_init (ECalBackendCalDAVClass *class)
 	sync_class->refresh_sync		= caldav_refresh;
 	sync_class->remove_sync			= caldav_remove;
 
-	sync_class->create_object_sync		= caldav_create_object;
-	sync_class->modify_object_sync		= caldav_modify_object;
-	sync_class->remove_object_sync		= caldav_remove_object;
+	sync_class->create_objects_sync		= caldav_create_objects;
+	sync_class->modify_objects_sync		= caldav_modify_objects;
+	sync_class->remove_objects_sync		= caldav_remove_objects;
 
 	sync_class->receive_objects_sync	= caldav_receive_objects;
 	sync_class->send_objects_sync		= caldav_send_objects;
diff --git a/calendar/backends/contacts/e-cal-backend-contacts.c b/calendar/backends/contacts/e-cal-backend-contacts.c
index f595ece..d0e19d7 100644
--- a/calendar/backends/contacts/e-cal-backend-contacts.c
+++ b/calendar/backends/contacts/e-cal-backend-contacts.c
@@ -1582,13 +1582,13 @@ e_cal_backend_contacts_init (ECalBackendContacts *cbc)
 }
 
 static void
-e_cal_backend_contacts_create_object (ECalBackendSync *backend,
-                                      EDataCal *cal,
-                                      GCancellable *cancellable,
-                                      const gchar *calobj,
-                                      gchar **uid,
-                                      ECalComponent **new_component,
-                                      GError **perror)
+e_cal_backend_contacts_create_objects (ECalBackendSync *backend,
+                                       EDataCal *cal,
+                                       GCancellable *cancellable,
+                                       const GSList *calobjs,
+                                       GSList **uids,
+                                       GSList **new_components,
+                                       GError **perror)
 {
 	g_propagate_error (perror, EDC_ERROR (PermissionDenied));
 }
@@ -1612,7 +1612,7 @@ e_cal_backend_contacts_class_init (ECalBackendContactsClass *class)
 	sync_class->get_backend_property_sync	= e_cal_backend_contacts_get_backend_property;
 	sync_class->open_sync			= e_cal_backend_contacts_open;
 	sync_class->remove_sync			= e_cal_backend_contacts_remove;
-	sync_class->create_object_sync		= e_cal_backend_contacts_create_object;
+	sync_class->create_objects_sync		= e_cal_backend_contacts_create_objects;
 	sync_class->receive_objects_sync	= e_cal_backend_contacts_receive_objects;
 	sync_class->send_objects_sync		= e_cal_backend_contacts_send_objects;
 	sync_class->get_object_sync		= e_cal_backend_contacts_get_object;
diff --git a/calendar/backends/file/e-cal-backend-file.c b/calendar/backends/file/e-cal-backend-file.c
index 79f4c8b..b63aea9 100644
--- a/calendar/backends/file/e-cal-backend-file.c
+++ b/calendar/backends/file/e-cal-backend-file.c
@@ -485,7 +485,10 @@ e_cal_backend_file_get_backend_property (ECalBackendSync *backend,
 					CAL_STATIC_CAPABILITY_NO_THISANDFUTURE ","
 					CAL_STATIC_CAPABILITY_DELEGATE_SUPPORTED ","
 					CAL_STATIC_CAPABILITY_REMOVE_ONLY_THIS ","
-					CAL_STATIC_CAPABILITY_NO_THISANDPRIOR);
+					CAL_STATIC_CAPABILITY_NO_THISANDPRIOR ","
+					CAL_STATIC_CAPABILITY_BULK_ADDS ","
+					CAL_STATIC_CAPABILITY_BULK_MODIFIES ","
+					CAL_STATIC_CAPABILITY_BULK_REMOVES);
 	} else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS) ||
 		   g_str_equal (prop_name, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) {
 		/* A file backend has no particular email address associated
@@ -2242,96 +2245,125 @@ sanitize_component (ECalBackendFile *cbfile,
 }
 
 static void
-e_cal_backend_file_create_object (ECalBackendSync *backend,
-                                  EDataCal *cal,
-                                  GCancellable *cancellable,
-                                  const gchar *in_calobj,
-                                  gchar **uid,
-                                  ECalComponent **new_component,
-                                  GError **error)
+e_cal_backend_file_create_objects (ECalBackendSync *backend,
+                                   EDataCal *cal,
+                                   GCancellable *cancellable,
+                                   const GSList *in_calobjs,
+                                   GSList **uids,
+                                   GSList **new_components,
+                                   GError **error)
 {
 	ECalBackendFile *cbfile;
 	ECalBackendFilePrivate *priv;
-	icalcomponent *icalcomp;
-	ECalComponent *comp;
-	const gchar *comp_uid;
-	struct icaltimetype current;
+	GSList *icalcomps = NULL;
+	const GSList *l;
 
 	cbfile = E_CAL_BACKEND_FILE (backend);
 	priv = cbfile->priv;
 
 	e_return_data_cal_error_if_fail (priv->icalcomp != NULL, NoSuchCal);
-	e_return_data_cal_error_if_fail (in_calobj != NULL, ObjectNotFound);
-	e_return_data_cal_error_if_fail (new_component != NULL, ObjectNotFound);
-
-	/* Parse the icalendar text */
-	icalcomp = icalparser_parse_string (in_calobj);
-	if (!icalcomp) {
-		g_propagate_error (error, EDC_ERROR (InvalidObject));
-		return;
-	}
+	e_return_data_cal_error_if_fail (in_calobjs != NULL, ObjectNotFound);
+	e_return_data_cal_error_if_fail (new_components != NULL, ObjectNotFound);
 
-	/* Check kind with the parent */
-	if (icalcomponent_isa (icalcomp) != e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
-		icalcomponent_free (icalcomp);
-		g_propagate_error (error, EDC_ERROR (InvalidObject));
-		return;
-	}
+	if (uids)
+		*uids = NULL;
 
 	g_static_rec_mutex_lock (&priv->idle_save_rmutex);
 
-	/* Get the UID */
-	comp_uid = icalcomponent_get_uid (icalcomp);
-	if (!comp_uid) {
-		gchar *new_uid;
+	/* First step, parse input strings and do uid verification: may fail */
+	for (l = in_calobjs; l; l = l->next) {
+		icalcomponent *icalcomp;
+		const gchar *comp_uid;
 
-		new_uid = e_cal_component_gen_uid ();
-		if (!new_uid) {
-			icalcomponent_free (icalcomp);
+		/* Parse the icalendar text */
+		icalcomp = icalparser_parse_string ((gchar *) l->data);
+		if (!icalcomp) {
+			g_slist_free_full (icalcomps, (GDestroyNotify) icalcomponent_free);
+			g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
+			g_propagate_error (error, EDC_ERROR (InvalidObject));
+			return;
+		}
+
+		/* Append icalcomponent to icalcomps */
+		icalcomps = g_slist_prepend (icalcomps, icalcomp);
+
+		/* Check kind with the parent */
+		if (icalcomponent_isa (icalcomp) != e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
+			g_slist_free_full (icalcomps, (GDestroyNotify) icalcomponent_free);
 			g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
 			g_propagate_error (error, EDC_ERROR (InvalidObject));
 			return;
 		}
 
-		icalcomponent_set_uid (icalcomp, new_uid);
+		/* Get the UID */
 		comp_uid = icalcomponent_get_uid (icalcomp);
+		if (!comp_uid) {
+			gchar *new_uid;
 
-		g_free (new_uid);
-	}
+			new_uid = e_cal_component_gen_uid ();
+			if (!new_uid) {
+				g_slist_free_full (icalcomps, (GDestroyNotify) icalcomponent_free);
+				g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
+				g_propagate_error (error, EDC_ERROR (InvalidObject));
+				return;
+			}
 
-	/* check the object is not in our cache */
-	if (uid_in_use (cbfile, comp_uid)) {
-		icalcomponent_free (icalcomp);
-		g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
-		g_propagate_error (error, EDC_ERROR (ObjectIdAlreadyExists));
-		return;
+			icalcomponent_set_uid (icalcomp, new_uid);
+			comp_uid = icalcomponent_get_uid (icalcomp);
+
+			g_free (new_uid);
+		}
+
+		/* check that the object is not in our cache */
+		if (uid_in_use (cbfile, comp_uid)) {
+			g_slist_free_full (icalcomps, (GDestroyNotify) icalcomponent_free);
+			g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
+			g_propagate_error (error, EDC_ERROR (ObjectIdAlreadyExists));
+			return;
+		}
 	}
 
-	/* Create the cal component */
-	comp = e_cal_component_new ();
-	e_cal_component_set_icalcomponent (comp, icalcomp);
+	icalcomps = g_slist_reverse (icalcomps);
+
+	/* Second step, add the objects */
+	for (l = icalcomps; l; l = l->next) {
+		ECalComponent *comp;
+		struct icaltimetype current;
+		icalcomponent *icalcomp = l->data;
+
+		/* Create the cal component */
+		comp = e_cal_component_new ();
+		e_cal_component_set_icalcomponent (comp, icalcomp);
+
+		/* Set the created and last modified times on the component */
+		current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
+		e_cal_component_set_created (comp, &current);
+		e_cal_component_set_last_modified (comp, &current);
+
+		/* sanitize the component*/
+		sanitize_component (cbfile, comp);
+
+		/* Add the object */
+		add_component (cbfile, comp, TRUE);
 
-	/* Set the created and last modified times on the component */
-	current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
-	e_cal_component_set_created (comp, &current);
-	e_cal_component_set_last_modified (comp, &current);
+		/* Keep the UID and the modified component to return them later */
+		if (uids)
+			*uids = g_slist_prepend (*uids, g_strdup (icalcomponent_get_uid (icalcomp)));
 
-	/* sanitize the component*/
-	sanitize_component (cbfile, comp);
+		*new_components = g_slist_prepend (*new_components, e_cal_component_clone (comp));
+	}
 
-	/* Add the object */
-	add_component (cbfile, comp, TRUE);
+	g_slist_free (icalcomps);
 
 	/* Save the file */
 	save (cbfile, TRUE);
 
-	/* Return the UID and the modified component */
-	if (uid)
-		*uid = g_strdup (comp_uid);
+	g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
 
-	*new_component = e_cal_component_clone (comp);
+	if (uids)
+		*uids = g_slist_reverse (*uids);
 
-	g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
+	*new_components = g_slist_reverse (*new_components);
 }
 
 typedef struct {
@@ -2371,32 +2403,25 @@ remove_object_instance_cb (gpointer key,
 }
 
 static void
-e_cal_backend_file_modify_object (ECalBackendSync *backend,
-                                  EDataCal *cal,
-                                  GCancellable *cancellable,
-                                  const gchar *calobj,
-                                  CalObjModType mod,
-                                  ECalComponent **old_component,
-                                  ECalComponent **new_component,
-                                  GError **error)
+e_cal_backend_file_modify_objects (ECalBackendSync *backend,
+                                   EDataCal *cal,
+                                   GCancellable *cancellable,
+                                   const GSList *calobjs,
+                                   CalObjModType mod,
+                                   GSList **old_components,
+                                   GSList **new_components,
+                                   GError **error)
 {
-	RemoveRecurrenceData rrdata;
 	ECalBackendFile *cbfile;
 	ECalBackendFilePrivate *priv;
-	icalcomponent *icalcomp;
-	const gchar *comp_uid;
-	gchar *rid = NULL;
-	gchar *real_rid;
-	ECalComponent *comp, *recurrence;
-	ECalBackendFileObject *obj_data;
-	struct icaltimetype current;
-	GList *detached = NULL;
+	GSList *icalcomps = NULL;
+	const GSList *l;
 
 	cbfile = E_CAL_BACKEND_FILE (backend);
 	priv = cbfile->priv;
 
 	e_return_data_cal_error_if_fail (priv->icalcomp != NULL, NoSuchCal);
-	e_return_data_cal_error_if_fail (calobj != NULL, ObjectNotFound);
+	e_return_data_cal_error_if_fail (calobjs != NULL, ObjectNotFound);
 	switch (mod) {
 	case CALOBJ_MOD_THIS:
 	case CALOBJ_MOD_THISANDPRIOR:
@@ -2408,226 +2433,251 @@ e_cal_backend_file_modify_object (ECalBackendSync *backend,
 		return;
 	}
 
-	/* Parse the icalendar text */
-	icalcomp = icalparser_parse_string ((gchar *) calobj);
-	if (!icalcomp) {
-		g_propagate_error (error, EDC_ERROR (InvalidObject));
-		return;
-	}
-
-	/* Check kind with the parent */
-	if (icalcomponent_isa (icalcomp) != e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
-		icalcomponent_free (icalcomp);
-		g_propagate_error (error, EDC_ERROR (InvalidObject));
-		return;
-	}
+	if (old_components)
+		*old_components = NULL;
+	if (new_components)
+		*new_components = NULL;
 
 	g_static_rec_mutex_lock (&priv->idle_save_rmutex);
 
-	/* Get the uid */
-	comp_uid = icalcomponent_get_uid (icalcomp);
+	/* First step, parse input strings and do uid verification: may fail */
+	for (l = calobjs; l; l = l->next) {
+		const gchar *comp_uid;
+		icalcomponent *icalcomp;
 
-	/* Get the object from our cache */
-	if (!(obj_data = g_hash_table_lookup (priv->comp_uid_hash, comp_uid))) {
-		icalcomponent_free (icalcomp);
-		g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
-		g_propagate_error (error, EDC_ERROR (ObjectNotFound));
-		return;
-	}
+		/* Parse the icalendar text */
+		icalcomp = icalparser_parse_string (l->data);
+		if (!icalcomp) {
+			g_slist_free_full (icalcomps, (GDestroyNotify) icalcomponent_free);
+			g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
+			g_propagate_error (error, EDC_ERROR (InvalidObject));
+			return;
+		}
 
-	/* Create the cal component */
-	comp = e_cal_component_new ();
-	e_cal_component_set_icalcomponent (comp, icalcomp);
+		icalcomps = g_slist_prepend (icalcomps, icalcomp);
 
-	/* Set the last modified time on the component */
-	current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
-	e_cal_component_set_last_modified (comp, &current);
+		/* Check kind with the parent */
+		if (icalcomponent_isa (icalcomp) != e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
+			g_slist_free_full (icalcomps, (GDestroyNotify) icalcomponent_free);
+			g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
+			g_propagate_error (error, EDC_ERROR (InvalidObject));
+			return;
+		}
 
-	/* sanitize the component*/
-	sanitize_component (cbfile, comp);
-	rid = e_cal_component_get_recurid_as_string (comp);
+		/* Get the uid */
+		comp_uid = icalcomponent_get_uid (icalcomp);
 
-	/* handle mod_type */
-	switch (mod) {
-	case CALOBJ_MOD_THIS :
-		if (!rid || !*rid) {
-			if (old_component && obj_data->full_object) {
-				*old_component = e_cal_component_clone (obj_data->full_object);
-			}
+		/* Get the object from our cache */
+		if (!g_hash_table_lookup (priv->comp_uid_hash, comp_uid)) {
+			g_slist_free_full (icalcomps, (GDestroyNotify) icalcomponent_free);
+			g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
+			g_propagate_error (error, EDC_ERROR (ObjectNotFound));
+			return;
+		}
+	}
 
-			/* replace only the full object */
-			if (obj_data->full_object) {
-				icalcomponent_remove_component (priv->icalcomp,
-							e_cal_component_get_icalcomponent (obj_data->full_object));
-				priv->comp = g_list_remove (priv->comp, obj_data->full_object);
+	icalcomps = g_slist_reverse (icalcomps);
 
-				g_object_unref (obj_data->full_object);
-			}
+	/* Second step, update the objects */
+	for (l = icalcomps; l; l = l->next) {
+		struct icaltimetype current;
+		RemoveRecurrenceData rrdata;
+		GList *detached = NULL;
+		gchar *rid = NULL;
+		gchar *real_rid;
+		const gchar *comp_uid;
+		icalcomponent * icalcomp = l->data;
+		ECalComponent *comp, *recurrence;
+		ECalBackendFileObject *obj_data;
 
-			/* add the new object */
-			obj_data->full_object = comp;
+		comp_uid = icalcomponent_get_uid (icalcomp);
+		obj_data = g_hash_table_lookup (priv->comp_uid_hash, comp_uid);
 
-			icalcomponent_add_component (priv->icalcomp,
-						     e_cal_component_get_icalcomponent (obj_data->full_object));
-			priv->comp = g_list_prepend (priv->comp, obj_data->full_object);
+		/* Create the cal component */
+		comp = e_cal_component_new ();
+		e_cal_component_set_icalcomponent (comp, icalcomp);
 
-			save (cbfile, TRUE);
+		/* Set the last modified time on the component */
+		current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
+		e_cal_component_set_last_modified (comp, &current);
 
-			if (new_component) {
-				*new_component = e_cal_component_clone (comp);
-			}
+		/* sanitize the component*/
+		sanitize_component (cbfile, comp);
+		rid = e_cal_component_get_recurid_as_string (comp);
 
-			g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
-			g_free (rid);
-			return;
-		}
+		/* handle mod_type */
+		switch (mod) {
+		case CALOBJ_MOD_THIS :
+			if (!rid || !*rid) {
+				if (old_components)
+					*old_components = g_slist_prepend (*old_components, obj_data->full_object ? e_cal_component_clone (obj_data->full_object) : NULL);
 
-		if (g_hash_table_lookup_extended (obj_data->recurrences, rid, (gpointer *) &real_rid, (gpointer *) &recurrence)) {
-			if (old_component) {
-				*old_component = e_cal_component_clone (recurrence);
-			}
+				/* replace only the full object */
+				if (obj_data->full_object) {
+					icalcomponent_remove_component (priv->icalcomp,
+								e_cal_component_get_icalcomponent (obj_data->full_object));
+					priv->comp = g_list_remove (priv->comp, obj_data->full_object);
 
-			/* remove the component from our data */
-			icalcomponent_remove_component (priv->icalcomp,
-							e_cal_component_get_icalcomponent (recurrence));
-			priv->comp = g_list_remove (priv->comp, recurrence);
-			obj_data->recurrences_list = g_list_remove (obj_data->recurrences_list, recurrence);
-			g_hash_table_remove (obj_data->recurrences, rid);
-		}
+					g_object_unref (obj_data->full_object);
+				}
 
-		/* add the detached instance */
-		g_hash_table_insert (obj_data->recurrences,
-				     rid,
-				     comp);
-		icalcomponent_add_component (priv->icalcomp,
-					     e_cal_component_get_icalcomponent (comp));
-		priv->comp = g_list_append (priv->comp, comp);
-		obj_data->recurrences_list = g_list_append (obj_data->recurrences_list, comp);
-		rid = NULL;
-		break;
-	case CALOBJ_MOD_THISANDPRIOR :
-	case CALOBJ_MOD_THISANDFUTURE :
-		if (!rid || !*rid) {
+				/* add the new object */
+				obj_data->full_object = comp;
 
-			if (old_component && obj_data->full_object) {
-				*old_component = e_cal_component_clone (obj_data->full_object);
+				icalcomponent_add_component (priv->icalcomp,
+								 e_cal_component_get_icalcomponent (obj_data->full_object));
+				priv->comp = g_list_prepend (priv->comp, obj_data->full_object);
+				break;
 			}
 
-			remove_component (cbfile, comp_uid, obj_data);
+			if (g_hash_table_lookup_extended (obj_data->recurrences, rid, (gpointer *) &real_rid, (gpointer *) &recurrence)) {
+				if (*old_components)
+					*old_components = g_slist_prepend (*old_components, e_cal_component_clone (recurrence));
 
-			/* Add the new object */
-			add_component (cbfile, comp, TRUE);
-			g_free (rid);
-			rid = NULL;
+				/* remove the component from our data */
+				icalcomponent_remove_component (priv->icalcomp,
+								e_cal_component_get_icalcomponent (recurrence));
+				priv->comp = g_list_remove (priv->comp, recurrence);
+				obj_data->recurrences_list = g_list_remove (obj_data->recurrences_list, recurrence);
+				g_hash_table_remove (obj_data->recurrences, rid);
+			} else {
+				if (old_components)
+					*old_components = g_slist_prepend (*old_components, NULL);
+			}
+
+			/* add the detached instance */
+			g_hash_table_insert (obj_data->recurrences,
+						 g_strdup (rid),
+						 comp);
+			icalcomponent_add_component (priv->icalcomp,
+							 e_cal_component_get_icalcomponent (comp));
+			priv->comp = g_list_append (priv->comp, comp);
+			obj_data->recurrences_list = g_list_append (obj_data->recurrences_list, comp);
 			break;
-		}
+		case CALOBJ_MOD_THISANDPRIOR :
+		case CALOBJ_MOD_THISANDFUTURE :
+			if (!rid || !*rid) {
+				if (old_components)
+					*old_components = g_slist_prepend (*old_components, obj_data->full_object ? e_cal_component_clone (obj_data->full_object) : NULL);
 
-		/* remove the component from our data, temporarily */
-		if (obj_data->full_object) {
-			icalcomponent_remove_component (priv->icalcomp,
-						e_cal_component_get_icalcomponent (obj_data->full_object));
-			priv->comp = g_list_remove (priv->comp, obj_data->full_object);
-		}
+				remove_component (cbfile, comp_uid, obj_data);
 
-		/* now deal with the detached recurrence */
-		if (g_hash_table_lookup_extended (obj_data->recurrences, rid,
-						  (gpointer *) &real_rid, (gpointer *) &recurrence)) {
+				/* Add the new object */
+				add_component (cbfile, comp, TRUE);
+				break;
+			}
 
-			if (old_component) {
-				*old_component = e_cal_component_clone (recurrence);
+			/* remove the component from our data, temporarily */
+			if (obj_data->full_object) {
+				icalcomponent_remove_component (priv->icalcomp,
+							e_cal_component_get_icalcomponent (obj_data->full_object));
+				priv->comp = g_list_remove (priv->comp, obj_data->full_object);
 			}
 
-			/* remove the component from our data */
-			icalcomponent_remove_component (priv->icalcomp,
-							e_cal_component_get_icalcomponent (recurrence));
-			priv->comp = g_list_remove (priv->comp, recurrence);
-			obj_data->recurrences_list = g_list_remove (obj_data->recurrences_list, recurrence);
-			g_hash_table_remove (obj_data->recurrences, rid);
-		} else {
+			/* now deal with the detached recurrence */
+			if (g_hash_table_lookup_extended (obj_data->recurrences, rid,
+							  (gpointer *) &real_rid, (gpointer *) &recurrence)) {
+				if (old_components)
+					*old_components = g_slist_prepend (*old_components, e_cal_component_clone (recurrence));
 
-			if (old_component && obj_data->full_object) {
-				*old_component = e_cal_component_clone (obj_data->full_object);
+				/* remove the component from our data */
+				icalcomponent_remove_component (priv->icalcomp,
+								e_cal_component_get_icalcomponent (recurrence));
+				priv->comp = g_list_remove (priv->comp, recurrence);
+				obj_data->recurrences_list = g_list_remove (obj_data->recurrences_list, recurrence);
+				g_hash_table_remove (obj_data->recurrences, rid);
+			} else {
+				if (*old_components)
+					*old_components = g_slist_prepend (*old_components, obj_data->full_object ? e_cal_component_clone (obj_data->full_object) : NULL);
 			}
-		}
 
-		rrdata.cbfile = cbfile;
-		rrdata.obj_data = obj_data;
-		rrdata.rid = rid;
-		rrdata.mod = mod;
-		g_hash_table_foreach_remove (obj_data->recurrences, (GHRFunc) remove_object_instance_cb, &rrdata);
+			rrdata.cbfile = cbfile;
+			rrdata.obj_data = obj_data;
+			rrdata.rid = rid;
+			rrdata.mod = mod;
+			g_hash_table_foreach_remove (obj_data->recurrences, (GHRFunc) remove_object_instance_cb, &rrdata);
 
-		/* add the modified object to the beginning of the list,
-		 * so that it's always before any detached instance we
-		 * might have */
-		if (obj_data->full_object) {
-			icalcomponent_add_component (priv->icalcomp,
-					     e_cal_component_get_icalcomponent (obj_data->full_object));
-			priv->comp = g_list_prepend (priv->comp, obj_data->full_object);
-		}
+			/* add the modified object to the beginning of the list,
+			 * so that it's always before any detached instance we
+			 * might have */
+			if (obj_data->full_object) {
+				icalcomponent_add_component (priv->icalcomp,
+							 e_cal_component_get_icalcomponent (obj_data->full_object));
+				priv->comp = g_list_prepend (priv->comp, obj_data->full_object);
+			}
 
-		/* add the new detached recurrence */
-		g_hash_table_insert (obj_data->recurrences,
-				     rid,
-				     comp);
-		icalcomponent_add_component (priv->icalcomp,
-					     e_cal_component_get_icalcomponent (comp));
-		priv->comp = g_list_append (priv->comp, comp);
-		obj_data->recurrences_list = g_list_append (obj_data->recurrences_list, comp);
-		rid = NULL;
-		break;
-	case CALOBJ_MOD_ALL :
-		/* Remove the old version */
-		if (old_component && obj_data->full_object) {
-			*old_component = e_cal_component_clone (obj_data->full_object);
-		}
+			/* add the new detached recurrence */
+			g_hash_table_insert (obj_data->recurrences,
+						 g_strdup (rid),
+						 comp);
+			icalcomponent_add_component (priv->icalcomp,
+							 e_cal_component_get_icalcomponent (comp));
+			priv->comp = g_list_append (priv->comp, comp);
+			obj_data->recurrences_list = g_list_append (obj_data->recurrences_list, comp);
+			break;
+		case CALOBJ_MOD_ALL :
+			/* Remove the old version */
+			if (old_components)
+				*old_components = g_slist_prepend (*old_components, obj_data->full_object ? e_cal_component_clone (obj_data->full_object) : NULL);
 
-		if (obj_data->recurrences_list) {
-			/* has detached components, preserve them */
-			GList *l;
+			if (obj_data->recurrences_list) {
+				/* has detached components, preserve them */
+				GList *ll;
 
-			for (l = obj_data->recurrences_list; l; l = l->next) {
-				detached = g_list_prepend (detached, g_object_ref (l->data));
+				for (ll = obj_data->recurrences_list; ll; ll = ll->next) {
+					detached = g_list_prepend (detached, g_object_ref (ll->data));
+				}
 			}
-		}
 
-		remove_component (cbfile, comp_uid, obj_data);
+			remove_component (cbfile, comp_uid, obj_data);
 
-		/* Add the new object */
-		add_component (cbfile, comp, TRUE);
+			/* Add the new object */
+			add_component (cbfile, comp, TRUE);
 
-		if (detached) {
-			/* it had some detached components, place them back */
-			comp_uid = icalcomponent_get_uid (e_cal_component_get_icalcomponent (comp));
+			if (detached) {
+				/* it had some detached components, place them back */
+				comp_uid = icalcomponent_get_uid (e_cal_component_get_icalcomponent (comp));
 
-			if ((obj_data = g_hash_table_lookup (priv->comp_uid_hash, comp_uid)) != NULL) {
-				GList *l;
+				if ((obj_data = g_hash_table_lookup (priv->comp_uid_hash, comp_uid)) != NULL) {
+					GList *ll;
 
-				for (l = detached; l; l = l->next) {
-					ECalComponent *c = l->data;
+					for (ll = detached; ll; ll = ll->next) {
+						ECalComponent *c = ll->data;
 
-					g_hash_table_insert (obj_data->recurrences, e_cal_component_get_recurid_as_string (c), c);
-					icalcomponent_add_component (priv->icalcomp, e_cal_component_get_icalcomponent (c));
-					priv->comp = g_list_append (priv->comp, c);
-					obj_data->recurrences_list = g_list_append (obj_data->recurrences_list, c);
+						g_hash_table_insert (obj_data->recurrences, e_cal_component_get_recurid_as_string (c), c);
+						icalcomponent_add_component (priv->icalcomp, e_cal_component_get_icalcomponent (c));
+						priv->comp = g_list_append (priv->comp, c);
+						obj_data->recurrences_list = g_list_append (obj_data->recurrences_list, c);
+					}
 				}
+
+				g_list_free (detached);
 			}
+			break;
+		case CALOBJ_MOD_ONLY_THIS:
+			/* not reached, keep compiler happy */
+			break;
+		}
 
-			g_list_free (detached);
+		g_free (rid);
+
+		if (new_components) {
+			*new_components = g_slist_prepend (*new_components, e_cal_component_clone (comp));
 		}
-		break;
-	case CALOBJ_MOD_ONLY_THIS:
-		/* not reached, keep compiler happy */
-		break;
 	}
 
-	save (cbfile, TRUE);
-	g_free (rid);
+	g_slist_free (icalcomps);
 
-	if (new_component) {
-		*new_component = e_cal_component_clone (comp);
-	}
+	/* All the components were updated, now we save the file */
+	save (cbfile, TRUE);
 
 	g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
+
+	if (old_components)
+		*old_components = g_slist_reverse (*old_components);
+
+	if (new_components)
+		*new_components = g_slist_reverse (*new_components);
 }
 
 /**
@@ -2824,28 +2874,26 @@ notify_comp_removed_cb (gpointer pecalcomp,
 
 /* Remove_object handler for the file backend */
 static void
-e_cal_backend_file_remove_object (ECalBackendSync *backend,
-                                  EDataCal *cal,
-                                  GCancellable *cancellable,
-                                  const gchar *uid,
-                                  const gchar *rid,
-                                  CalObjModType mod,
-                                  ECalComponent **old_component,
-                                  ECalComponent **new_component,
-                                  GError **error)
+e_cal_backend_file_remove_objects (ECalBackendSync *backend,
+                                   EDataCal *cal,
+                                   GCancellable *cancellable,
+                                   const GSList *ids,
+                                   CalObjModType mod,
+                                   GSList **old_components,
+                                   GSList **new_components,
+                                   GError **error)
 {
 	ECalBackendFile *cbfile;
 	ECalBackendFilePrivate *priv;
-	ECalBackendFileObject *obj_data;
-	ECalComponent *comp;
-	RemoveRecurrenceData rrdata;
-	const gchar *recur_id = NULL;
+	const GSList *l;
 
 	cbfile = E_CAL_BACKEND_FILE (backend);
 	priv = cbfile->priv;
 
 	e_return_data_cal_error_if_fail (priv->icalcomp != NULL, NoSuchCal);
-	e_return_data_cal_error_if_fail (uid != NULL, ObjectNotFound);
+	e_return_data_cal_error_if_fail (ids != NULL, ObjectNotFound);
+	e_return_data_cal_error_if_fail (old_components != NULL, ObjectNotFound);
+	e_return_data_cal_error_if_fail (new_components != NULL, ObjectNotFound);
 
 	switch (mod) {
 	case CALOBJ_MOD_THIS:
@@ -2859,78 +2907,115 @@ e_cal_backend_file_remove_object (ECalBackendSync *backend,
 		return;
 	}
 
-	*old_component = *new_component = NULL;
+	*old_components = *new_components = NULL;
 
 	g_static_rec_mutex_lock (&priv->idle_save_rmutex);
 
-	obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
-	if (!obj_data) {
-		g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
-		g_propagate_error (error, EDC_ERROR (ObjectNotFound));
-		return;
+	/* First step, validate the input */
+	for (l = ids; l; l = l->next) {
+		ECalComponentId *id = l->data;
+				/* Make the ID contains a uid */
+		if (!id || !id->uid) {
+			g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
+			g_propagate_error (error, EDC_ERROR (ObjectNotFound));
+			return;
+		}
+				/* Check that it has a recurrence id if mod is CALOBJ_MOD_THISANDPRIOR
+					 or CALOBJ_MOD_THISANDFUTURE */
+		if ((mod == CALOBJ_MOD_THISANDPRIOR || mod == CALOBJ_MOD_THISANDFUTURE) &&
+		        (!id->rid || !*(id->rid))) {
+			g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
+			g_propagate_error (error, EDC_ERROR (ObjectNotFound));
+			return;
+		}
+				/* Make sure the uid exists in the local hash table */
+		if (!g_hash_table_lookup (priv->comp_uid_hash, id->uid)) {
+			g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
+			g_propagate_error (error, EDC_ERROR (ObjectNotFound));
+			return;
+		}
 	}
 
-	if (rid && *rid)
-		recur_id = rid;
+	/* Second step, remove objects from the calendar */
+	for (l = ids; l; l = l->next) {
+		const gchar *recur_id = NULL;
+		ECalComponent *comp;
+		RemoveRecurrenceData rrdata;
+		ECalBackendFileObject *obj_data;
+		ECalComponentId *id = l->data;
 
-	switch (mod) {
-	case CALOBJ_MOD_ALL :
-		*old_component = clone_ecalcomp_from_fileobject (obj_data, recur_id);
-		if (obj_data->recurrences_list)
-			g_list_foreach (obj_data->recurrences_list, notify_comp_removed_cb, cbfile);
-		remove_component (cbfile, uid, obj_data);
+		obj_data = g_hash_table_lookup (priv->comp_uid_hash, id->uid);
 
-		*new_component = NULL;
-		break;
-	case CALOBJ_MOD_ONLY_THIS:
-	case CALOBJ_MOD_THIS :
-		remove_instance (cbfile, obj_data, uid, recur_id, mod,
-				 old_component, new_component, error);
-		break;
-	case CALOBJ_MOD_THISANDPRIOR :
-	case CALOBJ_MOD_THISANDFUTURE :
-		comp = obj_data->full_object;
+		if (id->rid && *(id->rid))
+			recur_id = id->rid;
 
-		if (!recur_id || !*recur_id) {
-			g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
-			g_propagate_error (error, EDC_ERROR (ObjectNotFound));
-			return;
-		}
+		switch (mod) {
+		case CALOBJ_MOD_ALL :
+			*old_components = g_slist_prepend (*old_components, clone_ecalcomp_from_fileobject (obj_data, recur_id));
+			*new_components = g_slist_prepend (*new_components, NULL);
 
-		if (comp) {
-			*old_component = e_cal_component_clone (comp);
+			if (obj_data->recurrences_list)
+				g_list_foreach (obj_data->recurrences_list, notify_comp_removed_cb, cbfile);
+			remove_component (cbfile, id->uid, obj_data);
+			break;
+		case CALOBJ_MOD_ONLY_THIS:
+		case CALOBJ_MOD_THIS: {
+			ECalComponent *old_component = NULL;
+			ECalComponent *new_component = NULL;
 
-			/* remove the component from our data, temporarily */
-			icalcomponent_remove_component (priv->icalcomp,
-						e_cal_component_get_icalcomponent (comp));
-			priv->comp = g_list_remove (priv->comp, comp);
+			obj_data = remove_instance (cbfile, obj_data, id->uid, recur_id, mod,
+							&old_component, &new_component, error);
 
-			e_cal_util_remove_instances (e_cal_component_get_icalcomponent (comp),
-					     icaltime_from_string (recur_id), mod);
+			*old_components = g_slist_prepend (*old_components, old_component);
+			*new_components = g_slist_prepend (*new_components, new_component);
+			break;
 		}
+		case CALOBJ_MOD_THISANDPRIOR :
+		case CALOBJ_MOD_THISANDFUTURE :
+			comp = obj_data->full_object;
 
-		/* now remove all detached instances */
-		rrdata.cbfile = cbfile;
-		rrdata.obj_data = obj_data;
-		rrdata.rid = recur_id;
-		rrdata.mod = mod;
-		g_hash_table_foreach_remove (obj_data->recurrences, (GHRFunc) remove_object_instance_cb, &rrdata);
+			if (comp) {
+				*old_components = g_slist_prepend (*old_components, e_cal_component_clone (comp));
 
-		/* add the modified object to the beginning of the list,
-		 * so that it's always before any detached instance we
-		 * might have */
-		if (comp)
-			priv->comp = g_list_prepend (priv->comp, comp);
+				/* remove the component from our data, temporarily */
+				icalcomponent_remove_component (priv->icalcomp,
+							e_cal_component_get_icalcomponent (comp));
+				priv->comp = g_list_remove (priv->comp, comp);
 
-		if (obj_data->full_object) {
-			*new_component = e_cal_component_clone (obj_data->full_object);
+				e_cal_util_remove_instances (e_cal_component_get_icalcomponent (comp),
+							 icaltime_from_string (recur_id), mod);
+			} else {
+				*old_components = g_slist_prepend (*old_components, NULL);
+			}
+
+			/* now remove all detached instances */
+			rrdata.cbfile = cbfile;
+			rrdata.obj_data = obj_data;
+			rrdata.rid = recur_id;
+			rrdata.mod = mod;
+			g_hash_table_foreach_remove (obj_data->recurrences, (GHRFunc) remove_object_instance_cb, &rrdata);
+
+			/* add the modified object to the beginning of the list,
+			 * so that it's always before any detached instance we
+			 * might have */
+			if (comp)
+				priv->comp = g_list_prepend (priv->comp, comp);
+
+			if (obj_data->full_object) {
+				*new_components = g_slist_prepend (*new_components, e_cal_component_clone (obj_data->full_object));
+			} else {
+				*new_components = g_slist_prepend (*new_components, NULL);
+			}
+			break;
 		}
-		break;
 	}
 
 	save (cbfile, TRUE);
 
 	g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
+
+	*old_components = g_slist_reverse (*old_components);
+	*new_components = g_slist_reverse (*new_components);
 }
 
 static gboolean
@@ -3443,9 +3528,9 @@ e_cal_backend_file_class_init (ECalBackendFileClass *class)
 	sync_class->get_backend_property_sync	= e_cal_backend_file_get_backend_property;
 	sync_class->open_sync			= e_cal_backend_file_open;
 	sync_class->remove_sync			= e_cal_backend_file_remove;
-	sync_class->create_object_sync		= e_cal_backend_file_create_object;
-	sync_class->modify_object_sync		= e_cal_backend_file_modify_object;
-	sync_class->remove_object_sync		= e_cal_backend_file_remove_object;
+	sync_class->create_objects_sync		= e_cal_backend_file_create_objects;
+	sync_class->modify_objects_sync		= e_cal_backend_file_modify_objects;
+	sync_class->remove_objects_sync		= e_cal_backend_file_remove_objects;
 	sync_class->receive_objects_sync	= e_cal_backend_file_receive_objects;
 	sync_class->send_objects_sync		= e_cal_backend_file_send_objects;
 	sync_class->get_object_sync		= e_cal_backend_file_get_object;
diff --git a/calendar/backends/http/e-cal-backend-http.c b/calendar/backends/http/e-cal-backend-http.c
index d120ce4..66a7e15 100644
--- a/calendar/backends/http/e-cal-backend-http.c
+++ b/calendar/backends/http/e-cal-backend-http.c
@@ -1261,43 +1261,42 @@ e_cal_backend_http_get_free_busy (ECalBackendSync *backend,
 }
 
 static void
-e_cal_backend_http_create_object (ECalBackendSync *backend,
-                                  EDataCal *cal,
-                                  GCancellable *cancellable,
-                                  const gchar *calobj,
-                                  gchar **uid,
-                                  ECalComponent **new_component,
-                                  GError **perror)
+e_cal_backend_http_create_objects (ECalBackendSync *backend,
+                                   EDataCal *cal,
+                                   GCancellable *cancellable,
+                                   const GSList *calobjs,
+                                   GSList **uids,
+                                   GSList **new_components,
+                                   GError **perror)
 {
 	g_propagate_error (perror, EDC_ERROR (PermissionDenied));
 }
 
 static void
-e_cal_backend_http_modify_object (ECalBackendSync *backend,
-                                  EDataCal *cal,
-                                  GCancellable *cancellable,
-                                  const gchar *calobj,
-                                  CalObjModType mod,
-                                  ECalComponent **old_component,
-                                  ECalComponent **new_component,
-                                  GError **perror)
+e_cal_backend_http_modify_objects (ECalBackendSync *backend,
+                                   EDataCal *cal,
+                                   GCancellable *cancellable,
+                                   const GSList *calobjs,
+                                   CalObjModType mod,
+                                   GSList **old_components,
+                                   GSList **new_components,
+                                   GError **perror)
 {
 	g_propagate_error (perror, EDC_ERROR (PermissionDenied));
 }
 
-/* Remove_object handler for the file backend */
+/* Remove_objects handler for the file backend */
 static void
-e_cal_backend_http_remove_object (ECalBackendSync *backend,
-                                  EDataCal *cal,
-                                  GCancellable *cancellable,
-                                  const gchar *uid,
-                                  const gchar *rid,
-                                  CalObjModType mod,
-                                  ECalComponent **old_component,
-                                  ECalComponent **new_component,
-                                  GError **perror)
+e_cal_backend_http_remove_objects (ECalBackendSync *backend,
+                                   EDataCal *cal,
+                                   GCancellable *cancellable,
+                                   const GSList *ids,
+                                   CalObjModType mod,
+                                   GSList **old_components,
+                                   GSList **new_components,
+                                   GError **perror)
 {
-	*old_component = *new_component = NULL;
+	*old_components = *new_components = NULL;
 
 	g_propagate_error (perror, EDC_ERROR (PermissionDenied));
 }
@@ -1389,9 +1388,9 @@ e_cal_backend_http_class_init (ECalBackendHttpClass *class)
 	sync_class->authenticate_user_sync	= e_cal_backend_http_authenticate_user;
 	sync_class->refresh_sync		= e_cal_backend_http_refresh;
 	sync_class->remove_sync			= e_cal_backend_http_remove;
-	sync_class->create_object_sync		= e_cal_backend_http_create_object;
-	sync_class->modify_object_sync		= e_cal_backend_http_modify_object;
-	sync_class->remove_object_sync		= e_cal_backend_http_remove_object;
+	sync_class->create_objects_sync		= e_cal_backend_http_create_objects;
+	sync_class->modify_objects_sync		= e_cal_backend_http_modify_objects;
+	sync_class->remove_objects_sync		= e_cal_backend_http_remove_objects;
 	sync_class->receive_objects_sync	= e_cal_backend_http_receive_objects;
 	sync_class->send_objects_sync		= e_cal_backend_http_send_objects;
 	sync_class->get_object_sync		= e_cal_backend_http_get_object;
diff --git a/calendar/libecal/e-cal-client.c b/calendar/libecal/e-cal-client.c
index 7f37f4e..6685e07 100644
--- a/calendar/libecal/e-cal-client.c
+++ b/calendar/libecal/e-cal-client.c
@@ -562,6 +562,43 @@ convert_type (ECalClientSourceType type)
 	return AnyType;
 }
 
+/*
+ * Converts a GSList of icalcomponents into a NULL-terminated array of
+ * valid UTF-8 strings, suitable for sending over DBus.
+ */
+static gchar **
+icalcomponent_slist_to_utf8_icomp_array (GSList *icalcomponents)
+{
+	gchar **array;
+	const GSList *l;
+	gint i = 0;
+
+	array = g_new0 (gchar *, g_slist_length (icalcomponents) + 1);
+	for (l = icalcomponents; l != NULL; l = l->next) {
+		gchar *comp_str = icalcomponent_as_ical_string_r ((icalcomponent *) l->data);
+		array[i++] = e_util_utf8_make_valid (comp_str);
+		g_free (comp_str);
+	}
+
+	return array;
+}
+
+/*
+ * Converts a GSList of icalcomponents into a GSList of strings.
+ */
+static GSList *
+icalcomponent_slist_to_string_slist (GSList *icalcomponents)
+{
+	GSList *strings = NULL;
+	const GSList *l;
+
+	for (l = icalcomponents; l != NULL; l = l->next) {
+		strings = g_slist_prepend (strings, icalcomponent_as_ical_string_r ((icalcomponent *) l->data));
+	}
+
+	return g_slist_reverse (strings);
+}
+
 /**
  * e_cal_client_new:
  * @source: An #ESource pointer
@@ -2693,6 +2730,29 @@ complete_string_exchange (gboolean res,
 }
 
 static gboolean
+complete_strv_exchange (gboolean res,
+                        gchar **out_strings,
+                        GSList **result,
+                        GError **error)
+{
+	g_return_val_if_fail (result != NULL, FALSE);
+
+	if (res && out_strings) {
+		*result = e_client_util_strv_to_slist ((const gchar * const*) out_strings);
+	} else {
+		*result = NULL;
+		res = FALSE;
+
+		if (error && !*error)
+			g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_INVALID_ARG, NULL));
+	}
+
+	g_strfreev (out_strings);
+
+	return res;
+}
+
+static gboolean
 cal_client_get_default_object_from_cache_finish (EClient *client,
                                                  GAsyncResult *result,
                                                  gchar **prop_value,
@@ -3644,14 +3704,19 @@ e_cal_client_create_object (ECalClient *client,
                             gpointer user_data)
 {
 	gchar *comp_str, *gdbus_comp = NULL;
+	const gchar *strv[2];
 
 	g_return_if_fail (icalcomp != NULL);
 
 	comp_str = icalcomponent_as_ical_string_r (icalcomp);
+	strv[0] = e_util_ensure_gdbus_string (comp_str, &gdbus_comp);
+	strv[1] = NULL;
 
-	e_client_proxy_call_string (E_CLIENT (client), e_util_ensure_gdbus_string (comp_str, &gdbus_comp), cancellable, callback, user_data, e_cal_client_create_object,
-			e_gdbus_cal_call_create_object,
-			NULL, NULL, e_gdbus_cal_call_create_object_finish, NULL, NULL);
+	g_return_if_fail (strv[0] != NULL);
+
+	e_client_proxy_call_strv (E_CLIENT (client), strv, cancellable, callback, user_data, e_cal_client_create_object,
+			e_gdbus_cal_call_create_objects,
+			NULL, NULL, NULL, e_gdbus_cal_call_create_objects_finish, NULL);
 
 	g_free (comp_str);
 	g_free (gdbus_comp);
@@ -3679,11 +3744,17 @@ e_cal_client_create_object_finish (ECalClient *client,
                                    GError **error)
 {
 	gboolean res;
+	gchar **out_strings = NULL;
 	gchar *out_string = NULL;
 
 	g_return_val_if_fail (uid != NULL, FALSE);
 
-	res = e_client_proxy_call_finish_string (E_CLIENT (client), result, &out_string, error, e_cal_client_create_object);
+	res = e_client_proxy_call_finish_strv (E_CLIENT (client), result, &out_strings, error, e_cal_client_create_object);
+
+	if (res && out_strings) {
+		out_string = g_strdup (out_strings[0]);
+		g_strfreev (out_strings);
+	}
 
 	return complete_string_exchange (res, out_string, uid, error);
 }
@@ -3715,6 +3786,8 @@ e_cal_client_create_object_sync (ECalClient *client,
 {
 	gboolean res;
 	gchar *comp_str, *gdbus_comp = NULL;
+	const gchar *strv[2];
+	gchar **out_strings = NULL;
 	gchar *out_string = NULL;
 
 	g_return_val_if_fail (client != NULL, FALSE);
@@ -3729,16 +3802,144 @@ e_cal_client_create_object_sync (ECalClient *client,
 	}
 
 	comp_str = icalcomponent_as_ical_string_r (icalcomp);
+	strv[0] = e_util_ensure_gdbus_string (comp_str, &gdbus_comp);
+	strv[1] = NULL;
+
+	g_return_val_if_fail (strv[0] != NULL, FALSE);
 
-	res = e_client_proxy_call_sync_string__string (E_CLIENT (client), e_util_ensure_gdbus_string (comp_str, &gdbus_comp), &out_string, cancellable, error, e_gdbus_cal_call_create_object_sync);
+	res = e_client_proxy_call_sync_strv__strv (E_CLIENT (client), strv, &out_strings, cancellable, error, e_gdbus_cal_call_create_objects_sync);
 
 	g_free (comp_str);
 	g_free (gdbus_comp);
 
+	if (res && out_strings) {
+		out_string = g_strdup (out_strings[0]);
+		g_strfreev (out_strings);
+	}
+
 	return complete_string_exchange (res, out_string, uid, error);
 }
 
 /**
+ * e_cal_client_create_objects:
+ * @client: an #ECalClient
+ * @icalcomps: The components to create
+ * @cancellable: a #GCancellable; can be %NULL
+ * @callback: callback to call when a result is ready
+ * @user_data: user data for the @callback
+ *
+ * Requests the calendar backend to create the objects specified by the @icalcomps
+ * argument. Some backends would assign a specific UID to the newly created object,
+ * but this function does not modify the original @icalcomps if their UID changes.
+ * The call is finished by e_cal_client_create_objects_finish() from
+ * the @callback.
+ *
+ * Since: 3.6
+ **/
+void
+e_cal_client_create_objects (ECalClient *client,
+                             GSList *icalcomps,
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
+{
+	gchar **array;
+
+	g_return_if_fail (client != NULL);
+	g_return_if_fail (E_IS_CAL_CLIENT (client));
+	g_return_if_fail (client->priv != NULL);
+	g_return_if_fail (icalcomps != NULL);
+
+	array = icalcomponent_slist_to_utf8_icomp_array (icalcomps);
+
+	e_client_proxy_call_strv (E_CLIENT (client), (const gchar * const *) array, cancellable, callback, user_data, e_cal_client_create_objects,
+			e_gdbus_cal_call_create_objects,
+			NULL, NULL, NULL, e_gdbus_cal_call_create_objects_finish, NULL);
+
+	g_strfreev (array);
+}
+
+/**
+ * e_cal_client_create_objects_finish:
+ * @client: an #ECalClient
+ * @result: a #GAsyncResult
+ * @uids: (out): Return value for the UIDs assigned to the new components by the calendar backend
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Finishes previous call of e_cal_client_create_objects() and
+ * sets @uids to newly assigned UIDs for the created objects.
+ * This @uids should be freed with e_client_util_free_string_slist().
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_cal_client_create_objects_finish (ECalClient *client,
+                                    GAsyncResult *result,
+                                    GSList **uids,
+                                    GError **error)
+{
+	gboolean res;
+	gchar **out_strings = NULL;
+
+	g_return_val_if_fail (client != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
+	g_return_val_if_fail (client->priv != NULL, FALSE);
+	g_return_val_if_fail (uids != NULL, FALSE);
+
+	res = e_client_proxy_call_finish_strv (E_CLIENT (client), result, &out_strings, error, e_cal_client_create_objects);
+
+	return complete_strv_exchange (res, out_strings, uids, error);
+}
+
+/**
+ * e_cal_client_create_objects_sync:
+ * @client: an #ECalClient
+ * @icalcomps: The components to create
+ * @uids: (out): Return value for the UIDs assigned to the new components by the calendar backend
+ * @cancellable: a #GCancellable; can be %NULL
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Requests the calendar backend to create the objects specified by the @icalcomps
+ * argument. Some backends would assign a specific UID to the newly created objects,
+ * in those cases these UIDs would be returned in the @uids argument. This function
+ * does not modify the original @icalcomps if their UID changes.
+ * Returned @uid should be freed with e_client_util_free_string_slist().
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_cal_client_create_objects_sync (ECalClient *client,
+                                  GSList *icalcomps,
+                                  GSList **uids,
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+	gboolean res;
+	gchar **array, **out_strings = NULL;
+
+	g_return_val_if_fail (client != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
+	g_return_val_if_fail (client->priv != NULL, FALSE);
+	g_return_val_if_fail (icalcomps != NULL, FALSE);
+	g_return_val_if_fail (uids != NULL, FALSE);
+
+	if (!client->priv->gdbus_cal) {
+		set_proxy_gone_error (error);
+		return FALSE;
+	}
+
+	array = icalcomponent_slist_to_utf8_icomp_array (icalcomps);
+
+	res = e_client_proxy_call_sync_strv__strv (E_CLIENT (client), (const gchar * const *) array, &out_strings, cancellable, error, e_gdbus_cal_call_create_objects_sync);
+
+	return complete_strv_exchange (res, out_strings, uids, error);
+}
+
+/**
  * e_cal_client_modify_object:
  * @client: an #ECalClient
  * @icalcomp: Component to modify
@@ -3769,15 +3970,17 @@ e_cal_client_modify_object (ECalClient *client,
                             gpointer user_data)
 {
 	gchar *comp_str, **strv;
+	GSList comp_strings = {0,};
 
 	g_return_if_fail (icalcomp != NULL);
 
 	comp_str = icalcomponent_as_ical_string_r (icalcomp);
-	strv = e_gdbus_cal_encode_modify_object (comp_str, mod);
+	comp_strings.data = comp_str;
+	strv = e_gdbus_cal_encode_modify_objects (&comp_strings, mod);
 
 	e_client_proxy_call_strv (E_CLIENT (client), (const gchar * const *) strv, cancellable, callback, user_data, e_cal_client_modify_object,
-			e_gdbus_cal_call_modify_object,
-			e_gdbus_cal_call_modify_object_finish, NULL, NULL, NULL, NULL);
+			e_gdbus_cal_call_modify_objects,
+			e_gdbus_cal_call_modify_objects_finish, NULL, NULL, NULL, NULL);
 
 	g_strfreev (strv);
 	g_free (comp_str);
@@ -3832,6 +4035,7 @@ e_cal_client_modify_object_sync (ECalClient *client,
 {
 	gboolean res;
 	gchar *comp_str, **strv;
+	GSList comp_strings = {0,};
 
 	g_return_val_if_fail (client != NULL, FALSE);
 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
@@ -3844,9 +4048,10 @@ e_cal_client_modify_object_sync (ECalClient *client,
 	}
 
 	comp_str = icalcomponent_as_ical_string_r (icalcomp);
-	strv = e_gdbus_cal_encode_modify_object (comp_str, mod);
+	comp_strings.data = comp_str;
+	strv = e_gdbus_cal_encode_modify_objects (&comp_strings, mod);
 
-	res = e_client_proxy_call_sync_strv__void (E_CLIENT (client), (const gchar * const *) strv, cancellable, error, e_gdbus_cal_call_modify_object_sync);
+	res = e_client_proxy_call_sync_strv__void (E_CLIENT (client), (const gchar * const *) strv, cancellable, error, e_gdbus_cal_call_modify_objects_sync);
 
 	g_strfreev (strv);
 	g_free (comp_str);
@@ -3855,6 +4060,131 @@ e_cal_client_modify_object_sync (ECalClient *client,
 }
 
 /**
+ * e_cal_client_modify_objects:
+ * @client: an #ECalClient
+ * @comps: Components to modify
+ * @mod: Type of modification
+ * @cancellable: a #GCancellable; can be %NULL
+ * @callback: callback to call when a result is ready
+ * @user_data: user data for the @callback
+ *
+ * Requests the calendar backend to modify existing objects. If an object
+ * does not exist on the calendar, an error will be returned.
+ *
+ * For recurrent appointments, the @mod argument specifies what to modify,
+ * if all instances (CALOBJ_MOD_ALL), a single instance (CALOBJ_MOD_THIS),
+ * or a specific set of instances (CALOBJ_MOD_THISNADPRIOR and
+ * CALOBJ_MOD_THISANDFUTURE).
+ *
+ * The call is finished by e_cal_client_modify_objects_finish() from
+ * the @callback.
+ *
+ * Since: 3.6
+ **/
+void
+e_cal_client_modify_objects (ECalClient *client,
+                             /* const */ GSList *comps,
+                             CalObjModType mod,
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
+{
+	GSList *comp_strings;
+	gchar **strv;
+
+	g_return_if_fail (client != NULL);
+	g_return_if_fail (E_IS_CAL_CLIENT (client));
+	g_return_if_fail (client->priv != NULL);
+	g_return_if_fail (comps != NULL);
+
+	comp_strings = icalcomponent_slist_to_string_slist(comps);
+	strv = e_gdbus_cal_encode_modify_objects (comp_strings, mod);
+
+	e_client_proxy_call_strv (E_CLIENT (client), (const gchar * const *) strv, cancellable, callback, user_data, e_cal_client_modify_objects,
+			e_gdbus_cal_call_modify_objects,
+			e_gdbus_cal_call_modify_objects_finish, NULL, NULL, NULL, NULL);
+
+	g_strfreev (strv);
+	e_client_util_free_string_slist (comp_strings);
+}
+
+/**
+ * e_cal_client_modify_objects_finish:
+ * @client: an #ECalClient
+ * @result: a #GAsyncResult
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Finishes previous call of e_cal_client_modify_objects().
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_cal_client_modify_objects_finish (ECalClient *client,
+                                    GAsyncResult *result,
+                                    GError **error)
+{
+	g_return_val_if_fail (client != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
+	g_return_val_if_fail (client->priv != NULL, FALSE);
+
+	return e_client_proxy_call_finish_void (E_CLIENT (client), result, error, e_cal_client_modify_objects);
+}
+
+/**
+ * e_cal_client_modify_objects_sync:
+ * @client: an #ECalClient
+ * @comps: Components to modify
+ * @mod: Type of modification
+ * @cancellable: a #GCancellable; can be %NULL
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Requests the calendar backend to modify existing objects. If an object
+ * does not exist on the calendar, an error will be returned.
+ *
+ * For recurrent appointments, the @mod argument specifies what to modify,
+ * if all instances (CALOBJ_MOD_ALL), a single instance (CALOBJ_MOD_THIS),
+ * or a specific set of instances (CALOBJ_MOD_THISNADPRIOR and
+ * CALOBJ_MOD_THISANDFUTURE).
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_cal_client_modify_objects_sync (ECalClient *client,
+                                  /* const */ GSList *comps,
+                                  CalObjModType mod,
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+	gboolean res;
+	gchar **strv;
+	GSList *comp_strings;
+
+	g_return_val_if_fail (client != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
+	g_return_val_if_fail (client->priv != NULL, FALSE);
+	g_return_val_if_fail (comps != NULL, FALSE);
+
+	if (!client->priv->gdbus_cal) {
+		set_proxy_gone_error (error);
+		return FALSE;
+	}
+
+	comp_strings = icalcomponent_slist_to_string_slist(comps);
+	strv = e_gdbus_cal_encode_modify_objects (comp_strings, mod);
+
+	res = e_client_proxy_call_sync_strv__void (E_CLIENT (client), (const gchar * const *) strv, cancellable, error, e_gdbus_cal_call_modify_objects_sync);
+
+	g_strfreev (strv);
+	e_client_util_free_string_slist (comp_strings);
+
+	return res;
+}
+
+/**
  * e_cal_client_remove_object:
  * @client: an #ECalClient
  * @uid: UID of the object to remove
@@ -3885,14 +4215,19 @@ e_cal_client_remove_object (ECalClient *client,
                             gpointer user_data)
 {
 	gchar **strv;
+	GSList ids = {0,};
+	ECalComponentId id;
 
 	g_return_if_fail (uid != NULL);
 
-	strv = e_gdbus_cal_encode_remove_object (uid, rid, mod);
+	id.uid = (gchar *)uid;
+	id.rid = (gchar *)rid;
+	ids.data = &id;
+	strv = e_gdbus_cal_encode_remove_objects (&ids, mod);
 
 	e_client_proxy_call_strv (E_CLIENT (client), (const gchar * const *) strv, cancellable, callback, user_data, e_cal_client_remove_object,
-			e_gdbus_cal_call_remove_object,
-			e_gdbus_cal_call_remove_object_finish, NULL, NULL, NULL, NULL);
+			e_gdbus_cal_call_remove_objects,
+			e_gdbus_cal_call_remove_objects_finish, NULL, NULL, NULL, NULL);
 
 	g_strfreev (strv);
 }
@@ -3946,6 +4281,8 @@ e_cal_client_remove_object_sync (ECalClient *client,
 {
 	gboolean res;
 	gchar **strv;
+	GSList ids = {0,};
+	ECalComponentId id;
 
 	g_return_val_if_fail (client != NULL, FALSE);
 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
@@ -3957,9 +4294,118 @@ e_cal_client_remove_object_sync (ECalClient *client,
 		return FALSE;
 	}
 
-	strv = e_gdbus_cal_encode_remove_object (uid, rid, mod);
+	id.uid = (gchar *)uid;
+	id.rid = (gchar *)rid;
+	ids.data = &id;
+	strv = e_gdbus_cal_encode_remove_objects (&ids, mod);
+
+	res = e_client_proxy_call_sync_strv__void (E_CLIENT (client), (const gchar * const *) strv, cancellable, error, e_gdbus_cal_call_remove_objects_sync);
+
+	g_strfreev (strv);
+
+	return res;
+}
+
+/**
+ * e_cal_client_remove_objects:
+ * @client: an #ECalClient
+ * @ids: A list of #ECalComponentId objects identifying the objects to remove
+ * @mod: Type of the removal
+ * @cancellable: a #GCancellable; can be %NULL
+ * @callback: callback to call when a result is ready
+ * @user_data: user data for the @callback
+ *
+ * This function allows the removal of instances of recurrent
+ * appointments. #ECalComponentId objects can identify specific instances (if rid is not NULL).
+ * If what you want is to remove all instances, use a #NULL rid in the #ECalComponentId and CALOBJ_MOD_ALL
+ * for the @mod.
+ *
+ * The call is finished by e_cal_client_remove_objects_finish() from
+ * the @callback.
+ *
+ * Since: 3.6
+ **/
+void
+e_cal_client_remove_objects (ECalClient *client,
+                             const GSList *ids,
+                             CalObjModType mod,
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
+{
+	gchar **strv;
+
+	g_return_if_fail (ids != NULL);
+
+	strv = e_gdbus_cal_encode_remove_objects (ids, mod);
+
+	e_client_proxy_call_strv (E_CLIENT (client), (const gchar * const *) strv, cancellable, callback, user_data, e_cal_client_remove_objects,
+			e_gdbus_cal_call_remove_objects,
+			e_gdbus_cal_call_remove_objects_finish, NULL, NULL, NULL, NULL);
+
+	g_strfreev (strv);
+}
+
+/**
+ * e_cal_client_remove_objects_finish:
+ * @client: an #ECalClient
+ * @result: a #GAsyncResult
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Finishes previous call of e_cal_client_remove_objects().
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_cal_client_remove_objects_finish (ECalClient *client,
+                                    GAsyncResult *result,
+                                    GError **error)
+{
+	return e_client_proxy_call_finish_void (E_CLIENT (client), result, error, e_cal_client_remove_objects);
+}
+
+/**
+ * e_cal_client_remove_objects_sync:
+ * @client: an #ECalClient
+ * @ids: A list of #ECalComponentId objects identifying the objects to remove
+ * @mod: Type of the removal
+ * @cancellable: a #GCancellable; can be %NULL
+ * @error: (out): a #GError to set an error, if any
+ *
+ * This function allows the removal of instances of recurrent
+ * appointments. #ECalComponentId objects can identify specific instances (if rid is not NULL).
+ * If what you want is to remove all instances, use a #NULL rid in the #ECalComponentId and CALOBJ_MOD_ALL
+ * for the @mod.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_cal_client_remove_objects_sync (ECalClient *client,
+                                  const GSList *ids,
+                                  CalObjModType mod,
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+	gboolean res;
+	gchar **strv;
+
+	g_return_val_if_fail (client != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
+	g_return_val_if_fail (client->priv != NULL, FALSE);
+	g_return_val_if_fail (ids != NULL, FALSE);
+
+	if (!client->priv->gdbus_cal) {
+		set_proxy_gone_error (error);
+		return FALSE;
+	}
+
+	strv = e_gdbus_cal_encode_remove_objects (ids, mod);
 
-	res = e_client_proxy_call_sync_strv__void (E_CLIENT (client), (const gchar * const *) strv, cancellable, error, e_gdbus_cal_call_remove_object_sync);
+	res = e_client_proxy_call_sync_strv__void (E_CLIENT (client), (const gchar * const *) strv, cancellable, error, e_gdbus_cal_call_remove_objects_sync);
 
 	g_strfreev (strv);
 
diff --git a/calendar/libecal/e-cal-client.h b/calendar/libecal/e-cal-client.h
index 3e77baf..1733511 100644
--- a/calendar/libecal/e-cal-client.h
+++ b/calendar/libecal/e-cal-client.h
@@ -206,14 +206,26 @@ void		e_cal_client_create_object			(ECalClient *client, /* const */ icalcomponen
 gboolean	e_cal_client_create_object_finish		(ECalClient *client, GAsyncResult *result, gchar **uid, GError **error);
 gboolean	e_cal_client_create_object_sync			(ECalClient *client, /* const */ icalcomponent *icalcomp, gchar **uid, GCancellable *cancellable, GError **error);
 
+void		e_cal_client_create_objects			(ECalClient *client, GSList *icalcomps, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+gboolean	e_cal_client_create_objects_finish	(ECalClient *client, GAsyncResult *result, GSList **uids, GError **error);
+gboolean	e_cal_client_create_objects_sync	(ECalClient *client, GSList *icalcomps, GSList **uids, GCancellable *cancellable, GError **error);
+
 void		e_cal_client_modify_object			(ECalClient *client, /* const */ icalcomponent *icalcomp, CalObjModType mod, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 gboolean	e_cal_client_modify_object_finish		(ECalClient *client, GAsyncResult *result, GError **error);
 gboolean	e_cal_client_modify_object_sync			(ECalClient *client, /* const */ icalcomponent *icalcomp, CalObjModType mod, GCancellable *cancellable, GError **error);
 
+void		e_cal_client_modify_objects			(ECalClient *client, /* const */ GSList *icalcomps, CalObjModType mod, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+gboolean	e_cal_client_modify_objects_finish	(ECalClient *client, GAsyncResult *result, GError **error);
+gboolean	e_cal_client_modify_objects_sync	(ECalClient *client, /* const */ GSList *icalcomps, CalObjModType mod, GCancellable *cancellable, GError **error);
+
 void		e_cal_client_remove_object			(ECalClient *client, const gchar *uid, const gchar *rid, CalObjModType mod, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 gboolean	e_cal_client_remove_object_finish		(ECalClient *client, GAsyncResult *result, GError **error);
 gboolean	e_cal_client_remove_object_sync			(ECalClient *client, const gchar *uid, const gchar *rid, CalObjModType mod, GCancellable *cancellable, GError **error);
 
+void		e_cal_client_remove_objects			(ECalClient *client, const GSList *ids, CalObjModType mod, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+gboolean	e_cal_client_remove_objects_finish	(ECalClient *client, GAsyncResult *result, GError **error);
+gboolean	e_cal_client_remove_objects_sync	(ECalClient *client, const GSList *ids, CalObjModType mod, GCancellable *cancellable, GError **error);
+
 void		e_cal_client_receive_objects			(ECalClient *client, /* const */ icalcomponent *icalcomp, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 gboolean	e_cal_client_receive_objects_finish		(ECalClient *client, GAsyncResult *result, GError **error);
 gboolean	e_cal_client_receive_objects_sync		(ECalClient *client, /* const */ icalcomponent *icalcomp, GCancellable *cancellable, GError **error);
diff --git a/calendar/libecal/e-cal-util.h b/calendar/libecal/e-cal-util.h
index fe6a48d..966aeb5 100644
--- a/calendar/libecal/e-cal-util.h
+++ b/calendar/libecal/e-cal-util.h
@@ -113,6 +113,33 @@ gboolean e_cal_util_event_dates_match (icalcomponent *icalcomp1, icalcomponent *
 #define CAL_STATIC_CAPABILITY_NO_TRANSPARENCY             "no-transparency"
 
 /**
+ * CAL_STATIC_CAPABILITY_BULK_ADDS:
+ *
+ * Flag indicating that the backend supports bulk additions.
+ *
+ * Since: 3.6
+ */
+#define CAL_STATIC_CAPABILITY_BULK_ADDS                   "bulk-adds"
+
+/**
+ * CAL_STATIC_CAPABILITY_BULK_MODIFIES:
+ *
+ * Flag indicating that the backend supports bulk modifications.
+ *
+ * Since: 3.6
+ */
+#define CAL_STATIC_CAPABILITY_BULK_MODIFIES               "bulk-modifies"
+
+/**
+ * CAL_STATIC_CAPABILITY_BULK_REMOVES:
+ *
+ * Flag indicating that the backend supports bulk removals.
+ *
+ * Since: 3.6
+ */
+#define CAL_STATIC_CAPABILITY_BULK_REMOVES                "bulk-removes"
+
+/**
  * CAL_STATIC_CAPABILITY_REMOVE_ONLY_THIS:
  *
  * FIXME: Document me.
diff --git a/calendar/libecal/e-cal.c b/calendar/libecal/e-cal.c
index c2ab5c9..2a9bca1 100644
--- a/calendar/libecal/e-cal.c
+++ b/calendar/libecal/e-cal.c
@@ -3917,7 +3917,9 @@ e_cal_create_object (ECal *ecal,
                      GError **error)
 {
 	ECalPrivate *priv;
-	gchar *obj, *muid = NULL, *gdbus_obj = NULL;
+	gchar *obj, *gdbus_obj = NULL;
+	const gchar *strv[2];
+	gchar **muids = NULL;
 
 	e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
 	e_return_error_if_fail (icalcomp != NULL, E_CALENDAR_STATUS_INVALID_ARG);
@@ -3930,8 +3932,10 @@ e_cal_create_object (ECal *ecal,
 	}
 
 	obj = icalcomponent_as_ical_string_r (icalcomp);
-	if (!e_gdbus_cal_call_create_object_sync (priv->gdbus_cal, e_util_ensure_gdbus_string (obj, &gdbus_obj), &muid, NULL, error)) {
-		g_free (muid);
+	strv[0] = e_util_ensure_gdbus_string (obj, &gdbus_obj);
+	strv[1] = NULL;
+
+	if (!e_gdbus_cal_call_create_objects_sync (priv->gdbus_cal, strv, &muids, NULL, error)) {
 		g_free (obj);
 		g_free (gdbus_obj);
 
@@ -3941,15 +3945,15 @@ e_cal_create_object (ECal *ecal,
 	g_free (obj);
 	g_free (gdbus_obj);
 
-	if (!muid) {
+	if (!muids) {
 		E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OTHER_ERROR, error);
 	} else {
-		icalcomponent_set_uid (icalcomp, muid);
+		icalcomponent_set_uid (icalcomp, muids[0]);
 
 		if (uid)
-			*uid = muid;
-		else
-			g_free (muid);
+			*uid = g_strdup (muids[0]);
+
+		g_strfreev (muids);
 
 		E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
 	}
@@ -3982,6 +3986,7 @@ e_cal_modify_object (ECal *ecal,
 {
 	ECalPrivate *priv;
 	gchar *obj, **strv;
+	GSList objs = {0,};
 
 	e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
 	e_return_error_if_fail (icalcomp, E_CALENDAR_STATUS_INVALID_ARG);
@@ -4003,8 +4008,9 @@ e_cal_modify_object (ECal *ecal,
 	}
 
 	obj = icalcomponent_as_ical_string_r (icalcomp);
-	strv = e_gdbus_cal_encode_modify_object (obj, mod);
-	if (!e_gdbus_cal_call_modify_object_sync (priv->gdbus_cal, (const gchar * const *) strv, NULL, error)) {
+	objs.data = obj;
+	strv = e_gdbus_cal_encode_modify_objects (&objs, mod);
+	if (!e_gdbus_cal_call_modify_objects_sync (priv->gdbus_cal, (const gchar * const *) strv, NULL, error)) {
 		g_free (obj);
 		g_strfreev (strv);
 
@@ -4073,6 +4079,8 @@ e_cal_remove_object_with_mod (ECal *ecal,
 {
 	ECalPrivate *priv;
 	gchar **strv;
+	GSList ids = {0,};
+	ECalComponentId id;
 
 	e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
 	e_return_error_if_fail (uid, E_CALENDAR_STATUS_INVALID_ARG);
@@ -4093,8 +4101,11 @@ e_cal_remove_object_with_mod (ECal *ecal,
 		E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
 	}
 
-	strv = e_gdbus_cal_encode_remove_object (uid, rid, mod);
-	if (!e_gdbus_cal_call_remove_object_sync (priv->gdbus_cal, (const gchar * const *) strv, NULL, error)) {
+	id.uid = (gchar *)uid;
+	id.rid = (gchar *)rid;
+	ids.data = &id;
+	strv = e_gdbus_cal_encode_remove_objects (&ids, mod);
+	if (!e_gdbus_cal_call_remove_objects_sync (priv->gdbus_cal, (const gchar * const *) strv, NULL, error)) {
 		g_strfreev (strv);
 
 		E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
diff --git a/calendar/libedata-cal/e-cal-backend-sync.c b/calendar/libedata-cal/e-cal-backend-sync.c
index 34ef6a2..f6226e5 100644
--- a/calendar/libedata-cal/e-cal-backend-sync.c
+++ b/calendar/libedata-cal/e-cal-backend-sync.c
@@ -11,6 +11,7 @@
 #endif
 
 #include "e-cal-backend-sync.h"
+#include "libedataserver/e-data-server-util.h"
 #include <libical/icaltz-util.h>
 
 #define E_CAL_BACKEND_SYNC_GET_PRIVATE(obj) \
@@ -307,96 +308,93 @@ e_cal_backend_sync_get_free_busy (ECalBackendSync *backend,
 }
 
 /**
- * e_cal_backend_sync_create_object:
+ * e_cal_backend_sync_create_objects:
  * @backend: An ECalBackendSync object.
  * @cal: An EDataCal object.
  * @cancellable: a #GCancellable for the operation
- * @calobj: The object to be added.
- * @uid: Placeholder for server-generated UID.
- * @new_component: (out) (transfer full): Placeholder for returned #ECalComponent.
+ * @calobjs: The objects to be added.
+ * @uids: Placeholder for server-generated UIDs.
+ * @new_components: (out) (transfer full): Placeholder for returned #ECalComponent objects.
  * @error: Out parameter for a #GError.
  *
- * Calls the create_object_sync method on the given backend.
+ * Calls the create_objects_sync method on the given backend.
  */
 void
-e_cal_backend_sync_create_object (ECalBackendSync *backend,
-                                  EDataCal *cal,
-                                  GCancellable *cancellable,
-                                  const gchar *calobj,
-                                  gchar **uid,
-                                  ECalComponent **new_component,
-                                  GError **error)
+e_cal_backend_sync_create_objects (ECalBackendSync *backend,
+                                   EDataCal *cal,
+                                   GCancellable *cancellable,
+                                   const GSList *calobjs,
+                                   GSList **uids,
+                                   GSList **new_components,
+                                   GError **error)
 {
 	e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
-	e_return_data_cal_error_if_fail (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->create_object_sync != NULL, UnsupportedMethod);
+	e_return_data_cal_error_if_fail (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->create_objects_sync != NULL, UnsupportedMethod);
 
-	LOCK_WRAPPER (create_object_sync, (backend, cal, cancellable, calobj, uid, new_component, error));
+	LOCK_WRAPPER (create_objects_sync, (backend, cal, cancellable, calobjs, uids, new_components, error));
 }
 
 /**
- * e_cal_backend_sync_modify_object:
+ * e_cal_backend_sync_modify_objects:
  * @backend: An ECalBackendSync object.
  * @cal: An EDataCal object.
  * @cancellable: a #GCancellable for the operation
- * @calobj: Object to be modified.
+ * @calobjs: Objects to be modified.
  * @mod: Type of modification to be done.
- * @old_component: (out) (transfer full): Placeholder for returning the old component as it was stored on the
+ * @old_components: (out) (transfer full): Placeholder for returning the old components as they were stored on the
  * backend.
- * @new_component: (out) (transfer full): Placeholder for returning the new component as it has been stored
+ * @new_components: (out) (transfer full): Placeholder for returning the new components as they have been stored
  * on the backend.
  * @error: Out parameter for a #GError.
  *
- * Calls the modify_object_sync method on the given backend.
+ * Calls the modify_objects_sync method on the given backend.
  */
 void
-e_cal_backend_sync_modify_object (ECalBackendSync *backend,
-                                  EDataCal *cal,
-                                  GCancellable *cancellable,
-                                  const gchar *calobj,
-                                  CalObjModType mod,
-                                  ECalComponent **old_component,
-                                  ECalComponent **new_component,
-                                  GError **error)
+e_cal_backend_sync_modify_objects (ECalBackendSync *backend,
+								   EDataCal *cal,
+								   GCancellable *cancellable,
+								   const GSList *calobjs,
+								   CalObjModType mod,
+								   GSList **old_components,
+								   GSList **new_components,
+								   GError **error)
 {
 	e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
-	e_return_data_cal_error_if_fail (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->modify_object_sync != NULL, UnsupportedMethod);
+	e_return_data_cal_error_if_fail (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->modify_objects_sync != NULL, UnsupportedMethod);
 
-	LOCK_WRAPPER (modify_object_sync, (backend, cal, cancellable, calobj, mod, old_component, new_component, error));
+	LOCK_WRAPPER (modify_objects_sync, (backend, cal, cancellable, calobjs, mod, old_components, new_components, error));
 }
 
 /**
- * e_cal_backend_sync_remove_object:
+ * e_cal_backend_sync_remove_objects:
  * @backend: An ECalBackendSync object.
  * @cal: An EDataCal object.
  * @cancellable: a #GCancellable for the operation
- * @uid: UID of the object to remove.
- * @rid: Recurrence ID of the instance to remove, or NULL if removing the
- * whole object.
+ * @ids: List of #ECalComponentId objects identifying the objects to remove.
  * @mod: Type of removal.
- * @old_component: (out) (transfer full): Placeholder for returning the old component as it was stored on the
+ * @old_components: (out) (transfer full): Placeholder for returning the old components as they were stored on the
  * backend.
- * @new_component: (out) (transfer full): Placeholder for returning the new component as it has been stored
- * on the backend (when removing individual instances). If removing the whole object,
- * this will be set to %NULL.
+ * @new_components: (out) (transfer full): Placeholder for returning the new components as they have been stored
+ * on the backend (when removing individual instances). If removing whole objects,
+ * they will be set to %NULL.
  * @error: Out parameter for a #GError.
  *
- * Calls the remove_object_sync method on the given backend.
+ * Calls the remove_objects_sync method on the given backend.
  */
 void
-e_cal_backend_sync_remove_object (ECalBackendSync *backend,
-                                  EDataCal *cal,
-                                  GCancellable *cancellable,
-                                  const gchar *uid,
-                                  const gchar *rid,
-                                  CalObjModType mod,
-                                  ECalComponent **old_component,
-                                  ECalComponent **new_component,
-                                  GError **error)
+e_cal_backend_sync_remove_objects (ECalBackendSync *backend,
+                                   EDataCal *cal,
+                                   GCancellable *cancellable,
+                                   const GSList *ids,
+                                   CalObjModType mod,
+                                   GSList **old_components,
+                                   GSList **new_components,
+                                   GError **error)
 {
 	e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
-	e_return_data_cal_error_if_fail (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->remove_object_sync != NULL, UnsupportedMethod);
+	e_return_data_cal_error_if_fail (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->remove_objects_sync != NULL, UnsupportedMethod);
 
-	LOCK_WRAPPER (remove_object_sync, (backend, cal, cancellable, uid, rid, mod, old_component, new_component, error));
+	LOCK_WRAPPER (remove_objects_sync, (backend, cal, cancellable, ids, mod, old_components, new_components, error));
 }
 
 /**
@@ -728,80 +726,81 @@ cal_backend_get_free_busy (ECalBackend *backend,
 	g_slist_free (freebusyobjs);
 }
 
+static GSList *
+ecalcomponent_slist_from_strings (const GSList *strings)
+{
+	GSList *ecalcomps = NULL;
+	const GSList *l;
+
+	for (l = strings; l; l = l->next) {
+		ECalComponent *component = e_cal_component_new_from_string (l->data);
+		ecalcomps = g_slist_prepend (ecalcomps, component);
+	}
+
+	return g_slist_reverse (ecalcomps);
+}
+
 static void
-cal_backend_create_object (ECalBackend *backend,
-                           EDataCal *cal,
-                           guint32 opid,
-                           GCancellable *cancellable,
-                           const gchar *calobj)
+cal_backend_create_objects (ECalBackend *backend,
+                            EDataCal *cal,
+                            guint32 opid,
+                            GCancellable *cancellable,
+                            const GSList *calobjs)
 {
 	GError *error = NULL;
-	gchar *uid = NULL;
-	ECalComponent *new_component = NULL;
+	GSList *uids = NULL;
+	GSList *new_components = NULL;
 
-	e_cal_backend_sync_create_object (E_CAL_BACKEND_SYNC (backend), cal, cancellable, calobj, &uid, &new_component, &error);
+	e_cal_backend_sync_create_objects (E_CAL_BACKEND_SYNC (backend), cal, cancellable, calobjs, &uids, &new_components, &error);
 
-	if (!new_component)
-		new_component = e_cal_component_new_from_string (calobj);
+	if (!new_components)
+		new_components = ecalcomponent_slist_from_strings (calobjs);
 
-	e_data_cal_respond_create_object (cal, opid, error, uid, new_component);
+	e_data_cal_respond_create_objects (cal, opid, error, uids, new_components);
 
-	g_free (uid);
-
-	if (new_component)
-		g_object_unref (new_component);
+	g_slist_free_full (uids, g_free);
+	e_util_free_nullable_object_slist (new_components);
 }
 
 static void
-cal_backend_modify_object (ECalBackend *backend,
-                           EDataCal *cal,
-                           guint32 opid,
-                           GCancellable *cancellable,
-                           const gchar *calobj,
-                           CalObjModType mod)
+cal_backend_modify_objects (ECalBackend *backend,
+                            EDataCal *cal,
+                            guint32 opid,
+                            GCancellable *cancellable,
+                            const GSList *calobjs,
+                            CalObjModType mod)
 {
 	GError *error = NULL;
-	ECalComponent *old_component = NULL, *new_component = NULL;
-
-	e_cal_backend_sync_modify_object (E_CAL_BACKEND_SYNC (backend), cal, cancellable, calobj, mod, &old_component, &new_component, &error);
+	GSList *old_components = NULL, *new_components = NULL;
 
-	if (!old_component)
-		old_component = e_cal_component_new_from_string (calobj);
+	e_cal_backend_sync_modify_objects (E_CAL_BACKEND_SYNC (backend), cal, cancellable, calobjs, mod, &old_components, &new_components, &error);
 
-	e_data_cal_respond_modify_object (cal, opid, error, old_component, new_component);
+	if (!old_components)
+		old_components = ecalcomponent_slist_from_strings (calobjs);
 
-	if (old_component)
-		g_object_unref (old_component);
+	e_data_cal_respond_modify_objects (cal, opid, error, old_components, new_components);
 
-	if (new_component)
-		g_object_unref (new_component);
+	e_util_free_nullable_object_slist (old_components);
+	e_util_free_nullable_object_slist (new_components);
 }
 
 static void
-cal_backend_remove_object (ECalBackend *backend,
-                           EDataCal *cal,
-                           guint32 opid,
-                           GCancellable *cancellable,
-                           const gchar *uid,
-                           const gchar *rid,
-                           CalObjModType mod)
+cal_backend_remove_objects (ECalBackend *backend,
+                            EDataCal *cal,
+                            guint32 opid,
+                            GCancellable *cancellable,
+                            const GSList *ids,
+                            CalObjModType mod)
 {
 	GError *error = NULL;
-	ECalComponent *old_component = NULL, *new_component = NULL;
-	ECalComponentId compid;
-
-	compid.uid = (gchar *) uid;
-	compid.rid = (gchar *) ((mod == CALOBJ_MOD_THIS || mod == CALOBJ_MOD_ONLY_THIS) ? rid : NULL);
-
-	e_cal_backend_sync_remove_object (E_CAL_BACKEND_SYNC (backend), cal, cancellable, uid, rid, mod, &old_component, &new_component, &error);
+	GSList *old_components = NULL, *new_components = NULL;
 
-	e_data_cal_respond_remove_object (cal, opid, error, &compid, old_component, new_component);
+	e_cal_backend_sync_remove_objects (E_CAL_BACKEND_SYNC (backend), cal, cancellable, ids, mod, &old_components, &new_components, &error);
 
-	if (old_component)
-		g_object_unref (old_component);
+	e_data_cal_respond_remove_objects (cal, opid, error, ids, old_components, new_components);
 
-	if (new_component)
-		g_object_unref (new_component);
+	e_util_free_nullable_object_slist (old_components);
+	e_util_free_nullable_object_slist (new_components);
 }
 
 static void
@@ -1049,9 +1048,9 @@ e_cal_backend_sync_class_init (ECalBackendSyncClass *class)
 	backend_class->get_object		= cal_backend_get_object;
 	backend_class->get_object_list		= cal_backend_get_object_list;
 	backend_class->get_free_busy		= cal_backend_get_free_busy;
-	backend_class->create_object		= cal_backend_create_object;
-	backend_class->modify_object		= cal_backend_modify_object;
-	backend_class->remove_object		= cal_backend_remove_object;
+	backend_class->create_objects		= cal_backend_create_objects;
+	backend_class->modify_objects		= cal_backend_modify_objects;
+	backend_class->remove_objects		= cal_backend_remove_objects;
 	backend_class->receive_objects		= cal_backend_receive_objects;
 	backend_class->send_objects		= cal_backend_send_objects;
 	backend_class->get_attachment_uris	= cal_backend_get_attachment_uris;
diff --git a/calendar/libedata-cal/e-cal-backend-sync.h b/calendar/libedata-cal/e-cal-backend-sync.h
index d3c2d13..6b7d180 100644
--- a/calendar/libedata-cal/e-cal-backend-sync.h
+++ b/calendar/libedata-cal/e-cal-backend-sync.h
@@ -39,9 +39,9 @@ struct _ECalBackendSyncClass {
 	void	(* get_object_sync)		(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *uid, const gchar *rid, gchar **calobj, GError **error);
 	void	(* get_object_list_sync)	(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *sexp, GSList **calobjs, GError **error);
 	void	(* get_free_busy_sync)		(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const GSList *users, time_t start, time_t end, GSList **freebusyobjs, GError **error);
-	void	(* create_object_sync)		(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *calobj, gchar **uid, ECalComponent **new_component, GError **error);
-	void	(* modify_object_sync)		(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *calobj, CalObjModType mod, ECalComponent **old_component, ECalComponent **new_component, GError **error);
-	void	(* remove_object_sync)		(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *uid, const gchar *rid, CalObjModType mod, ECalComponent **old_component, ECalComponent **new_component, GError **error);
+	void	(* create_objects_sync)		(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const GSList *calobjs, GSList **uids, GSList **new_components, GError **error);
+	void	(* modify_objects_sync)		(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const GSList *calobjs, CalObjModType mod, GSList **old_components, GSList **new_components, GError **error);
+	void	(* remove_objects_sync)		(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const GSList *ids, CalObjModType mod, GSList **old_components, GSList **new_components, GError **error);
 	void	(* receive_objects_sync)	(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *calobj, GError **error);
 	void	(* send_objects_sync)		(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *calobj, GSList **users, gchar **modified_calobj, GError **error);
 	void	(* get_attachment_uris_sync)	(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *uid, const gchar *rid, GSList **attachments, GError **error);
@@ -64,9 +64,9 @@ gboolean e_cal_backend_sync_set_backend_property (ECalBackendSync *backend, EDat
 void	e_cal_backend_sync_get_object		(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *uid, const gchar *rid, gchar **calobj, GError **error);
 void	e_cal_backend_sync_get_object_list	(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *sexp, GSList **calobjs, GError **error);
 void	e_cal_backend_sync_get_free_busy	(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const GSList *users, time_t start, time_t end, GSList **freebusyobjects, GError **error);
-void	e_cal_backend_sync_create_object	(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *calobj, gchar **uid, ECalComponent **new_component, GError **error);
-void	e_cal_backend_sync_modify_object	(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *calobj, CalObjModType mod, ECalComponent **old_component, ECalComponent **new_component, GError **error);
-void	e_cal_backend_sync_remove_object	(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *uid, const gchar *rid, CalObjModType mod, ECalComponent **old_component, ECalComponent **new_component, GError **error);
+void	e_cal_backend_sync_create_objects	(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const GSList *calobjs, GSList **uids, GSList **new_components, GError **error);
+void	e_cal_backend_sync_modify_objects	(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const GSList *calobjs, CalObjModType mod, GSList **old_components, GSList **new_components, GError **error);
+void	e_cal_backend_sync_remove_objects	(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const GSList *ids, CalObjModType mod, GSList **old_components, GSList **new_components, GError **error);
 void	e_cal_backend_sync_receive_objects	(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *calobj, GError **error);
 void	e_cal_backend_sync_send_objects		(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *calobj, GSList **users, gchar **modified_calobj, GError **error);
 void	e_cal_backend_sync_get_attachment_uris	(ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *uid, const gchar *rid, GSList **attachments, GError **error);
diff --git a/calendar/libedata-cal/e-cal-backend.c b/calendar/libedata-cal/e-cal-backend.c
index 1894690..d956ae3 100644
--- a/calendar/libedata-cal/e-cal-backend.c
+++ b/calendar/libedata-cal/e-cal-backend.c
@@ -1014,105 +1014,103 @@ e_cal_backend_get_free_busy (ECalBackend *backend,
 }
 
 /**
- * e_cal_backend_create_object:
+ * e_cal_backend_create_objects:
  * @backend: an #ECalBackend
  * @cal: an #EDataCal
  * @opid: the ID to use for this operation
  * @cancellable: a #GCancellable for the operation
- * @calobj: The object to create.
+ * @calobjs: The objects to create (list of gchar *).
  *
  * Calls the create_object method on the given backend.
- * This might be finished with e_data_cal_respond_create_object().
+ * This might be finished with e_data_cal_respond_create_objects().
  **/
 void
-e_cal_backend_create_object (ECalBackend *backend,
-                             EDataCal *cal,
-                             guint32 opid,
-                             GCancellable *cancellable,
-                             const gchar *calobj)
+e_cal_backend_create_objects (ECalBackend *backend,
+                              EDataCal *cal,
+                              guint32 opid,
+                              GCancellable *cancellable,
+                              const GSList *calobjs)
 {
 	g_return_if_fail (backend != NULL);
 	g_return_if_fail (E_IS_CAL_BACKEND (backend));
-	g_return_if_fail (calobj != NULL);
+	g_return_if_fail (calobjs != NULL);
 
 	if (e_cal_backend_is_opening (backend))
-		e_data_cal_respond_create_object (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
-	else if (!E_CAL_BACKEND_GET_CLASS (backend)->create_object)
-		e_data_cal_respond_create_object (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
+		e_data_cal_respond_create_objects (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
+	else if (!E_CAL_BACKEND_GET_CLASS (backend)->create_objects)
+		e_data_cal_respond_create_objects (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
 	else if (!e_cal_backend_is_opened (backend))
-		e_data_cal_respond_create_object (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
+		e_data_cal_respond_create_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
 	else
-		(* E_CAL_BACKEND_GET_CLASS (backend)->create_object) (backend, cal, opid, cancellable, calobj);
+		(* E_CAL_BACKEND_GET_CLASS (backend)->create_objects) (backend, cal, opid, cancellable, calobjs);
 }
 
 /**
- * e_cal_backend_modify_object:
+ * e_cal_backend_modify_objects:
  * @backend: an #ECalBackend
  * @cal: an #EDataCal
  * @opid: the ID to use for this operation
  * @cancellable: a #GCancellable for the operation
- * @calobj: Object to be modified.
+ * @calobjs: Objects to be modified (list of gchar *).
  * @mod: Type of modification.
  *
- * Calls the modify_object method on the given backend.
- * This might be finished with e_data_cal_respond_modify_object().
+ * Calls the modify_objects method on the given backend.
+ * This might be finished with e_data_cal_respond_modify_objects().
  **/
 void
-e_cal_backend_modify_object (ECalBackend *backend,
-                             EDataCal *cal,
-                             guint32 opid,
-                             GCancellable *cancellable,
-                             const gchar *calobj,
-                             CalObjModType mod)
+e_cal_backend_modify_objects (ECalBackend *backend,
+                              EDataCal *cal,
+                              guint32 opid,
+                              GCancellable *cancellable,
+                              const GSList *calobjs,
+                              CalObjModType mod)
 {
 	g_return_if_fail (backend != NULL);
 	g_return_if_fail (E_IS_CAL_BACKEND (backend));
-	g_return_if_fail (calobj != NULL);
+	g_return_if_fail (calobjs != NULL);
 
 	if (e_cal_backend_is_opening (backend))
-		e_data_cal_respond_modify_object (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
-	else if (!E_CAL_BACKEND_GET_CLASS (backend)->modify_object)
-		e_data_cal_respond_modify_object (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
+		e_data_cal_respond_modify_objects (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
+	else if (!E_CAL_BACKEND_GET_CLASS (backend)->modify_objects)
+		e_data_cal_respond_modify_objects (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
 	else if (!e_cal_backend_is_opened (backend))
-		e_data_cal_respond_modify_object (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
+		e_data_cal_respond_modify_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
 	else
-		(* E_CAL_BACKEND_GET_CLASS (backend)->modify_object) (backend, cal, opid, cancellable, calobj, mod);
+		(* E_CAL_BACKEND_GET_CLASS (backend)->modify_objects) (backend, cal, opid, cancellable, calobjs, mod);
 }
 
 /**
- * e_cal_backend_remove_object:
+ * e_cal_backend_remove_objects:
  * @backend: an #ECalBackend
  * @cal: an #EDataCal
  * @opid: the ID to use for this operation
  * @cancellable: a #GCancellable for the operation
- * @uid: Unique identifier of the object to remove.
- * @rid: A recurrence ID.
+ * @ids: List of #ECalComponentId objects identifying the objects to remove
  * @mod: Type of removal.
  *
- * Removes an object in a calendar backend.  The backend will notify all of its
+ * Removes objects in a calendar backend.  The backend will notify all of its
  * clients about the change.
- * This might be finished with e_data_cal_respond_remove_object().
+ * This might be finished with e_data_cal_respond_remove_objects().
  **/
 void
-e_cal_backend_remove_object (ECalBackend *backend,
-                             EDataCal *cal,
-                             guint32 opid,
-                             GCancellable *cancellable,
-                             const gchar *uid,
-                             const gchar *rid,
-                             CalObjModType mod)
+e_cal_backend_remove_objects (ECalBackend *backend,
+                              EDataCal *cal,
+                              guint32 opid,
+                              GCancellable *cancellable,
+                              const GSList *ids,
+                              CalObjModType mod)
 {
 	g_return_if_fail (backend != NULL);
 	g_return_if_fail (E_IS_CAL_BACKEND (backend));
-	g_return_if_fail (uid != NULL);
-	g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->remove_object != NULL);
+	g_return_if_fail (ids != NULL);
+	g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->remove_objects != NULL);
 
 	if (e_cal_backend_is_opening (backend))
-		e_data_cal_respond_remove_object (cal, opid, EDC_OPENING_ERROR, NULL, NULL, NULL);
+		e_data_cal_respond_remove_objects (cal, opid, EDC_OPENING_ERROR, NULL, NULL, NULL);
 	else if (!e_cal_backend_is_opened (backend))
-		e_data_cal_respond_remove_object (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL, NULL);
+		e_data_cal_respond_remove_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL, NULL);
 	else
-		(* E_CAL_BACKEND_GET_CLASS (backend)->remove_object) (backend, cal, opid, cancellable, uid, rid, mod);
+		(* E_CAL_BACKEND_GET_CLASS (backend)->remove_objects) (backend, cal, opid, cancellable, ids, mod);
 }
 
 /**
diff --git a/calendar/libedata-cal/e-cal-backend.h b/calendar/libedata-cal/e-cal-backend.h
index 13cf488..20e99d0 100644
--- a/calendar/libedata-cal/e-cal-backend.h
+++ b/calendar/libedata-cal/e-cal-backend.h
@@ -160,9 +160,9 @@ struct _ECalBackendClass {
 	void	(* get_object)			(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *uid, const gchar *rid);
 	void	(* get_object_list)		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *sexp);
 	void	(* get_free_busy)		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const GSList *users, time_t start, time_t end);
-	void	(* create_object)		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *calobj);
-	void	(* modify_object)		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *calobj, CalObjModType mod);
-	void	(* remove_object)		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *uid, const gchar *rid, CalObjModType mod);
+	void	(* create_objects)		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const GSList *calobjs);
+	void	(* modify_objects)		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const GSList *calobjs, CalObjModType mod);
+	void	(* remove_objects)		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const GSList *ids, CalObjModType mod);
 	void	(* receive_objects)		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *calobj);
 	void	(* send_objects)		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *calobj);
 	void	(* get_attachment_uris)		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *uid, const gchar *rid);
@@ -212,9 +212,9 @@ void		e_cal_backend_refresh			(ECalBackend *backend, EDataCal *cal, guint32 opid
 void		e_cal_backend_get_object		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *uid, const gchar *rid);
 void		e_cal_backend_get_object_list		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *sexp);
 void		e_cal_backend_get_free_busy		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const GSList *users, time_t start, time_t end);
-void		e_cal_backend_create_object		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *calobj);
-void		e_cal_backend_modify_object		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *calobj, CalObjModType mod);
-void		e_cal_backend_remove_object		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *uid, const gchar *rid, CalObjModType mod);
+void		e_cal_backend_create_objects		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const GSList *calobjs);
+void		e_cal_backend_modify_objects		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const GSList *calobjs, CalObjModType mod);
+void		e_cal_backend_remove_objects		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const GSList *ids, CalObjModType mod);
 void		e_cal_backend_receive_objects		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *calobj);
 void		e_cal_backend_send_objects		(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *calobj);
 void		e_cal_backend_get_attachment_uris	(ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *uid, const gchar *rid);
diff --git a/calendar/libedata-cal/e-data-cal.c b/calendar/libedata-cal/e-data-cal.c
index df1f682..680baf4 100644
--- a/calendar/libedata-cal/e-data-cal.c
+++ b/calendar/libedata-cal/e-data-cal.c
@@ -71,9 +71,9 @@ typedef enum {
 	OP_GET_OBJECT,
 	OP_GET_OBJECT_LIST,
 	OP_GET_FREE_BUSY,
-	OP_CREATE_OBJECT,
-	OP_MODIFY_OBJECT,
-	OP_REMOVE_OBJECT,
+	OP_CREATE_OBJECTS,
+	OP_MODIFY_OBJECTS,
+	OP_REMOVE_OBJECTS,
 	OP_RECEIVE_OBJECTS,
 	OP_SEND_OBJECTS,
 	OP_GET_ATTACHMENT_URIS,
@@ -117,21 +117,21 @@ typedef struct {
 			time_t start, end;
 			GSList *users;
 		} fb;
-		/* OP_CREATE_OBJECT */
+		/* OP_CREATE_OBJECTS */
+		GSList *calobjs;
 		/* OP_RECEIVE_OBJECTS */
 		/* OP_SEND_OBJECTS */
 		struct _co {
 			gchar *calobj;
 		} co;
-		/* OP_MODIFY_OBJECT */
+		/* OP_MODIFY_OBJECTS */
 		struct _mo {
-			gchar *calobj;
+			GSList *calobjs;
 			EDataCalObjModType mod;
 		} mo;
-		/* OP_REMOVE_OBJECT */
+		/* OP_REMOVE_OBJECTS */
 		struct _ro {
-			gchar *uid;
-			gchar *rid;
+			GSList *ids;
 			EDataCalObjModType mod;
 		} ro;
 		/* OP_GET_TIMEZONE */
@@ -214,21 +214,19 @@ operation_thread (gpointer data,
 		break;
 	case OP_GET_FREE_BUSY:
 		e_cal_backend_get_free_busy (backend, op->cal, op->id, op->cancellable, op->d.fb.users, op->d.fb.start, op->d.fb.end);
-		g_slist_foreach (op->d.fb.users, (GFunc) g_free, NULL);
-		g_slist_free (op->d.fb.users);
+		g_slist_free_full (op->d.fb.users, g_free);
 		break;
-	case OP_CREATE_OBJECT:
-		e_cal_backend_create_object (backend, op->cal, op->id, op->cancellable, op->d.co.calobj);
-		g_free (op->d.co.calobj);
+	case OP_CREATE_OBJECTS:
+		e_cal_backend_create_objects (backend, op->cal, op->id, op->cancellable, op->d.calobjs);
+		g_slist_free_full (op->d.calobjs, g_free);
 		break;
-	case OP_MODIFY_OBJECT:
-		e_cal_backend_modify_object (backend, op->cal, op->id, op->cancellable, op->d.mo.calobj, op->d.mo.mod);
-		g_free (op->d.mo.calobj);
+	case OP_MODIFY_OBJECTS:
+		e_cal_backend_modify_objects (backend, op->cal, op->id, op->cancellable, op->d.mo.calobjs, op->d.mo.mod);
+		g_slist_free_full (op->d.mo.calobjs, g_free);
 		break;
-	case OP_REMOVE_OBJECT:
-		e_cal_backend_remove_object (backend, op->cal, op->id, op->cancellable, op->d.ro.uid, op->d.ro.rid && *op->d.ro.rid ? op->d.ro.rid : NULL, op->d.ro.mod);
-		g_free (op->d.ro.uid);
-		g_free (op->d.ro.rid);
+	case OP_REMOVE_OBJECTS:
+		e_cal_backend_remove_objects (backend, op->cal, op->id, op->cancellable, op->d.ro.ids, op->d.ro.mod);
+		g_slist_free_full (op->d.ro.ids, (GDestroyNotify)e_cal_component_free_id);
 		break;
 	case OP_RECEIVE_OBJECTS:
 		e_cal_backend_receive_objects (backend, op->cal, op->id, op->cancellable, op->d.co.calobj);
@@ -679,55 +677,55 @@ impl_Cal_get_free_busy (EGdbusCal *object,
 }
 
 static gboolean
-impl_Cal_create_object (EGdbusCal *object,
-                        GDBusMethodInvocation *invocation,
-                        const gchar *in_calobj,
-                        EDataCal *cal)
+impl_Cal_create_objects (EGdbusCal *object,
+                         GDBusMethodInvocation *invocation,
+                         const gchar * const *in_calobjs,
+                         EDataCal *cal)
 {
 	OperationData *op;
 
-	op = op_new (OP_CREATE_OBJECT, cal);
-	op->d.co.calobj = g_strdup (in_calobj);
+	op = op_new (OP_CREATE_OBJECTS, cal);
+	op->d.calobjs = e_util_strv_to_slist (in_calobjs);
 
-	e_gdbus_cal_complete_create_object (cal->priv->gdbus_object, invocation, op->id);
+	e_gdbus_cal_complete_create_objects (cal->priv->gdbus_object, invocation, op->id);
 	e_operation_pool_push (ops_pool, op);
 
 	return TRUE;
 }
 
 static gboolean
-impl_Cal_modify_object (EGdbusCal *object,
-                        GDBusMethodInvocation *invocation,
-                        const gchar * const *in_calobj_mod,
-                        EDataCal *cal)
+impl_Cal_modify_objects (EGdbusCal *object,
+                         GDBusMethodInvocation *invocation,
+                         const gchar * const *in_mod_calobjs,
+                         EDataCal *cal)
 {
 	OperationData *op;
 	guint mod;
 
-	op = op_new (OP_MODIFY_OBJECT, cal);
-	g_return_val_if_fail (e_gdbus_cal_decode_modify_object (in_calobj_mod, &op->d.mo.calobj, &mod), FALSE);
+	op = op_new (OP_MODIFY_OBJECTS, cal);
+	g_return_val_if_fail (e_gdbus_cal_decode_modify_objects (in_mod_calobjs, &op->d.mo.calobjs, &mod), FALSE);
 	op->d.mo.mod = mod;
 
-	e_gdbus_cal_complete_modify_object (cal->priv->gdbus_object, invocation, op->id);
+	e_gdbus_cal_complete_modify_objects (cal->priv->gdbus_object, invocation, op->id);
 	e_operation_pool_push (ops_pool, op);
 
 	return TRUE;
 }
 
 static gboolean
-impl_Cal_remove_object (EGdbusCal *object,
-                        GDBusMethodInvocation *invocation,
-                        const gchar * const *in_uid_rid_mod,
-                        EDataCal *cal)
+impl_Cal_remove_objects (EGdbusCal *object,
+                         GDBusMethodInvocation *invocation,
+                         const gchar * const *in_mod_ids,
+                         EDataCal *cal)
 {
 	OperationData *op;
 	guint mod = 0;
 
-	op = op_new (OP_REMOVE_OBJECT, cal);
-	g_return_val_if_fail (e_gdbus_cal_decode_remove_object (in_uid_rid_mod, &op->d.ro.uid, &op->d.ro.rid, &mod), FALSE);
+	op = op_new (OP_REMOVE_OBJECTS, cal);
+	g_return_val_if_fail (e_gdbus_cal_decode_remove_objects (in_mod_ids, &op->d.ro.ids, &mod), FALSE);
 	op->d.ro.mod = mod;
 
-	e_gdbus_cal_complete_remove_object (cal->priv->gdbus_object, invocation, op->id);
+	e_gdbus_cal_complete_remove_objects (cal->priv->gdbus_object, invocation, op->id);
 	e_operation_pool_push (ops_pool, op);
 
 	return TRUE;
@@ -1163,102 +1161,125 @@ e_data_cal_respond_get_free_busy (EDataCal *cal,
 }
 
 /**
- * e_data_cal_respond_create_object:
+ * e_data_cal_respond_create_objects:
  * @cal: A calendar client interface.
  * @error: Operation error, if any, automatically freed if passed it.
- * @uid: UID of the object created.
- * @new_component: The newly created #ECalComponent.
+ * @uids: UIDs of the objects created.
+ * @new_components: The newly created #ECalComponent objects.
  *
- * Notifies listeners of the completion of the create_object method call.
+ * Notifies listeners of the completion of the create_objects method call.
  *
- * Since: 3.2
+ * Since: 3.6
  */
 void
-e_data_cal_respond_create_object (EDataCal *cal,
-                                  guint32 opid,
-                                  GError *error,
-                                  const gchar *uid,
-                                  /* const */ ECalComponent *new_component)
+e_data_cal_respond_create_objects (EDataCal *cal,
+                                   guint32 opid,
+                                   GError *error,
+                                   const GSList *uids,
+                                   /* const */ GSList *new_components)
 {
-	gchar *gdbus_uid = NULL;
+	gchar **array = NULL;
+	const GSList *l;
+	gint i = 0;
 
 	op_complete (cal, opid);
 
+	array = g_new0 (gchar *, g_slist_length ((GSList *) uids) + 1);
+	for (l = uids; l != NULL; l = l->next) {
+		array[i++] = e_util_utf8_make_valid (l->data);
+	}
+
 	/* Translators: This is prefix to a detailed error message */
 	g_prefix_error (&error, "%s", _("Cannot create calendar object: "));
 
-	e_gdbus_cal_emit_create_object_done (cal->priv->gdbus_object, opid, error, e_util_ensure_gdbus_string (uid, &gdbus_uid));
+	e_gdbus_cal_emit_create_objects_done (cal->priv->gdbus_object, opid, error, (const gchar * const *)array);
 
-	g_free (gdbus_uid);
+	g_strfreev (array);
 	if (error)
 		g_error_free (error);
-	else
-		e_cal_backend_notify_component_created (cal->priv->backend, new_component);
+	else {
+		for (l = new_components; l; l = l->next) {
+			e_cal_backend_notify_component_created (cal->priv->backend, l->data);
+		}
+	}
 }
 
 /**
- * e_data_cal_respond_modify_object:
+ * e_data_cal_respond_modify_objects:
  * @cal: A calendar client interface.
  * @error: Operation error, if any, automatically freed if passed it.
- * @old_component: The old #ECalComponent.
- * @new_component: The new #ECalComponent.
+ * @old_components: The old #ECalComponents.
+ * @new_components: The new #ECalComponents.
  *
- * Notifies listeners of the completion of the modify_object method call.
+ * Notifies listeners of the completion of the modify_objects method call.
  *
- * Since: 3.2
+ * Since: 3.6
  */
 void
-e_data_cal_respond_modify_object (EDataCal *cal,
-                                  guint32 opid,
-                                  GError *error,
-                                  /* const */ ECalComponent *old_component,
-                                  /* const */ ECalComponent *new_component)
+e_data_cal_respond_modify_objects (EDataCal *cal,
+                                   guint32 opid,
+                                   GError *error,
+                                   /* const */ GSList *old_components,
+                                   /* const */ GSList *new_components)
 {
 	op_complete (cal, opid);
 
 	/* Translators: This is prefix to a detailed error message */
 	g_prefix_error (&error, "%s", _("Cannot modify calendar object: "));
 
-	e_gdbus_cal_emit_modify_object_done (cal->priv->gdbus_object, opid, error);
+	e_gdbus_cal_emit_modify_objects_done (cal->priv->gdbus_object, opid, error);
 
 	if (error)
 		g_error_free (error);
-	else
-		e_cal_backend_notify_component_modified (cal->priv->backend, old_component, new_component);
+	else {
+		const GSList *lold = old_components, *lnew = new_components;
+		while (lold && lnew) {
+			e_cal_backend_notify_component_modified (cal->priv->backend, lold->data, lnew->data);
+			lold = lold->next;
+			lnew = lnew->next;
+		}
+	}
 }
 
 /**
- * e_data_cal_respond_remove_object:
+ * e_data_cal_respond_remove_objects:
  * @cal: A calendar client interface.
  * @error: Operation error, if any, automatically freed if passed it.
- * @id: ID of the removed object.
- * @old_component: The old #ECalComponent.
- * @new_component: The new #ECalComponent. This will not be NULL only
- * when removing instances of a recurring appointment.
+ * @ids: IDs of the removed objects.
+ * @old_components: The old #ECalComponents.
+ * @new_components: The new #ECalComponents. They will not be NULL only
+ * when removing instances of recurring appointments.
  *
- * Notifies listeners of the completion of the remove_object method call.
+ * Notifies listeners of the completion of the remove_objects method call.
  *
- * Since: 3.2
+ * Since: 3.6
  */
 void
-e_data_cal_respond_remove_object (EDataCal *cal,
+e_data_cal_respond_remove_objects (EDataCal *cal,
                                   guint32 opid,
                                   GError *error,
-                                  const ECalComponentId *id,
-                                  /* const */ ECalComponent *old_component,
-                                  /* const */ ECalComponent *new_component)
+                                  const GSList *ids,
+                                  /* const */ GSList *old_components,
+                                  /* const */ GSList *new_components)
 {
 	op_complete (cal, opid);
 
 	/* Translators: This is prefix to a detailed error message */
 	g_prefix_error (&error, "%s", _("Cannot remove calendar object: "));
 
-	e_gdbus_cal_emit_remove_object_done (cal->priv->gdbus_object, opid, error);
+	e_gdbus_cal_emit_remove_objects_done (cal->priv->gdbus_object, opid, error);
 
 	if (error)
 		g_error_free (error);
-	else
-		e_cal_backend_notify_component_removed (cal->priv->backend, id, old_component, new_component);
+	else {
+		const GSList *lid = ids, *lold = old_components, *lnew = new_components;
+		while (lid && lold && lnew) {
+			e_cal_backend_notify_component_removed (cal->priv->backend, lid->data, lold->data, lnew->data);
+			lid = lid->next;
+			lold = lold->next;
+			lnew = lnew->next;
+		}
+	}
 }
 
 /**
@@ -1768,14 +1789,14 @@ e_data_cal_init (EDataCal *ecal)
 		gdbus_object, "handle-get-free-busy",
 		G_CALLBACK (impl_Cal_get_free_busy), ecal);
 	g_signal_connect (
-		gdbus_object, "handle-create-object",
-		G_CALLBACK (impl_Cal_create_object), ecal);
+		gdbus_object, "handle-create-objects",
+		G_CALLBACK (impl_Cal_create_objects), ecal);
 	g_signal_connect (
-		gdbus_object, "handle-modify-object",
-		G_CALLBACK (impl_Cal_modify_object), ecal);
+		gdbus_object, "handle-modify-objects",
+		G_CALLBACK (impl_Cal_modify_objects), ecal);
 	g_signal_connect (
-		gdbus_object, "handle-remove-object",
-		G_CALLBACK (impl_Cal_remove_object), ecal);
+		gdbus_object, "handle-remove-objects",
+		G_CALLBACK (impl_Cal_remove_objects), ecal);
 	g_signal_connect (
 		gdbus_object, "handle-receive-objects",
 		G_CALLBACK (impl_Cal_receive_objects), ecal);
diff --git a/calendar/libedata-cal/e-data-cal.h b/calendar/libedata-cal/e-data-cal.h
index f9c5ecd..15daa43 100644
--- a/calendar/libedata-cal/e-data-cal.h
+++ b/calendar/libedata-cal/e-data-cal.h
@@ -136,9 +136,9 @@ void		e_data_cal_respond_set_backend_property		(EDataCal *cal, guint32 opid, GEr
 void		e_data_cal_respond_get_object			(EDataCal *cal, guint32 opid, GError *error, const gchar *object);
 void		e_data_cal_respond_get_object_list		(EDataCal *cal, guint32 opid, GError *error, const GSList *objects);
 void		e_data_cal_respond_get_free_busy		(EDataCal *cal, guint32 opid, GError *error);
-void		e_data_cal_respond_create_object		(EDataCal *cal, guint32 opid, GError *error, const gchar *uid, /*const */ ECalComponent *new_component);
-void		e_data_cal_respond_modify_object		(EDataCal *cal, guint32 opid, GError *error, /* const */ ECalComponent *old_component, /* const */ ECalComponent *new_component);
-void		e_data_cal_respond_remove_object		(EDataCal *cal, guint32 opid, GError *error, const ECalComponentId *id, /* const */ ECalComponent *old_component, /* const */ ECalComponent *new_component);
+void		e_data_cal_respond_create_objects		(EDataCal *cal, guint32 opid, GError *error, const GSList *uids, /*const */ GSList *new_components);
+void		e_data_cal_respond_modify_objects		(EDataCal *cal, guint32 opid, GError *error, /* const */ GSList *old_components, /* const */ GSList *new_components);
+void		e_data_cal_respond_remove_objects		(EDataCal *cal, guint32 opid, GError *error, const GSList *ids, /* const */ GSList *old_components, /* const */ GSList *new_components);
 void		e_data_cal_respond_receive_objects		(EDataCal *cal, guint32 opid, GError *error);
 void		e_data_cal_respond_send_objects			(EDataCal *cal, guint32 opid, GError *error, const GSList *users, const gchar *calobj);
 void		e_data_cal_respond_get_attachment_uris		(EDataCal *cal, guint32 opid, GError *error, const GSList *attachment_uris);
diff --git a/calendar/libegdbus/e-gdbus-cal.c b/calendar/libegdbus/e-gdbus-cal.c
index 8c9212a..c8edbd8 100644
--- a/calendar/libegdbus/e-gdbus-cal.c
+++ b/calendar/libegdbus/e-gdbus-cal.c
@@ -25,6 +25,8 @@
 
 #include <libedataserver/e-data-server-util.h>
 #include <libedataserver/e-gdbus-marshallers.h>
+/* We only need the ECalComponentId structure from the following header */
+#include <libecal/e-cal-component.h>
 
 #include "e-gdbus-cal.h"
 
@@ -63,12 +65,12 @@ enum
 	__GET_OBJECT_LIST_DONE_SIGNAL,
 	__GET_FREE_BUSY_METHOD,
 	__GET_FREE_BUSY_DONE_SIGNAL,
-	__CREATE_OBJECT_METHOD,
-	__CREATE_OBJECT_DONE_SIGNAL,
-	__MODIFY_OBJECT_METHOD,
-	__MODIFY_OBJECT_DONE_SIGNAL,
-	__REMOVE_OBJECT_METHOD,
-	__REMOVE_OBJECT_DONE_SIGNAL,
+	__CREATE_OBJECTS_METHOD,
+	__CREATE_OBJECTS_DONE_SIGNAL,
+	__MODIFY_OBJECTS_METHOD,
+	__MODIFY_OBJECTS_DONE_SIGNAL,
+	__REMOVE_OBJECTS_METHOD,
+	__REMOVE_OBJECTS_DONE_SIGNAL,
 	__RECEIVE_OBJECTS_METHOD,
 	__RECEIVE_OBJECTS_DONE_SIGNAL,
 	__SEND_OBJECTS_METHOD,
@@ -163,12 +165,12 @@ E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRV (GDBUS_CAL_INTERFACE_NAME,
                                                       get_object_list)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID (GDBUS_CAL_INTERFACE_NAME,
                                                       get_free_busy)
-E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRING (GDBUS_CAL_INTERFACE_NAME,
-                                                        create_object)
+E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRV (GDBUS_CAL_INTERFACE_NAME,
+													  create_objects)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID (GDBUS_CAL_INTERFACE_NAME,
-                                                      modify_object)
+													  modify_objects)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID (GDBUS_CAL_INTERFACE_NAME,
-                                                      remove_object)
+                                                      remove_objects)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID (GDBUS_CAL_INTERFACE_NAME,
                                                       receive_objects)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRV (GDBUS_CAL_INTERFACE_NAME,
@@ -211,9 +213,9 @@ e_gdbus_cal_default_init (EGdbusCalIface *iface)
 	E_INIT_GDBUS_METHOD_ASYNC_STRV__STRING	(EGdbusCalIface, "get_object",			get_object, __GET_OBJECT_METHOD, __GET_OBJECT_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_ASYNC_STRING__STRV	(EGdbusCalIface, "get_object_list",		get_object_list, __GET_OBJECT_LIST_METHOD, __GET_OBJECT_LIST_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_ASYNC_STRV__VOID	(EGdbusCalIface, "get_free_busy",		get_free_busy, __GET_FREE_BUSY_METHOD, __GET_FREE_BUSY_DONE_SIGNAL)
-	E_INIT_GDBUS_METHOD_ASYNC_STRING__STRING(EGdbusCalIface, "create_object",		create_object, __CREATE_OBJECT_METHOD, __CREATE_OBJECT_DONE_SIGNAL)
-	E_INIT_GDBUS_METHOD_ASYNC_STRV__VOID	(EGdbusCalIface, "modify_object",		modify_object, __MODIFY_OBJECT_METHOD, __MODIFY_OBJECT_DONE_SIGNAL)
-	E_INIT_GDBUS_METHOD_ASYNC_STRV__VOID	(EGdbusCalIface, "remove_object",		remove_object, __REMOVE_OBJECT_METHOD, __REMOVE_OBJECT_DONE_SIGNAL)
+	E_INIT_GDBUS_METHOD_ASYNC_STRV__STRV	(EGdbusCalIface, "create_objects",		create_objects, __CREATE_OBJECTS_METHOD, __CREATE_OBJECTS_DONE_SIGNAL)
+	E_INIT_GDBUS_METHOD_ASYNC_STRV__VOID	(EGdbusCalIface, "modify_objects",		modify_objects, __MODIFY_OBJECTS_METHOD, __MODIFY_OBJECTS_DONE_SIGNAL)
+	E_INIT_GDBUS_METHOD_ASYNC_STRV__VOID	(EGdbusCalIface, "remove_objects",		remove_objects, __REMOVE_OBJECTS_METHOD, __REMOVE_OBJECTS_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_ASYNC_STRING__VOID	(EGdbusCalIface, "receive_objects",		receive_objects, __RECEIVE_OBJECTS_METHOD, __RECEIVE_OBJECTS_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_ASYNC_STRING__STRV	(EGdbusCalIface, "send_objects",		send_objects, __SEND_OBJECTS_METHOD, __SEND_OBJECTS_DONE_SIGNAL)
 	E_INIT_GDBUS_METHOD_ASYNC_STRV__STRV	(EGdbusCalIface, "get_attachment_uris",		get_attachment_uris, __GET_ATTACHMENT_URIS_METHOD, __GET_ATTACHMENT_URIS_DONE_SIGNAL)
@@ -547,170 +549,199 @@ e_gdbus_cal_call_get_free_busy_sync (GDBusProxy *proxy,
 }
 
 void
-e_gdbus_cal_call_create_object (GDBusProxy *proxy,
-                                const gchar *in_calobj,
-                                GCancellable *cancellable,
-                                GAsyncReadyCallback callback,
-                                gpointer user_data)
+e_gdbus_cal_call_create_objects (GDBusProxy *proxy,
+                                 const gchar * const *in_calobjs,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
 {
-	e_gdbus_proxy_call_string ("create_object", e_gdbus_cal_call_create_object, E_GDBUS_ASYNC_OP_KEEPER (proxy), in_calobj, cancellable, callback, user_data);
+	e_gdbus_proxy_call_strv ("create_objects", e_gdbus_cal_call_create_objects, E_GDBUS_ASYNC_OP_KEEPER (proxy), in_calobjs, cancellable, callback, user_data);
 }
 
 gboolean
-e_gdbus_cal_call_create_object_finish (GDBusProxy *proxy,
-                                       GAsyncResult *result,
-                                       gchar **out_uid,
-                                       GError **error)
+e_gdbus_cal_call_create_objects_finish (GDBusProxy *proxy,
+                                        GAsyncResult *result,
+                                        gchar ***out_uids,
+                                        GError **error)
 {
-	return e_gdbus_proxy_finish_call_string (E_GDBUS_ASYNC_OP_KEEPER (proxy), result, out_uid, error, e_gdbus_cal_call_create_object);
+	return e_gdbus_proxy_finish_call_strv (E_GDBUS_ASYNC_OP_KEEPER (proxy), result, out_uids, error, e_gdbus_cal_call_create_objects);
 }
 
 gboolean
-e_gdbus_cal_call_create_object_sync (GDBusProxy *proxy,
-                                     const gchar *in_calobj,
-                                     gchar **out_uid,
-                                     GCancellable *cancellable,
-                                     GError **error)
+e_gdbus_cal_call_create_objects_sync (GDBusProxy *proxy,
+                                      const gchar * const *in_calobjs,
+                                      gchar ***out_uids,
+                                      GCancellable *cancellable,
+                                      GError **error)
 {
-	return e_gdbus_proxy_call_sync_string__string (proxy, in_calobj, out_uid, cancellable, error,
-		e_gdbus_cal_call_create_object,
-		e_gdbus_cal_call_create_object_finish);
+	return e_gdbus_proxy_call_sync_strv__strv (proxy, in_calobjs, out_uids, cancellable, error,
+		e_gdbus_cal_call_create_objects,
+		e_gdbus_cal_call_create_objects_finish);
 }
 
 /* free returned pointer with g_strfreev() */
 gchar **
-e_gdbus_cal_encode_modify_object (const gchar *in_calobj,
-                                  guint in_mod)
+e_gdbus_cal_encode_modify_objects (const GSList *in_calobjs,
+                                   guint in_mod)
 {
 	gchar **strv;
+	const GSList *l;
+	gint i = 0;
 
-	g_return_val_if_fail (in_calobj != NULL, NULL);
+	g_return_val_if_fail (in_calobjs != NULL, NULL);
 
-	strv = g_new0 (gchar *, 3);
-	strv[0] = e_util_utf8_make_valid (in_calobj);
-	strv[1] = g_strdup_printf ("%u", (guint32) in_mod);
-	strv[2] = NULL;
+	strv = g_new0 (gchar *, g_slist_length ((GSList *)in_calobjs) + 2);
+	strv[i++] = g_strdup_printf ("%u", (guint32) in_mod);
+
+	for (l = in_calobjs; l; l = l->next) {
+		strv[i++] = e_util_utf8_make_valid ((gchar *) l->data);
+	}
+
+	strv[i] = NULL;
 
 	return strv;
 }
 
-/* free out_calobj with g_free() */
+/* free calobjs with g_slist_free_full(calobjs, g_free) */
 gboolean
-e_gdbus_cal_decode_modify_object (const gchar * const *in_strv,
-                                  gchar **out_calobj,
-                                  guint *out_mod)
+e_gdbus_cal_decode_modify_objects (const gchar * const *in_strv,
+                                   GSList **calobjs,
+                                   guint *out_mod)
 {
+	gint ii;
+
 	g_return_val_if_fail (in_strv != NULL, FALSE);
 	g_return_val_if_fail (in_strv[0] != NULL, FALSE);
 	g_return_val_if_fail (in_strv[1] != NULL, FALSE);
-	g_return_val_if_fail (in_strv[2] == NULL, FALSE);
-	g_return_val_if_fail (out_calobj != NULL, FALSE);
+	g_return_val_if_fail (calobjs != NULL, FALSE);
 	g_return_val_if_fail (out_mod != NULL, FALSE);
 
-	*out_calobj = g_strdup (in_strv[0]);
-	*out_mod = atoi (in_strv[1]);
+	*out_mod = atoi (in_strv[0]);
+	*calobjs = NULL;
+
+	for (ii = 1; in_strv[ii]; ii++) {
+		*calobjs = g_slist_prepend (*calobjs, g_strdup (in_strv[ii]));
+	}
+
+	*calobjs = g_slist_reverse (*calobjs);
 
 	return TRUE;
 }
 
 void
-e_gdbus_cal_call_modify_object (GDBusProxy *proxy,
-                                const gchar * const *in_calobj_mod,
-                                GCancellable *cancellable,
-                                GAsyncReadyCallback callback,
-                                gpointer user_data)
+e_gdbus_cal_call_modify_objects (GDBusProxy *proxy,
+                                 const gchar * const *in_mod_calobjs,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
 {
-	e_gdbus_proxy_call_strv ("modify_object", e_gdbus_cal_call_modify_object, E_GDBUS_ASYNC_OP_KEEPER (proxy), in_calobj_mod, cancellable, callback, user_data);
+	e_gdbus_proxy_call_strv ("modify_objects", e_gdbus_cal_call_modify_objects, E_GDBUS_ASYNC_OP_KEEPER (proxy), in_mod_calobjs, cancellable, callback, user_data);
 }
 
 gboolean
-e_gdbus_cal_call_modify_object_finish (GDBusProxy *proxy,
-                                       GAsyncResult *result,
-                                       GError **error)
+e_gdbus_cal_call_modify_objects_finish (GDBusProxy *proxy,
+                                        GAsyncResult *result,
+                                        GError **error)
 {
-	return e_gdbus_proxy_finish_call_void (E_GDBUS_ASYNC_OP_KEEPER (proxy), result, error, e_gdbus_cal_call_modify_object);
+	return e_gdbus_proxy_finish_call_void (E_GDBUS_ASYNC_OP_KEEPER (proxy), result, error, e_gdbus_cal_call_modify_objects);
 }
 
 gboolean
-e_gdbus_cal_call_modify_object_sync (GDBusProxy *proxy,
-                                     const gchar * const *in_calobj_mod,
-                                     GCancellable *cancellable,
-                                     GError **error)
+e_gdbus_cal_call_modify_objects_sync (GDBusProxy *proxy,
+                                      const gchar * const *in_mod_calobjs,
+                                      GCancellable *cancellable,
+                                      GError **error)
 {
-	return e_gdbus_proxy_call_sync_strv__void (proxy, in_calobj_mod, cancellable, error,
-		e_gdbus_cal_call_modify_object,
-		e_gdbus_cal_call_modify_object_finish);
+	return e_gdbus_proxy_call_sync_strv__void (proxy, in_mod_calobjs, cancellable, error,
+		e_gdbus_cal_call_modify_objects,
+		e_gdbus_cal_call_modify_objects_finish);
 }
 
 /* free returned pointer with g_strfreev() */
 gchar **
-e_gdbus_cal_encode_remove_object (const gchar *in_uid,
-                                  const gchar *in_rid,
-                                  guint in_mod)
+e_gdbus_cal_encode_remove_objects (const GSList *in_ids,
+                                   guint in_mod)
 {
 	gchar **strv;
+	const GSList *l;
+	gint i = 0;
 
-	g_return_val_if_fail (in_uid != NULL, NULL);
+	g_return_val_if_fail (in_ids != NULL, NULL);
 
-	strv = g_new0 (gchar *, 4);
-	strv[0] = e_util_utf8_make_valid (in_uid);
-	strv[1] = e_util_utf8_make_valid (in_rid ? in_rid : "");
-	strv[2] = g_strdup_printf ("%u", (guint32) in_mod);
-	strv[3] = NULL;
+	strv = g_new0 (gchar *, 2 + 2 * g_slist_length ((GSList *)in_ids));
+	strv[i++] = g_strdup_printf ("%u", (guint32) in_mod);
+
+	for (l = in_ids; l; l = l->next) {
+		ECalComponentId *id = l->data;
+
+		strv[i++] = e_util_utf8_make_valid (id->uid);
+		strv[i++] = e_util_utf8_make_valid (id->rid ? id->rid : "");
+	}
+
+	strv[i] = NULL;
 
 	return strv;
 }
 
-/* free out_uid and out_rid with g_free() */
+/* free ids g_slist_free_full(ids, g_free) */
 gboolean
-e_gdbus_cal_decode_remove_object (const gchar * const *in_strv,
-                                  gchar **out_uid,
-                                  gchar **out_rid,
-                                  guint *out_mod)
+e_gdbus_cal_decode_remove_objects (const gchar * const *in_strv,
+																	 GSList **out_ids,
+                                   guint *out_mod)
 {
+	gint ii;
+
 	g_return_val_if_fail (in_strv != NULL, FALSE);
 	g_return_val_if_fail (in_strv[0] != NULL, FALSE);
 	g_return_val_if_fail (in_strv[1] != NULL, FALSE);
 	g_return_val_if_fail (in_strv[2] != NULL, FALSE);
-	g_return_val_if_fail (in_strv[3] == NULL, FALSE);
-	g_return_val_if_fail (out_uid != NULL, FALSE);
-	g_return_val_if_fail (out_rid != NULL, FALSE);
+	g_return_val_if_fail (out_ids != NULL, FALSE);
 	g_return_val_if_fail (out_mod != NULL, FALSE);
 
-	*out_uid = g_strdup (in_strv[0]);
-	*out_rid = g_strdup (in_strv[1]);
-	*out_mod = atoi (in_strv[2]);
+	*out_mod = atoi (in_strv[0]);
+
+	*out_ids = NULL;
+
+	for (ii = 1; in_strv[ii] && in_strv[ii + 1]; ii += 2) {
+		ECalComponentId *id = g_new (ECalComponentId, 1);
+		id->uid = g_strdup (in_strv[ii]);
+		id->rid = *in_strv[ii + 1] ? g_strdup (in_strv[ii + 1]) : NULL;
+
+		*out_ids = g_slist_prepend (*out_ids, id);
+	}
+
+	*out_ids = g_slist_reverse (*out_ids);
 
 	return TRUE;
 }
 
 void
-e_gdbus_cal_call_remove_object (GDBusProxy *proxy,
-                                const gchar * const *in_uid_rid_mod,
-                                GCancellable *cancellable,
-                                GAsyncReadyCallback callback,
-                                gpointer user_data)
+e_gdbus_cal_call_remove_objects (GDBusProxy *proxy,
+                                 const gchar * const *in_mod_ids,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
 {
-	e_gdbus_proxy_call_strv ("remove_object", e_gdbus_cal_call_remove_object, E_GDBUS_ASYNC_OP_KEEPER (proxy), in_uid_rid_mod, cancellable, callback, user_data);
+	e_gdbus_proxy_call_strv ("remove_objects", e_gdbus_cal_call_remove_objects, E_GDBUS_ASYNC_OP_KEEPER (proxy), in_mod_ids, cancellable, callback, user_data);
 }
 
 gboolean
-e_gdbus_cal_call_remove_object_finish (GDBusProxy *proxy,
-                                       GAsyncResult *result,
-                                       GError **error)
+e_gdbus_cal_call_remove_objects_finish (GDBusProxy *proxy,
+                                        GAsyncResult *result,
+                                        GError **error)
 {
-	return e_gdbus_proxy_finish_call_void (E_GDBUS_ASYNC_OP_KEEPER (proxy), result, error, e_gdbus_cal_call_remove_object);
+	return e_gdbus_proxy_finish_call_void (E_GDBUS_ASYNC_OP_KEEPER (proxy), result, error, e_gdbus_cal_call_remove_objects);
 }
 
 gboolean
-e_gdbus_cal_call_remove_object_sync (GDBusProxy *proxy,
-                                     const gchar * const *in_uid_rid_mod,
-                                     GCancellable *cancellable,
-                                     GError **error)
+e_gdbus_cal_call_remove_objects_sync (GDBusProxy *proxy,
+                                      const gchar * const *in_mod_ids,
+                                      GCancellable *cancellable,
+                                      GError **error)
 {
-	return e_gdbus_proxy_call_sync_strv__void (proxy, in_uid_rid_mod, cancellable, error,
-		e_gdbus_cal_call_remove_object,
-		e_gdbus_cal_call_remove_object_finish);
+	return e_gdbus_proxy_call_sync_strv__void (proxy, in_mod_ids, cancellable, error,
+		e_gdbus_cal_call_remove_objects,
+		e_gdbus_cal_call_remove_objects_finish);
 }
 
 void
@@ -1163,13 +1194,13 @@ DECLARE_EMIT_DONE_SIGNAL_1 (get_object_list,
                             const gchar * const *)
 DECLARE_EMIT_DONE_SIGNAL_0 (get_free_busy,
                             __GET_FREE_BUSY_DONE_SIGNAL)
-DECLARE_EMIT_DONE_SIGNAL_1 (create_object,
-                            __CREATE_OBJECT_DONE_SIGNAL,
-                            const gchar *)
-DECLARE_EMIT_DONE_SIGNAL_0 (modify_object,
-                            __MODIFY_OBJECT_DONE_SIGNAL)
-DECLARE_EMIT_DONE_SIGNAL_0 (remove_object,
-                            __REMOVE_OBJECT_DONE_SIGNAL)
+DECLARE_EMIT_DONE_SIGNAL_1 (create_objects,
+                            __CREATE_OBJECTS_DONE_SIGNAL,
+                            const gchar * const *)
+DECLARE_EMIT_DONE_SIGNAL_0 (modify_objects,
+                            __MODIFY_OBJECTS_DONE_SIGNAL)
+DECLARE_EMIT_DONE_SIGNAL_0 (remove_objects,
+                            __REMOVE_OBJECTS_DONE_SIGNAL)
 DECLARE_EMIT_DONE_SIGNAL_0 (receive_objects,
                             __RECEIVE_OBJECTS_DONE_SIGNAL)
 DECLARE_EMIT_DONE_SIGNAL_1 (send_objects,
@@ -1259,9 +1290,9 @@ E_DECLARE_GDBUS_ASYNC_METHOD_1			(cal, set_backend_property, propnamevalue, "as"
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(cal, get_object, uid_rid, "as", object, "s")
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(cal, get_object_list, sexp, "s", objects, "as")
 E_DECLARE_GDBUS_ASYNC_METHOD_1			(cal, get_free_busy, start_stop_users, "as")
-E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(cal, create_object, object, "s", uid, "s")
-E_DECLARE_GDBUS_ASYNC_METHOD_1			(cal, modify_object, object_mod, "as")
-E_DECLARE_GDBUS_ASYNC_METHOD_1			(cal, remove_object, uid_rid_mod, "as")
+E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(cal, create_objects, objects, "as", uids, "as")
+E_DECLARE_GDBUS_ASYNC_METHOD_1			(cal, modify_objects, object_mod, "as")
+E_DECLARE_GDBUS_ASYNC_METHOD_1			(cal, remove_objects, mod_ids, "as")
 E_DECLARE_GDBUS_ASYNC_METHOD_1			(cal, receive_objects, object, "s")
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(cal, send_objects, object, "s", object_users, "as")
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN	(cal, get_attachment_uris, uid_rid, "as", attachments, "as")
@@ -1287,9 +1318,9 @@ static const GDBusMethodInfo * const e_gdbus_cal_method_info_pointers[] =
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (cal, get_object),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (cal, get_object_list),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (cal, get_free_busy),
-	&E_DECLARED_GDBUS_METHOD_INFO_NAME (cal, create_object),
-	&E_DECLARED_GDBUS_METHOD_INFO_NAME (cal, modify_object),
-	&E_DECLARED_GDBUS_METHOD_INFO_NAME (cal, remove_object),
+	&E_DECLARED_GDBUS_METHOD_INFO_NAME (cal, create_objects),
+	&E_DECLARED_GDBUS_METHOD_INFO_NAME (cal, modify_objects),
+	&E_DECLARED_GDBUS_METHOD_INFO_NAME (cal, remove_objects),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (cal, receive_objects),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (cal, send_objects),
 	&E_DECLARED_GDBUS_METHOD_INFO_NAME (cal, get_attachment_uris),
@@ -1322,9 +1353,9 @@ static const GDBusSignalInfo * const e_gdbus_cal_signal_info_pointers[] =
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, get_object_done),
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, get_object_list_done),
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, get_free_busy_done),
-	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, create_object_done),
-	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, modify_object_done),
-	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, remove_object_done),
+	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, create_objects_done),
+	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, modify_objects_done),
+	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, remove_objects_done),
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, receive_objects_done),
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, send_objects_done),
 	&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, get_attachment_uris_done),
@@ -1561,9 +1592,9 @@ e_gdbus_cal_proxy_init (EGdbusCalProxy *proxy)
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRING (get_object);
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRV   (get_object_list);
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID   (get_free_busy);
-	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRING (create_object);
-	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID   (modify_object);
-	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID   (remove_object);
+	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRV   (create_objects);
+	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID   (modify_objects);
+	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID   (remove_objects);
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID   (receive_objects);
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRV   (send_objects);
 	E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRV   (get_attachment_uris);
diff --git a/calendar/libegdbus/e-gdbus-cal.h b/calendar/libegdbus/e-gdbus-cal.h
index 695d13b..7220f64 100644
--- a/calendar/libegdbus/e-gdbus-cal.h
+++ b/calendar/libegdbus/e-gdbus-cal.h
@@ -136,14 +136,14 @@ struct _EGdbusCalIface
 	gboolean (*handle_get_free_busy)		(EGdbusCal *object, GDBusMethodInvocation *invocation, const gchar * const *in_start_end_userlist);
 	void	 (*get_free_busy_done)			(EGdbusCal *object, guint arg_opid, const GError *arg_error);
 
-	gboolean (*handle_create_object)		(EGdbusCal *object, GDBusMethodInvocation *invocation, const gchar *in_calobj);
-	void	 (*create_object_done)			(EGdbusCal *object, guint arg_opid, const GError *arg_error, gchar **out_uid);
+	gboolean (*handle_create_objects)		(EGdbusCal *object, GDBusMethodInvocation *invocation, const gchar *const *in_calobjs);
+	void	 (*create_objects_done)			(EGdbusCal *object, guint arg_opid, const GError *arg_error, gchar ***out_uids);
 
-	gboolean (*handle_modify_object)		(EGdbusCal *object, GDBusMethodInvocation *invocation, const gchar * const *in_calobj_mod);
-	void	 (*modify_object_done)			(EGdbusCal *object, guint arg_opid, const GError *arg_error);
+	gboolean (*handle_modify_objects)		(EGdbusCal *object, GDBusMethodInvocation *invocation, const gchar * const *in_mod_calobjs);
+	void	 (*modify_objects_done)			(EGdbusCal *object, guint arg_opid, const GError *arg_error);
 
-	gboolean (*handle_remove_object)		(EGdbusCal *object, GDBusMethodInvocation *invocation, const gchar * const *in_uid_rid_mod);
-	void	 (*remove_object_done)			(EGdbusCal *object, guint arg_opid, const GError *arg_error);
+	gboolean (*handle_remove_objects)		(EGdbusCal *object, GDBusMethodInvocation *invocation, const gchar * const *in_mod_ids);
+	void	 (*remove_objects_done)			(EGdbusCal *object, guint arg_opid, const GError *arg_error);
 
 	gboolean (*handle_receive_objects)		(EGdbusCal *object, GDBusMethodInvocation *invocation, const gchar *in_calobj);
 	void	 (*receive_objects_done)		(EGdbusCal *object, guint arg_opid, const GError *arg_error);
@@ -211,21 +211,21 @@ void		e_gdbus_cal_call_get_free_busy			(GDBusProxy *proxy, const gchar * const *
 gboolean	e_gdbus_cal_call_get_free_busy_finish		(GDBusProxy *proxy, GAsyncResult *result, GError **error);
 gboolean	e_gdbus_cal_call_get_free_busy_sync		(GDBusProxy *proxy, const gchar * const *in_start_end_userlist, GCancellable *cancellable, GError **error);
 
-void		e_gdbus_cal_call_create_object			(GDBusProxy *proxy, const gchar *in_calobj, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
-gboolean	e_gdbus_cal_call_create_object_finish		(GDBusProxy *proxy, GAsyncResult *result, gchar **out_uid, GError **error);
-gboolean	e_gdbus_cal_call_create_object_sync		(GDBusProxy *proxy, const gchar *in_calobj, gchar **out_uid, GCancellable *cancellable, GError **error);
+void		e_gdbus_cal_call_create_objects			(GDBusProxy *proxy, const gchar * const *in_calobjs, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+gboolean	e_gdbus_cal_call_create_objects_finish	(GDBusProxy *proxy, GAsyncResult *result, gchar ***out_uids, GError **error);
+gboolean	e_gdbus_cal_call_create_objects_sync	(GDBusProxy *proxy, const gchar * const *in_calobjs, gchar ***out_uids, GCancellable *cancellable, GError **error);
 
-gchar **	e_gdbus_cal_encode_modify_object		(const gchar *in_calobj, guint in_mod);
-gboolean	e_gdbus_cal_decode_modify_object		(const gchar * const *in_strv, gchar **out_calobj, guint *out_mod);
-void		e_gdbus_cal_call_modify_object			(GDBusProxy *proxy, const gchar * const *in_calobj_mod, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
-gboolean	e_gdbus_cal_call_modify_object_finish		(GDBusProxy *proxy, GAsyncResult *result, GError **error);
-gboolean	e_gdbus_cal_call_modify_object_sync		(GDBusProxy *proxy, const gchar * const *in_calobj_mod, GCancellable *cancellable, GError **error);
+gchar **	e_gdbus_cal_encode_modify_objects		(const GSList *in_calobjs, guint in_mod);
+gboolean	e_gdbus_cal_decode_modify_objects		(const gchar * const *in_strv, GSList **out_calobjs, guint *out_mod);
+void		e_gdbus_cal_call_modify_objects			(GDBusProxy *proxy, const gchar * const *in_mod_calobjs, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+gboolean	e_gdbus_cal_call_modify_objects_finish	(GDBusProxy *proxy, GAsyncResult *result, GError **error);
+gboolean	e_gdbus_cal_call_modify_objects_sync	(GDBusProxy *proxy, const gchar * const *in_mod_calobjs, GCancellable *cancellable, GError **error);
 
-gchar **	e_gdbus_cal_encode_remove_object		(const gchar *in_uid, const gchar *in_rid, guint in_mod);
-gboolean	e_gdbus_cal_decode_remove_object		(const gchar * const *in_strv, gchar **out_uid, gchar **out_rid, guint *out_mod);
-void		e_gdbus_cal_call_remove_object			(GDBusProxy *proxy, const gchar * const *in_uid_rid_mod, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
-gboolean	e_gdbus_cal_call_remove_object_finish		(GDBusProxy *proxy, GAsyncResult *result, GError **error);
-gboolean	e_gdbus_cal_call_remove_object_sync		(GDBusProxy *proxy, const gchar * const *in_uid_rid_mod, GCancellable *cancellable, GError **error);
+gchar **	e_gdbus_cal_encode_remove_objects		(const GSList *in_ids, guint in_mod);
+gboolean	e_gdbus_cal_decode_remove_objects		(const gchar * const *in_strv, GSList **out_ids, guint *out_mod);
+void		e_gdbus_cal_call_remove_objects			(GDBusProxy *proxy, const gchar * const *in_mod_ids, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+gboolean	e_gdbus_cal_call_remove_objects_finish	(GDBusProxy *proxy, GAsyncResult *result, GError **error);
+gboolean	e_gdbus_cal_call_remove_objects_sync	(GDBusProxy *proxy, const gchar * const *in_mod_ids, GCancellable *cancellable, GError **error);
 
 void		e_gdbus_cal_call_receive_objects		(GDBusProxy *proxy, const gchar *in_calobj, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 gboolean	e_gdbus_cal_call_receive_objects_finish		(GDBusProxy *proxy, GAsyncResult *result, GError **error);
@@ -286,9 +286,9 @@ gboolean	e_gdbus_cal_call_close_sync			(GDBusProxy *proxy, GCancellable *cancell
 #define e_gdbus_cal_complete_get_object			e_gdbus_complete_async_method
 #define e_gdbus_cal_complete_get_object_list		e_gdbus_complete_async_method
 #define e_gdbus_cal_complete_get_free_busy		e_gdbus_complete_async_method
-#define e_gdbus_cal_complete_create_object		e_gdbus_complete_async_method
-#define e_gdbus_cal_complete_modify_object		e_gdbus_complete_async_method
-#define e_gdbus_cal_complete_remove_object		e_gdbus_complete_async_method
+#define e_gdbus_cal_complete_create_objects		e_gdbus_complete_async_method
+#define e_gdbus_cal_complete_modify_objects		e_gdbus_complete_async_method
+#define e_gdbus_cal_complete_remove_objects		e_gdbus_complete_async_method
 #define e_gdbus_cal_complete_receive_objects		e_gdbus_complete_async_method
 #define e_gdbus_cal_complete_send_objects		e_gdbus_complete_async_method
 #define e_gdbus_cal_complete_get_attachment_uris	e_gdbus_complete_async_method
@@ -310,9 +310,9 @@ void e_gdbus_cal_emit_get_object_done			(EGdbusCal *object, guint arg_opid, cons
 void e_gdbus_cal_emit_get_object_list_done		(EGdbusCal *object, guint arg_opid, const GError *arg_error, const gchar * const *out_objects);
 void e_gdbus_cal_emit_get_free_busy_done		(EGdbusCal *object, guint arg_opid, const GError *arg_error);
 void e_gdbus_cal_emit_get_free_busy_data		(EGdbusCal *object, guint arg_opid, const GError *arg_error, const gchar * const *out_freebusy);
-void e_gdbus_cal_emit_create_object_done		(EGdbusCal *object, guint arg_opid, const GError *arg_error, const gchar *out_uid);
-void e_gdbus_cal_emit_modify_object_done		(EGdbusCal *object, guint arg_opid, const GError *arg_error);
-void e_gdbus_cal_emit_remove_object_done		(EGdbusCal *object, guint arg_opid, const GError *arg_error);
+void e_gdbus_cal_emit_create_objects_done		(EGdbusCal *object, guint arg_opid, const GError *arg_error, const gchar * const *out_uids);
+void e_gdbus_cal_emit_modify_objects_done		(EGdbusCal *object, guint arg_opid, const GError *arg_error);
+void e_gdbus_cal_emit_remove_objects_done		(EGdbusCal *object, guint arg_opid, const GError *arg_error);
 void e_gdbus_cal_emit_receive_objects_done		(EGdbusCal *object, guint arg_opid, const GError *arg_error);
 void e_gdbus_cal_emit_send_objects_done			(EGdbusCal *object, guint arg_opid, const GError *arg_error, const gchar * const *out_calobj_users);
 void e_gdbus_cal_emit_get_attachment_uris_done		(EGdbusCal *object, guint arg_opid, const GError *arg_error, const gchar * const *out_attachments);
diff --git a/configure.ac b/configure.ac
index d9a9e33..ad9bac2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -61,7 +61,7 @@ dnl ******************************
 dnl D-Bus versioning
 dnl ******************************
 ADDRESS_BOOK_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.AddressBook3"
-CALENDAR_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.Calendar1"
+CALENDAR_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.Calendar2"
 
 AC_DEFINE_UNQUOTED(
        ADDRESS_BOOK_DBUS_SERVICE_NAME,
@@ -87,11 +87,11 @@ LIBEDATASERVERUI_CURRENT=1
 LIBEDATASERVERUI_REVISION=0
 LIBEDATASERVERUI_AGE=0
 
-LIBECAL_CURRENT=13
+LIBECAL_CURRENT=14
 LIBECAL_REVISION=2
 LIBECAL_AGE=2
 
-LIBEDATACAL_CURRENT=15
+LIBEDATACAL_CURRENT=16
 LIBEDATACAL_REVISION=0
 LIBEDATACAL_AGE=0
 
diff --git a/libedataserver/e-data-server-util.c b/libedataserver/e-data-server-util.c
index c7211a2..a608fbd 100644
--- a/libedataserver/e-data-server-util.c
+++ b/libedataserver/e-data-server-util.c
@@ -947,6 +947,26 @@ e_util_free_object_slist (GSList *objects)
 }
 
 /**
+ * e_util_free_nullable_object_slist:
+ * @objects: a #GSList of nullable #GObject-s
+ *
+ * Calls g_object_unref() on each member of @objects if non-NULL and then frees
+ * also @objects itself.
+ *
+ * Since: 3.6
+ **/
+void
+e_util_free_nullable_object_slist (GSList *objects)
+{
+	const GSList *l;
+	for (l = objects; l; l = l->next) {
+		if (l->data)
+			g_object_unref (l->data);
+	}
+	g_slist_free (objects);
+}
+
+/**
  * e_binding_transform_enum_value_to_nick:
  * @binding: a #GBinding
  * @source_value: a #GValue whose type is derived from #G_TYPE_ENUM
diff --git a/libedataserver/e-data-server-util.h b/libedataserver/e-data-server-util.h
index 8450c63..b68c460 100644
--- a/libedataserver/e-data-server-util.h
+++ b/libedataserver/e-data-server-util.h
@@ -69,6 +69,7 @@ GSList *	e_util_copy_string_slist	(GSList *copy_to, const GSList *strings);
 GSList *	e_util_copy_object_slist	(GSList *copy_to, const GSList *objects);
 void		e_util_free_string_slist	(GSList *strings);
 void		e_util_free_object_slist	(GSList *objects);
+void		e_util_free_nullable_object_slist	(GSList *objects);
 
 /* Useful GBinding transform functions */
 gboolean	e_binding_transform_enum_value_to_nick
diff --git a/tests/libecal/client/Makefile.am b/tests/libecal/client/Makefile.am
index 68037ca..af2b893 100644
--- a/tests/libecal/client/Makefile.am
+++ b/tests/libecal/client/Makefile.am
@@ -31,6 +31,7 @@ TESTS = 					\
 	test-client-get-revision		\
 	test-client-send-objects		\
 	test-client-receive-objects		\
+        test-client-bulk-methods		\
 	test-client-get-attachment-uris		\
 	test-client-get-view			\
 	test-client-revision-view		\
@@ -77,6 +78,8 @@ test_client_open_LDADD=$(TEST_LIBS)
 test_client_open_CPPFLAGS=$(TEST_CPPFLAGS)
 test_client_receive_objects_LDADD=$(TEST_LIBS)
 test_client_receive_objects_CPPFLAGS=$(TEST_CPPFLAGS)
+test_client_bulk_methods_LDADD=$(TEST_LIBS)
+test_client_bulk_methods_CPPFLAGS=$(TEST_CPPFLAGS)
 test_client_refresh_LDADD=$(TEST_LIBS)
 test_client_refresh_CPPFLAGS=$(TEST_CPPFLAGS)
 test_client_remove_object_LDADD=$(TEST_LIBS)
diff --git a/tests/libecal/client/test-client-bulk-methods.c b/tests/libecal/client/test-client-bulk-methods.c
new file mode 100644
index 0000000..c4d9069
--- /dev/null
+++ b/tests/libecal/client/test-client-bulk-methods.c
@@ -0,0 +1,253 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <libecal/e-cal-client.h>
+#include <libical/ical.h>
+
+#include "client-test-utils.h"
+
+#define NB_COMPONENTS 5
+
+static gboolean
+test_icalcomps (icalcomponent *icalcomp1,
+				icalcomponent *icalcomp2)
+{
+	struct icaltimetype t1, t2;
+
+	if (!icalcomp2) {
+		g_printerr ("Failure: get object returned NULL\n");
+		return FALSE;
+	}
+
+	if (g_strcmp0 (icalcomponent_get_uid (icalcomp1), icalcomponent_get_uid (icalcomp2)) != 0) {
+		g_printerr ("Failure: uid doesn't match, expected '%s', got '%s'\n", icalcomponent_get_uid (icalcomp1), icalcomponent_get_uid (icalcomp2));
+		return FALSE;
+	}
+
+	if (g_strcmp0 (icalcomponent_get_summary (icalcomp1), icalcomponent_get_summary (icalcomp2)) != 0) {
+		g_printerr ("Failure: summary doesn't match, expected '%s', got '%s'\n", icalcomponent_get_summary (icalcomp1), icalcomponent_get_summary (icalcomp2));
+		return FALSE;
+	}
+
+	t1 = icalcomponent_get_dtstart (icalcomp1);
+	t2 = icalcomponent_get_dtstart (icalcomp2);
+
+	if (icaltime_compare (t1, t2) != 0) {
+		g_printerr ("Failure: dtend doesn't match, expected '%s', got '%s'\n", icaltime_as_ical_string (t1), icaltime_as_ical_string (t2));
+		return FALSE;
+	}
+
+	t1 = icalcomponent_get_dtend (icalcomp1);
+	t2 = icalcomponent_get_dtend (icalcomp2);
+
+	if (icaltime_compare (t1, t2) != 0) {
+		g_printerr ("Failure: dtend doesn't match, expected '%s', got '%s'\n", icaltime_as_ical_string (t1), icaltime_as_ical_string (t2));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+check_removed (ECalClient *cal_client,
+			   const GSList *uids)
+{
+	g_return_val_if_fail (cal_client != NULL, FALSE);
+	g_return_val_if_fail (uids != NULL, FALSE);
+
+	while (uids) {
+		GError *error = NULL;
+		icalcomponent *icalcomp = NULL;
+
+		if (!e_cal_client_get_object_sync(cal_client, uids->data, NULL, &icalcomp, NULL, &error) &&
+				g_error_matches (error, E_CAL_CLIENT_ERROR, E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND)) {
+			g_clear_error (&error);
+		} else {
+			report_error ("check objects removed sync", &error);
+			icalcomponent_free (icalcomp);
+			return FALSE;
+		}
+
+		uids = uids->next;
+	}
+
+	return TRUE;
+}
+
+static GSList *
+uid_slist_to_ecalcomponentid_slist(GSList *uids)
+{
+	GSList *ids = NULL;
+	const GSList *l;
+
+	for (l = uids; l; l = l->next) {
+		ECalComponentId *id = g_new0 (ECalComponentId, 1);
+		id->uid = g_strdup (l->data);
+		ids = g_slist_append (ids, id);
+	}
+
+	return ids;
+}
+
+static gboolean
+check_icalcomps_exist (ECalClient *cal_client,
+					   GSList *icalcomps)
+{
+	const GSList *l;
+
+	for (l = icalcomps; l; l = l->next) {
+		GError *error = NULL;
+		icalcomponent *icalcomp = l->data;
+		icalcomponent *icalcomp2 = NULL;
+		const char *uid = icalcomponent_get_uid(icalcomp);
+
+		if (!e_cal_client_get_object_sync(cal_client, uid, NULL, &icalcomp2, NULL, &error)) {
+			report_error ("get object sync", &error);
+			return FALSE;
+		}
+
+		g_return_val_if_fail (icalcomp2 != NULL, FALSE);
+
+		if (!test_icalcomps (icalcomp, icalcomp2)) {
+			icalcomponent_free (icalcomp2);
+			return FALSE;
+		}
+
+		icalcomponent_free (icalcomp2);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+test_bulk_methods(GSList *icalcomps)
+{
+	ECalClient *cal_client;
+	GError *error = NULL;
+	GSList *uids = NULL, *ids = NULL;
+	const GSList *lcomp, *luid;
+	gint i = 0;
+
+	g_return_val_if_fail (icalcomps != NULL, FALSE);
+
+	cal_client = new_temp_client (E_CAL_CLIENT_SOURCE_TYPE_EVENTS, NULL);
+	g_return_val_if_fail (cal_client != NULL, FALSE);
+
+	if (!e_client_open_sync (E_CLIENT (cal_client), FALSE, NULL, &error)) {
+		report_error ("client open sync", &error);
+		g_object_unref (cal_client);
+		return FALSE;
+	}
+
+	/* Create all the objects in bulk */
+	if (!e_cal_client_create_objects_sync (cal_client, icalcomps, &uids, NULL, &error)) {
+		report_error ("create objects sync", &error);
+		g_object_unref (cal_client);
+		return FALSE;
+	}
+
+	g_return_val_if_fail (uids != NULL, FALSE);
+	g_return_val_if_fail (g_slist_length (uids) == NB_COMPONENTS, FALSE);
+
+	/* Update icalcomponents uids */
+	luid = uids;
+	lcomp = icalcomps;
+	while (luid && lcomp) {
+		icalcomponent_set_uid (lcomp->data, luid->data);
+		luid = luid->next;
+		lcomp = lcomp->next;
+	}
+
+	/* Retrieve all the objects and check that they are the same */
+	if (!check_icalcomps_exist (cal_client, icalcomps)) {
+		g_object_unref (cal_client);
+		g_slist_free_full (uids, g_free);
+		return FALSE;
+	}
+
+	/* Modify the objects */
+	for (lcomp = icalcomps; lcomp; lcomp = lcomp->next) {
+		gchar *summary;
+		icalcomponent *icalcomp = lcomp->data;
+
+		summary = g_strdup_printf ("Edited test summary %d", i);
+		icalcomponent_set_summary(icalcomp, summary);
+
+		g_free (summary);
+		++i;
+	}
+
+	/* Save the modified objects in bulk */
+	if (!e_cal_client_modify_objects_sync(cal_client, icalcomps, CALOBJ_MOD_ALL, NULL, &error)) {
+		report_error ("modify objects sync", &error);
+		g_object_unref (cal_client);
+		g_slist_free_full (uids, g_free);
+		return FALSE;
+	}
+
+	/* Retrieve all the objects and check that they have been modified */
+	if (!check_icalcomps_exist (cal_client, icalcomps)) {
+		g_object_unref (cal_client);
+		g_slist_free_full (uids, g_free);
+		return FALSE;
+	}
+
+	/* Remove all the objects in bulk */
+	ids = uid_slist_to_ecalcomponentid_slist (uids);
+
+	if (!e_cal_client_remove_objects_sync(cal_client, ids, CALOBJ_MOD_ALL, NULL, &error)) {
+		report_error ("remove objects sync", &error);
+		g_object_unref (cal_client);
+		g_slist_free_full (ids, (GDestroyNotify) e_cal_component_free_id);
+		g_slist_free_full (uids, g_free);
+		return FALSE;
+	}
+	g_slist_free_full (ids, (GDestroyNotify) e_cal_component_free_id);
+
+	/* Check that the objects don't exist anymore */
+	if (!check_removed(cal_client, uids)) {
+		g_object_unref (cal_client);
+		g_slist_free_full (uids, g_free);
+		return FALSE;
+	}
+
+	g_slist_free_full (uids, g_free);
+	g_object_unref (cal_client);
+	return TRUE;
+}
+
+gint
+main (gint argc,
+	  gchar **argv)
+{
+	GSList *icalcomps = NULL;
+	struct icaltimetype now;
+	gint i;
+	gboolean res;
+
+	main_initialize ();
+
+	now = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
+
+	/* Build up new components */
+	for (i = 0; i < NB_COMPONENTS; ++i) {
+		icalcomponent *icalcomp;
+		gchar *summary;
+
+		icalcomp = icalcomponent_new (ICAL_VEVENT_COMPONENT);
+		summary = g_strdup_printf ("Test summary %d", i);
+		icalcomponent_set_summary (icalcomp, summary);
+		icalcomponent_set_dtstart (icalcomp, now);
+		icalcomponent_set_dtend   (icalcomp, icaltime_from_timet (icaltime_as_timet (now) + 60 * 60 * 60, 0));
+
+		icalcomps = g_slist_append (icalcomps, icalcomp);
+		g_free (summary);
+	}
+
+	/* Test synchronous bulk methods */
+	res = test_bulk_methods (icalcomps);
+
+	g_slist_free_full (icalcomps, (GDestroyNotify)icalcomponent_free);
+
+	return (res != TRUE);
+}



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