[PATCH 3/3] ecal atomic updates and deletes: check and return revision



Same API change as for ebook, using the LAST-MODIFIED
property instead of REV.

The current API suffers from multiple race conditions: when
overwriting an item, another client might have already made
an update that gets overwritten by older data. Removing an
item is also affected => added an API extension that
allows backends a) to detect that clients want the more
careful data modifications and b) provides enough information
to do the checking based on the revision string in the backend
(CAL_STATIC_CAPABILITY_ATOMIC_MODIFICATIONS).

Software that does change tracking based on the last-modified string
(like SyncEvolution) needs to know which string was
assigned to an updated or added item. Currently it must do
the operation, then ask for the whole item. There is a small
window for a race condition here, but more important, this
requires another round-trip and transmits too much information.
With CAL_STATIC_CAPABILITY_RETURN_LAST_MODIFIED the client can be sure
to get the necessary information right away.
---
 calendar/libecal/e-cal-util.h |    6 +++
 calendar/libecal/e-cal.c      |   91 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 97 insertions(+), 0 deletions(-)

diff --git a/calendar/libecal/e-cal-util.h b/calendar/libecal/e-cal-util.h
index a369b59..be9b910 100644
--- a/calendar/libecal/e-cal-util.h
+++ b/calendar/libecal/e-cal-util.h
@@ -128,6 +128,12 @@ gboolean e_cal_util_event_dates_match (icalcomponent *icalcomp1, icalcomponent *
 #define CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY		 "delegate-to-many"
 #define CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING     "has-unaccepted-meeting"
 
+/** operations where the backend updates an item's last-modified return the new value */
+#define CAL_STATIC_CAPABILITY_RETURN_LAST_MODIFIED	 "return-last-modified"
+
+/** backend supports atomic update/delete operations (the _instance variant of the calls) */
+#define CAL_STATIC_CAPABILITY_ATOMIC_MODIFICATIONS	 "atomic-modifications"
+
 /* Recurrent events. Management for instances */
 icalcomponent *e_cal_util_construct_instance (icalcomponent *icalcomp,
 					    struct icaltimetype rid);
diff --git a/calendar/libecal/e-cal.c b/calendar/libecal/e-cal.c
index 9603a52..9b0b1b4 100644
--- a/calendar/libecal/e-cal.c
+++ b/calendar/libecal/e-cal.c
@@ -4245,6 +4245,14 @@ e_cal_get_component_as_string (ECal *ecal, icalcomponent *icalcomp)
  * argument. Some backends would assign a specific UID to the newly created object,
  * in those cases that UID would be returned in the @uid argument.
  *
+ * If the backend has the #CAL_STATIC_CAPABILITY_RETURN_LAST_MODIFIED
+ * capability, then LAST-MODIFIED will be set to the value assigned by
+ * the backend. The value assigned to it when calling the function is
+ * ignored. In the somewhat unusual situation that an object has to be
+ * created with a specific LAST-MODIFIED value, create it and then
+ * update it with
+ * e_cal_modify_object_instance(flags=E_CAL_SET_LAST_MODIFIED)
+ *
  * Return value: TRUE if the operation was successful, FALSE otherwise.
  */
 gboolean
@@ -4387,6 +4395,51 @@ e_cal_modify_object (ECal *ecal, icalcomponent *icalcomp, CalObjModType mod, GEr
 }
 
 /**
+ * e_cal_modify_object_instance:
+ * @ecal: A calendar client.
+ * @icalcomp: Component to modify.
+ * @mod: Type of modification.
+ * @flags: #E_CAL_IGNORE_REVISION, #E_CAL_CHECK_REVISION, or #E_CAL_SET_REVISION
+ * @error: Placeholder for error information.
+ *
+ * Requests the calendar backend to modify an existing object. If the 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 handling of the last-modified value set for @icalcomp depends on
+ * @flags. With #E_CAL_IGNORE_REVISION, the value is ignored.
+ *
+ * With #E_CAL_CHECK_REVISION, the string must match the current one
+ * at the time when the backend processes the request. When it does
+ * not match, the commit fails. Using this is recommended to avoid
+ * overwriting changes that others might have made in the
+ * meantime. With CALOBJ_MOD_THIS, the value is compared against the
+ * selected object (main event if recurrence-id is empty, otherwise
+ * the detached recurrence). Otherwise it is always compared against
+ * the main event.
+ *
+ * With #E_CAL_SET_REVISION, the object is stored with the given
+ * last-modified value, if possible. If this is not possible, then a
+ * new value is assigned. Use this when restoring data from a backup.
+ *
+ * If the backend has the
+ * #CAL_STATIC_CAPABILITY_RETURN_LAST_MODIFIED, then the
+ * last-modified value assigned to the object is guaranteed to be set
+ * in @icalcomp when the function returns.
+ *
+ * Return value: TRUE if the operation was successful, FALSE otherwise.
+ */
+gboolean
+e_cal_modify_object_instance (ECal *ecal, icalcomponent *icalcomp, int flags, GError **error)
+{
+	return FALSE;
+}
+
+/**
  * e_cal_remove_object_with_mod:
  * @ecal: A calendar client.
  * @uid: UID og the object to remove.
@@ -4463,6 +4516,44 @@ e_cal_remove_object_with_mod (ECal *ecal, const char *uid,
 }
 
 /**
+ * e_cal_remove_object_instance:
+ * @ecal: A calendar client.
+ * @uid: UID og the object to remove.
+ * @rid: Recurrence ID of the specific recurrence to remove.
+ * @last_modified: Expected LAST-MODIFIED value, NULL allowed
+ * @mod: Type of removal.
+ * @error: Placeholder for error information.
+ *
+ * This function allows the removal of instances of a recurrent
+ * appointment. By using a combination of the @uid, @rid and @mod
+ * arguments, you can remove specific instances. If what you want
+ * is to remove all instances, use e_cal_remove_object instead.
+ *
+ * If not all instances are removed, the client will get a "obj_modified"
+ * signal, while it will get a "obj_removed" signal when all instances
+ * are removed.
+ *
+ * If @last_modified is non-NULL, then removing the object(s) will
+ * fail with #E_CAL_ERROR_PERMISSION_DENIED if the it does not match
+ * the value of the object at the time when the backend tries to
+ * remove the object.  The reference value in the backend is the same
+ * as for e_cal_modify_object_instance().
+ *
+ * If it is non-NULL and the backend does not support last-modified
+ * checking, #E_CAL_ERROR_PROTOCOL_NOT_SUPPORTED is returned. 
+ *
+ * Return value: TRUE if the operation was successful, FALSE otherwise.
+ */
+gboolean
+e_cal_remove_object_instance (ECal *ecal, const char *uid,
+			      const char *rid,
+			      const char *last_modified,
+			      CalObjModType mod, GError **error)
+{
+	return FALSE;
+}
+
+/**
  * e_cal_remove_object:
  * @ecal:  A calendar client.
  * @uid: Unique identifier of the calendar component to remove.
-- 
1.6.2


--=-qEVQ6ANjL886xlj8XDOe--



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