commit 25ddd82dfdfffbfc36ae8eb11ff4711aa29db8bb
Author: Tristan Van Berkom <tristan.van.berkom@gmail.com>
Date:   Tue Nov 22 17:58:30 2011 +0900

    Bug #652177 - Add "revision" property to calendar file backend
    
    Added "revision" property also now as a common property definition
    in e-client.h. revisions are guarunteed to remain the same until there
    is a modification to the calendar.

diff --git a/addressbook/libebook/e-book-client.h b/addressbook/libebook/e-book-client.h
index 37bf5d8..3ebac76 100644
--- a/addressbook/libebook/e-book-client.h
+++ b/addressbook/libebook/e-book-client.h
@@ -66,17 +66,6 @@ G_BEGIN_DECLS
 #define BOOK_BACKEND_PROPERTY_SUPPORTED_AUTH_METHODS	"supported-auth-methods"
 
 /**
- * BOOK_BACKEND_PROPERTY_REVISION:
- *
- * The current overall revision string, this can be used as
- * a quick check to see if data has changed at all since the
- * last time the addressbook revision was observed.
- *
- * Since: 3.4
- **/
-#define BOOK_BACKEND_PROPERTY_REVISION			"revision"
-
-/**
  * E_BOOK_CLIENT_ERROR:
  *
  * FIXME: Document me.
diff --git a/calendar/backends/file/e-cal-backend-file.c b/calendar/backends/file/e-cal-backend-file.c
index 5e7d060..5f77a19 100644
--- a/calendar/backends/file/e-cal-backend-file.c
+++ b/calendar/backends/file/e-cal-backend-file.c
@@ -52,6 +52,8 @@
 #define EDC_ERROR(_code) e_data_cal_create_error (_code, NULL)
 #define EDC_ERROR_NO_URI() e_data_cal_create_error (OtherError, "Cannot get URI")
 
+#define ECAL_REVISION_X_PROP  "X-EVOLUTION-DATA-REVISION"
+
 G_DEFINE_TYPE (ECalBackendFile, e_cal_backend_file, E_TYPE_CAL_BACKEND_SYNC)
 
 /* Placeholder for each component and its recurrences */
@@ -112,6 +114,9 @@ struct _ECalBackendFilePrivate {
 
 	/* timeour id for refresh type "2" */
 	guint refresh_timeout_id;
+
+	/* Just an incremental number to ensure uniqueness across revisions */
+	guint revision_counter;
 };
 
 
@@ -128,6 +133,8 @@ static void free_refresh_data (ECalBackendFile *cbfile);
 static icaltimezone *
 e_cal_backend_file_internal_get_timezone (ECalBackend *backend, const gchar *tzid);
 
+static void bump_revision (ECalBackendFile *cbfile);
+
 /* g_hash_table_foreach() callback to destroy a ECalBackendFileObject */
 static void
 free_object_data (gpointer data)
@@ -257,10 +264,14 @@ save_file_when_idle (gpointer user_data)
 }
 
 static void
-save (ECalBackendFile *cbfile)
+save (ECalBackendFile *cbfile,
+      gboolean         do_bump_revision)
 {
 	ECalBackendFilePrivate *priv;
 
+	if (do_bump_revision)
+	  bump_revision (cbfile);
+
 	priv = cbfile->priv;
 
 	g_static_rec_mutex_lock (&priv->idle_save_rmutex);
@@ -393,6 +404,82 @@ uid_in_use (ECalBackendFile *cbfile,
 
 
 
+static icalproperty *
+get_revision_property (ECalBackendFile *cbfile)
+{
+       ECalBackendFilePrivate *priv;
+       icalproperty           *prop;
+
+       priv = cbfile->priv;
+       prop = icalcomponent_get_first_property (priv->icalcomp, ICAL_X_PROPERTY);
+
+       while (prop != NULL) {
+	      const gchar *name = icalproperty_get_x_name (prop);
+
+	      if (name && strcmp (name, ECAL_REVISION_X_PROP) == 0)
+		     return prop;
+
+	      prop = icalcomponent_get_next_property (priv->icalcomp, ICAL_X_PROPERTY);
+       }
+
+       return NULL;
+}
+
+static gchar *
+make_revision_string (ECalBackendFile *cbfile)
+{
+       GTimeVal timeval;
+       gchar   *datestr;
+       gchar   *revision;
+
+       g_get_current_time (&timeval);
+
+       datestr = g_time_val_to_iso8601 (&timeval);
+       revision = g_strdup_printf ("%s(%d)", datestr, cbfile->priv->revision_counter++);
+
+       g_free (datestr);
+       return revision;
+}
+
+static icalproperty *
+ensure_revision (ECalBackendFile *cbfile)
+{
+       icalproperty* prop;
+
+       prop = get_revision_property (cbfile);
+
+       if (!prop) {
+	      gchar *revision = make_revision_string (cbfile);
+
+	      prop = icalproperty_new (ICAL_X_PROPERTY);
+
+	      icalproperty_set_x_name (prop, ECAL_REVISION_X_PROP);
+	      icalproperty_set_x (prop, revision);
+
+	      icalcomponent_add_property (cbfile->priv->icalcomp, prop);
+
+	      g_free (revision);
+       }
+
+       return prop;
+}
+
+static void
+bump_revision (ECalBackendFile *cbfile)
+{
+       /* Update the revision string */
+       icalproperty *prop     = ensure_revision (cbfile);
+       gchar        *revision = make_revision_string (cbfile);
+
+       icalproperty_set_x (prop, revision);
+
+       e_cal_backend_notify_property_changed (E_CAL_BACKEND (cbfile),
+					      CAL_BACKEND_PROPERTY_REVISION,
+					      revision);
+
+       g_free (revision);
+}
+
 /* Calendar backend methods */
 
 /* Get_email_address handler for the file backend */
@@ -444,6 +531,14 @@ e_cal_backend_file_get_backend_property (ECalBackendSync *backend,
 
 		*prop_value = e_cal_component_get_as_string (comp);
 		g_object_unref (comp);
+	} else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_REVISION)) {
+	       icalproperty *prop;
+	       const gchar  *revision;
+
+	       prop     = ensure_revision (E_CAL_BACKEND_FILE (backend));
+	       revision = icalproperty_get_x (prop);
+
+	       *prop_value = g_strdup (revision);
 	} else {
 		processed = FALSE;
 	}
@@ -522,7 +617,7 @@ check_dup_uid (ECalBackendFile *cbfile,
 	 * CREATED/DTSTAMP/LAST-MODIFIED.
 	 */
 
-	save (cbfile);
+	save (cbfile, FALSE);
 
  done:
 	g_free (rid);
@@ -737,7 +832,7 @@ remove_component (ECalBackendFile *cbfile,
 
 	g_hash_table_remove (priv->comp_uid_hash, uid);
 
-	save (cbfile);
+	save (cbfile, TRUE);
 }
 
 /* Scans the toplevel VCALENDAR component and stores the objects it finds */
@@ -1224,7 +1319,7 @@ create_cal (ECalBackendFile *cbfile,
 
 	priv->path = uri_to_path (E_CAL_BACKEND (cbfile));
 
-	save (cbfile);
+	save (cbfile, TRUE);
 
 	g_free (priv->custom_file);
 	priv->custom_file = g_strdup (uristr);
@@ -1573,7 +1668,8 @@ e_cal_backend_file_add_timezone (ECalBackendSync *backend,
 		if (!icalcomponent_get_timezone (priv->icalcomp,
 						 icaltimezone_get_tzid (zone))) {
 			icalcomponent_add_component (priv->icalcomp, tz_comp);
-			save (cbfile);
+
+			save (cbfile, TRUE);
 		}
 		g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
 
@@ -2273,7 +2369,7 @@ e_cal_backend_file_create_object (ECalBackendSync *backend,
 	add_component (cbfile, comp, TRUE);
 
 	/* Save the file */
-	save (cbfile);
+	save (cbfile, TRUE);
 
 	/* Return the UID and the modified component */
 	if (uid)
@@ -2423,7 +2519,7 @@ e_cal_backend_file_modify_object (ECalBackendSync *backend,
 						     e_cal_component_get_icalcomponent (obj_data->full_object));
 			priv->comp = g_list_prepend (priv->comp, obj_data->full_object);
 
-			save (cbfile);
+			save (cbfile, TRUE);
 
 			if (new_component) {
 				icalcomponent *new_icalcomp =
@@ -2584,7 +2680,7 @@ e_cal_backend_file_modify_object (ECalBackendSync *backend,
 		break;
 	}
 
-	save (cbfile);
+	save (cbfile, TRUE);
 	g_free (rid);
 
 	if (new_component) {
@@ -2920,7 +3016,7 @@ e_cal_backend_file_remove_object (ECalBackendSync *backend,
 		break;
 	}
 
-	save (cbfile);
+	save (cbfile, TRUE);
 
 	g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
 }
@@ -3335,7 +3431,7 @@ e_cal_backend_file_receive_objects (ECalBackendSync *backend,
 	 * resolving any conflicting TZIDs. */
 	icalcomponent_merge_component (priv->icalcomp, toplevel_comp);
 
-	save (cbfile);
+	save (cbfile, TRUE);
 
  error:
 	g_hash_table_destroy (tzdata.zones);
diff --git a/calendar/libedata-cal/e-cal-backend.h b/calendar/libedata-cal/e-cal-backend.h
index a50c1fc..6c064da 100644
--- a/calendar/libedata-cal/e-cal-backend.h
+++ b/calendar/libedata-cal/e-cal-backend.h
@@ -125,6 +125,17 @@ G_BEGIN_DECLS
  **/
 #define CAL_BACKEND_PROPERTY_DEFAULT_OBJECT		"default-object"
 
+/**
+ * CAL_BACKEND_PROPERTY_REVISION:
+ *
+ * The current overall revision string, this can be used as
+ * a quick check to see if data has changed at all since the
+ * last time the calendar revision was observed.
+ *
+ * Since: 3.4
+ **/
+#define CAL_BACKEND_PROPERTY_REVISION			"revision"
+
 struct _ECalBackendCache;
 
 typedef struct _ECalBackendPrivate ECalBackendPrivate;
diff --git a/libedataserver/e-client.h b/libedataserver/e-client.h
index df3338f..fced505 100644
--- a/libedataserver/e-client.h
+++ b/libedataserver/e-client.h
@@ -99,6 +99,17 @@
 #define CLIENT_BACKEND_PROPERTY_CAPABILITIES		"capabilities"
 
 /**
+ * CLIENT_BACKEND_PROPERTY_REVISION:
+ *
+ * The current overall revision string, this can be used as
+ * a quick check to see if data has changed at all since the
+ * last time the revision was observed.
+ *
+ * Since: 3.4
+ **/
+#define CLIENT_BACKEND_PROPERTY_REVISION		"revision"
+
+/**
  * E_CLIENT_ERROR:
  *
  * Error domain for #EClient operations.  Errors in this domain will be
diff --git a/tests/libebook/client/test-client-get-revision.c b/tests/libebook/client/test-client-get-revision.c
index 2d0f39f..9d56b2d 100644
--- a/tests/libebook/client/test-client-get-revision.c
+++ b/tests/libebook/client/test-client-get-revision.c
@@ -15,7 +15,7 @@ get_revision_compare_cycle (EBookClient *client)
        EContact *contact = NULL;
        GError   *error = NULL;
 
-       if (!e_client_get_backend_property_sync (E_CLIENT (client), BOOK_BACKEND_PROPERTY_REVISION, &revision_before, NULL, &error))
+       if (!e_client_get_backend_property_sync (E_CLIENT (client), CLIENT_BACKEND_PROPERTY_REVISION, &revision_before, NULL, &error))
                g_error ("Error getting book revision: %s", error->message);
 
 	if (!add_contact_from_test_case_verify (client, "simple-1", &contact)) {
@@ -28,7 +28,7 @@ get_revision_compare_cycle (EBookClient *client)
 
 	g_object_unref (contact);
 
-       if (!e_client_get_backend_property_sync (E_CLIENT (client), BOOK_BACKEND_PROPERTY_REVISION, &revision_after, NULL, &error))
+       if (!e_client_get_backend_property_sync (E_CLIENT (client), CLIENT_BACKEND_PROPERTY_REVISION, &revision_after, NULL, &error))
                g_error ("Error getting book revision: %s", error->message);
 
        g_assert (revision_before);
