[evolution-mapi] Bug #642022 - Modified instances in a recurrent event disappear



commit bb7e7d839dcd985b7d4622c0f27b05d61ba9eb12
Author: Sean Finney <seanius seanius net>
Date:   Thu Feb 17 18:20:51 2011 +0100

    Bug #642022 - Modified instances in a recurrent event disappear

 src/calendar/e-cal-backend-mapi.c                  |   54 +++--
 .../exchange-mapi-cal-recur-utils.c                |  235 ++++++++++++++++++--
 .../exchange-mapi-cal-recur-utils.h                |    2 +-
 src/libexchangemapi/exchange-mapi-cal-utils.c      |   14 +-
 src/libexchangemapi/exchange-mapi-cal-utils.h      |    2 +-
 5 files changed, 270 insertions(+), 37 deletions(-)
---
diff --git a/src/calendar/e-cal-backend-mapi.c b/src/calendar/e-cal-backend-mapi.c
index 586f8b1..c46290d 100644
--- a/src/calendar/e-cal-backend-mapi.c
+++ b/src/calendar/e-cal-backend-mapi.c
@@ -420,6 +420,7 @@ mapi_cal_get_changes_cb (FetchItemsCallbackData *item_data, gpointer data)
 	GSList *streams = item_data->streams;
 	GSList *recipients = item_data->recipients;
 	GSList *attachments = item_data->attachments;
+	GSList *detached = NULL, *d_i = NULL;
 	ECalBackendMAPI *cbmapi	= data;
 	ECalBackendMAPIPrivate *priv = cbmapi->priv;
 	icalcomponent_kind kind;
@@ -450,20 +451,27 @@ mapi_cal_get_changes_cb (FetchItemsCallbackData *item_data, gpointer data)
 	if (cache_comp == NULL) {
 		ECalComponent *comp = exchange_mapi_cal_util_mapi_props_to_comp (item_data->conn, kind, tmp, array,
 									streams, recipients, attachments,
-									cache_dir, priv->default_zone, FALSE);
+									cache_dir, priv->default_zone, FALSE, &detached);
 
-		if (E_IS_CAL_COMPONENT (comp)) {
-			gchar *comp_str;
+		detached = g_slist_prepend (detached, comp);
 
-			e_cal_component_commit_sequence (comp);
-			comp_str = e_cal_component_get_as_string (comp);
+		for (d_i = detached; d_i; d_i = g_slist_next (d_i)) {
+			comp = d_i->data;
 
-			put_component_to_store (cbmapi, comp);
-			e_cal_backend_notify_object_created (E_CAL_BACKEND (cbmapi), (const gchar *) comp_str);
+			if (E_IS_CAL_COMPONENT (comp)) {
+				gchar *comp_str;
 
-			g_free (comp_str);
+				e_cal_component_commit_sequence (comp);
+				put_component_to_store (cbmapi, comp);
+
+				comp_str = e_cal_component_get_as_string (comp);
+				e_cal_backend_notify_object_created (E_CAL_BACKEND (cbmapi), comp_str);
+				g_free (comp_str);
+			}
+
+			g_object_unref (comp);
 		}
-		g_object_unref (comp);
+		g_slist_free (detached);
 	} else {
 		struct timeval t;
 
@@ -483,7 +491,7 @@ mapi_cal_get_changes_cb (FetchItemsCallbackData *item_data, gpointer data)
 
 				comp = exchange_mapi_cal_util_mapi_props_to_comp (item_data->conn, kind, tmp, array,
 									streams, recipients, attachments,
-									cache_dir, priv->default_zone, FALSE);
+									cache_dir, priv->default_zone, FALSE, NULL);
 
 				e_cal_component_commit_sequence (comp);
 				modif_comp_str = e_cal_component_get_as_string (comp);
@@ -1190,6 +1198,7 @@ mapi_cal_cache_create_cb (FetchItemsCallbackData *item_data, gpointer data)
 	GSList *streams = item_data->streams;
 	GSList *recipients = item_data->recipients;
 	GSList *attachments = item_data->attachments;
+	GSList *detached = NULL, *d_i = NULL;
 	ECalBackendMAPI *cbmapi	= E_CAL_BACKEND_MAPI (data);
 	ECalBackendMAPIPrivate *priv = cbmapi->priv;
 	icalcomponent_kind kind;
@@ -1225,18 +1234,27 @@ mapi_cal_cache_create_cb (FetchItemsCallbackData *item_data, gpointer data)
 	tmp = exchange_mapi_util_mapi_id_to_string (mid);
 	comp = exchange_mapi_cal_util_mapi_props_to_comp (item_data->conn, kind, tmp, properties,
 							streams, recipients, attachments,
-							cache_dir, priv->default_zone, FALSE);
+							cache_dir, priv->default_zone, FALSE, &detached);
 	g_free (tmp);
 
-	if (E_IS_CAL_COMPONENT (comp)) {
-		gchar *comp_str;
-		e_cal_component_commit_sequence (comp);
-		comp_str = e_cal_component_get_as_string (comp);
-		e_cal_backend_notify_object_created (E_CAL_BACKEND (cbmapi), (const gchar *) comp_str);
-		g_free (comp_str);
-		put_component_to_store (cbmapi, comp);
+	detached = g_slist_prepend (detached, comp);
+	for (d_i = detached; d_i; d_i = g_slist_next (d_i)) {
+		comp = d_i->data;
+
+		if (E_IS_CAL_COMPONENT (comp)) {
+			gchar *comp_str;
+
+			e_cal_component_commit_sequence (comp);
+			put_component_to_store (cbmapi, comp);
+
+			comp_str = e_cal_component_get_as_string (comp);
+			e_cal_backend_notify_object_created (E_CAL_BACKEND (cbmapi), comp_str);
+			g_free (comp_str);
+		}
+
 		g_object_unref (comp);
 	}
+	g_slist_free (detached);
 
 	exchange_mapi_util_free_stream_list (&streams);
 	exchange_mapi_util_free_recipient_list (&recipients);
diff --git a/src/libexchangemapi/exchange-mapi-cal-recur-utils.c b/src/libexchangemapi/exchange-mapi-cal-recur-utils.c
index 452a235..1af71cf 100644
--- a/src/libexchangemapi/exchange-mapi-cal-recur-utils.c
+++ b/src/libexchangemapi/exchange-mapi-cal-recur-utils.c
@@ -26,6 +26,7 @@
 #endif
 
 #include "exchange-mapi-cal-recur-utils.h"
+#include <libecal/e-cal-util.h>
 
 /* Reader/Writer versions */
 #define READER_VERSION	0x3004
@@ -76,6 +77,18 @@ struct ExceptionInfo {
 };
 #endif
 
+/* Override flags defining what fields might be found in ExceptionInfo */
+#define ARO_SUBJECT 0x0001
+#define ARO_MEETINGTYPE 0x0002
+#define ARO_REMINDERDELTA 0x0004
+#define ARO_REMINDER 0x0008
+#define ARO_LOCATION 0x0010
+#define ARO_BUSYSTATUS 0x0020
+#define ARO_ATTACHMENT 0x0040
+#define ARO_SUBTYPE 0x0080
+#define ARO_APPTCOLOR 0x0100
+#define ARO_EXCEPTIONAL_BODY 0x0200
+
 static icalrecurrencetype_weekday
 get_ical_weekstart (uint32_t fdow)
 {
@@ -234,11 +247,11 @@ check_calendar_type (guint16 type)
 }
 
 gboolean
-exchange_mapi_cal_util_bin_to_rrule (GByteArray *ba, ECalComponent *comp)
+exchange_mapi_cal_util_bin_to_rrule (GByteArray *ba, ECalComponent *comp, GSList **extra_detached)
 {
 	struct icalrecurrencetype rt;
 	guint16 flag16;
-	guint32 flag32;
+	guint32 flag32, writer_version;
 	guint8 *ptr = ba->data;
 	gint i;
 	GSList *exdate_list = NULL;
@@ -632,7 +645,9 @@ exchange_mapi_cal_util_bin_to_rrule (GByteArray *ba, ECalComponent *comp)
 	/* number of changed exceptions */
 	flag32 = *((guint32 *)ptr);
 	ptr += sizeof (guint32);
-	/* FIXME: Parse modified instances */
+	/* For each changed exception, there will be a corresponding
+          ExceptionInfo below.  So at present we don't need to do
+          anything with the information here beyond skipping it */
 	if (flag32)
 		ptr += flag32 * sizeof (guint32);
 
@@ -654,11 +669,11 @@ exchange_mapi_cal_util_bin_to_rrule (GByteArray *ba, ECalComponent *comp)
 		return FALSE;
 
 	/* some constant */
-	flag32 = *((guint32 *)ptr);
+	writer_version = *((guint32 *)ptr);
 	ptr += sizeof (guint32);
 	/* It should be set, but not must. It can be, technically, any value.
 	   Seen were 0x3006, 0x3008, 0x3009. It affects format of extended exception info
-	if (flag32 != WRITER_VERSION2)
+	if (writer_version != WRITER_VERSION2)
 		return FALSE; */
 
 	/* start time in mins */
@@ -669,15 +684,6 @@ exchange_mapi_cal_util_bin_to_rrule (GByteArray *ba, ECalComponent *comp)
 	flag32 = *((guint32 *)ptr);
 	ptr += sizeof (guint32);
 
-	/* modified exceptions */
-	flag16 = *((guint16 *)ptr);
-	ptr += sizeof (guint16);
-    /* FIXME: there are flag16 count modified exceptions here, which
-              are variable in size, followed by a ReservedBlock1{Size,}
-              and ReservedBlock2{Size,}.  However, since we have nothing 
-              else to do until we are able to parse these modified
-              instances, we just stop now. */
-
 	/* Set the recurrence */
 	{
 		GSList l;
@@ -691,6 +697,207 @@ exchange_mapi_cal_util_bin_to_rrule (GByteArray *ba, ECalComponent *comp)
 	/* FIXME: this also has modified instances */
 	e_cal_component_set_exdate_list (comp, exdate_list);
 
+	/* modified exceptions, an ExceptionCount sized list of
+	   ExceptionInfo instances */
+	flag16 = *((guint16 *)ptr);
+	ptr += sizeof (guint16);
+	if (flag16 && extra_detached) {
+		gint count = flag16;
+
+		e_cal_component_commit_sequence (comp);
+
+		for (i = 0; i < count; i++) {
+			uint32_t starttime, endtime, origtime;
+			guint16 overrideflags;
+			struct icaltimetype tt;
+			ECalComponent *detached = NULL;
+			ECalComponentDateTime edt;
+			ECalComponentRange rid;
+
+			/* ExceptionInfo.StartTime */
+			starttime = *((guint32 *)ptr);
+			ptr += sizeof (guint32);
+
+			/* ExceptionInfo.EndTime */
+			endtime = *((guint32 *)ptr);
+			ptr += sizeof (guint32);
+
+			/* ExceptionInfo.OriginalStartDate */
+			origtime = *((guint32 *)ptr);
+			ptr += sizeof (guint32);
+
+			/* make a shallow clone of comp */
+			detached = e_cal_component_clone (comp);
+
+			tt = icaltime_from_timet_with_zone (convert_recurrence_minutes_to_timet (origtime), 0, 0);
+			rid.type = E_CAL_COMPONENT_RANGE_SINGLE;
+			rid.datetime.value = &tt;
+			rid.datetime.tzid = "UTC";
+			e_cal_component_set_recurid (detached, &rid);
+
+			tt = icaltime_from_timet_with_zone (convert_recurrence_minutes_to_timet (starttime), 0, 0);
+			edt.value = &tt;
+			edt.tzid = "UTC";
+			e_cal_component_set_dtstart (detached, &edt);
+
+			tt = icaltime_from_timet_with_zone (convert_recurrence_minutes_to_timet (endtime), 0, 0);
+			edt.value = &tt;
+			edt.tzid = "UTC";
+			e_cal_component_set_dtend (detached, &edt);
+
+			e_cal_component_set_rdate_list (detached, NULL);
+			e_cal_component_set_rrule_list (detached, NULL);
+			e_cal_component_set_exdate_list (detached, NULL);
+			e_cal_component_set_exrule_list (detached, NULL);
+
+			/* continue parsing stuff we don't need, because we need to
+			   get to the next ExceptionInfo object or back out to the
+			   containing AppointmentRecurrencePattern object */
+
+			/* ExceptionInfo.OverrideFlags */
+			overrideflags = *((guint16 *) ptr);
+			ptr += sizeof (guint16);
+
+			if (overrideflags & ARO_SUBJECT) {
+				ECalComponentText text = { 0 };
+				gchar *str;
+
+				/* ExceptionInfo.SubjectLength, ExceptionInfo.SubjectLength2
+				   and ExceptionInfo.Subject */
+				ptr += sizeof (guint16);
+				flag16 = *(guint16 *)ptr; /* use SubjectLength2 */
+				ptr += sizeof (guint16);
+				/* note a discrepency in MS-OXOCAL here, which suggests that
+				   Subject is actually 2 bytes */
+
+				str = g_strndup ((const gchar *) ptr, flag16);
+				text.value = str;
+				e_cal_component_set_summary (detached, &text);
+				g_free (str);
+
+				ptr += flag16;
+			}
+
+			if (overrideflags & ARO_MEETINGTYPE) {
+				/* ExceptionInfo.MeetingType */
+				ptr += sizeof (guint32);
+			}
+
+			if (overrideflags & ARO_REMINDERDELTA) {
+				/* ExceptionInfo.ReminderDelta */
+				ptr += sizeof (guint32);
+			}
+
+			if (overrideflags & ARO_REMINDER) {
+				/* ExceptionInfo.ReminderSet */
+				ptr += sizeof (guint32);
+			}
+
+			if (overrideflags & ARO_LOCATION) {
+				gchar *str;
+
+				/* ExceptionInfo.LocationLength, ExceptionInfo.LocationLength2
+				   and ExceptionInfo.Location */
+				ptr += sizeof (guint16);
+				flag16 = *(guint16 *) ptr; /* use LocationLength2 */
+				ptr += sizeof (guint16);
+				/* note a discrepency in MS-OXOCAL here, which suggests that
+				   Location is actually 4 bytes */
+
+				str = g_strndup ((const gchar *) ptr, flag16);
+				e_cal_component_set_location (detached, str);
+				g_free (str);
+
+				ptr += flag16;
+			}
+
+			if (overrideflags & ARO_BUSYSTATUS) {
+				/* ExceptionInfo.BusyStatus */
+				ptr += sizeof (guint32);
+			}
+
+			if (overrideflags & ARO_ATTACHMENT) {
+				/* ExceptionInfo.Attachment */
+				ptr += sizeof (guint32);
+			}
+
+			if (overrideflags & ARO_SUBTYPE) {
+				/* ExceptionInfo.Subtype */
+				ptr += sizeof (guint32);
+			}
+
+			if (overrideflags & ARO_APPTCOLOR) {
+				/* ExceptionInfo.AppointmentColor */
+				ptr += sizeof (guint32);
+			}
+
+			/* ExceptionInfo.ReservedBlock1Size */
+			flag32 = *((guint32 *)ptr);
+			ptr += sizeof (guint32) * 2;
+			/* The spec is self-contradicting regarding ReservedBlock1Size
+			   And Reserved1Block here.  Observations are that the former
+			   exists as a 4 byte integer which "MUST" be but isn't always
+			   set to 0, and ReservedBlock1 simply doesn't exist.
+			 */
+
+			if (writer_version >= 0x3009) {
+				/* ChangeHighlight struct */
+				flag32 = *((guint32 *)ptr);
+				ptr += sizeof (guint32) * (1 + flag32);
+			}
+
+			/* ReservedBlockEE1Size */
+			flag32 = *((guint32 *)ptr);
+			ptr += sizeof (guint32);
+			if (!flag32) {
+				/* it's supposed to be 0 */
+				
+				/* StartTime */
+				ptr += sizeof (guint32);
+
+				/* EndTime */
+				ptr += sizeof (guint32);
+
+				/* OriginalStartDate */
+				ptr += sizeof (guint32);
+
+				if (overrideflags & ARO_SUBJECT) {
+					ECalComponentText text = { 0 };
+					gchar *str;
+
+					/* SubjectLength */
+					flag16 = *(guint16 *)ptr;
+					ptr += sizeof (guint16);
+
+					str = g_convert ((const gchar *) ptr, flag16 * 2, "UTF-8", "UTF-16", NULL, NULL, NULL);
+					text.value = str;
+					e_cal_component_set_summary (detached, &text);
+					g_free (str);
+
+					ptr += flag16 * 2;
+				}
+
+				if (overrideflags & ARO_LOCATION) {
+					gchar *str;
+
+					/* LocationLength */
+					flag16 = *(guint16 *)ptr;
+					ptr += sizeof (guint16);
+
+					str = g_convert ((const gchar *) ptr, flag16 * 2, "UTF-8", "UTF-16", NULL, NULL, NULL);
+					e_cal_component_set_location (detached, str);
+					g_free (str);
+
+					ptr += flag16 * 2;
+				}
+			}
+
+			*extra_detached = g_slist_append (*extra_detached, detached);
+		}
+	}
+
+	/* in case anyone ever needs to traverse further, from this point ptr 
+	   should be pointing at AppointmentRecurrencePattern.ReservedBlock1Size */
 	return TRUE;
 }
 
diff --git a/src/libexchangemapi/exchange-mapi-cal-recur-utils.h b/src/libexchangemapi/exchange-mapi-cal-recur-utils.h
index 621b8d9..faa26b4 100644
--- a/src/libexchangemapi/exchange-mapi-cal-recur-utils.h
+++ b/src/libexchangemapi/exchange-mapi-cal-recur-utils.h
@@ -31,7 +31,7 @@
 G_BEGIN_DECLS
 
 gboolean
-exchange_mapi_cal_util_bin_to_rrule (GByteArray *ba, ECalComponent *comp);
+exchange_mapi_cal_util_bin_to_rrule (GByteArray *ba, ECalComponent *comp, GSList **extra_detached);
 
 GByteArray *
 exchange_mapi_cal_util_rrule_to_bin (ECalComponent *comp, GSList *modified_comps);
diff --git a/src/libexchangemapi/exchange-mapi-cal-utils.c b/src/libexchangemapi/exchange-mapi-cal-utils.c
index a3c87ce..4e91253 100644
--- a/src/libexchangemapi/exchange-mapi-cal-utils.c
+++ b/src/libexchangemapi/exchange-mapi-cal-utils.c
@@ -692,7 +692,7 @@ id_to_string (GByteArray *ba)
 ECalComponent *
 exchange_mapi_cal_util_mapi_props_to_comp (ExchangeMapiConnection *conn, icalcomponent_kind kind, const gchar *mid, struct mapi_SPropValue_array *properties,
 					   GSList *streams, GSList *recipients, GSList *attachments,
-					   const gchar *local_store_uri, const icaltimezone *default_zone, gboolean is_reply)
+					   const gchar *local_store_uri, const icaltimezone *default_zone, gboolean is_reply, GSList **detached_components)
 {
 	ECalComponent *comp = NULL;
 	struct timeval t;
@@ -957,7 +957,7 @@ exchange_mapi_cal_util_mapi_props_to_comp (ExchangeMapiConnection *conn, icalcom
 		if (b && *b) {
 			stream = exchange_mapi_util_find_stream (streams, PidLidAppointmentRecur);
 			if (stream) {
-				exchange_mapi_cal_util_bin_to_rrule (stream->value, comp);
+				exchange_mapi_cal_util_bin_to_rrule (stream->value, comp, detached_components);
 			}
 		}
 
@@ -1074,6 +1074,7 @@ fetch_camel_cal_comp_cb (FetchItemsCallbackData *item_data, gpointer data)
 {
 	struct fetch_camel_cal_data *fccd = data;
 	ECalComponent *comp = NULL;
+	GSList *detached_recurrences = NULL, *d_i = NULL;
 	mapi_id_t mid = 0;
 	icalcomponent *icalcomp = NULL;
 	gchar *str = NULL, *smid = NULL, *filepath;
@@ -1093,7 +1094,8 @@ fetch_camel_cal_comp_cb (FetchItemsCallbackData *item_data, gpointer data)
 			smid = e_cal_component_gen_uid();
 		comp = exchange_mapi_cal_util_mapi_props_to_comp (item_data->conn, fccd->kind, smid,
 							item_data->properties, item_data->streams, item_data->recipients,
-							item_data->attachments, filepath, NULL, TRUE);
+							item_data->attachments, filepath, NULL, TRUE, 
+							&detached_recurrences);
 
 		g_free (smid);
 	}
@@ -1105,10 +1107,16 @@ fetch_camel_cal_comp_cb (FetchItemsCallbackData *item_data, gpointer data)
 	if (comp)
 		icalcomponent_add_component (icalcomp,
 			icalcomponent_new_clone(e_cal_component_get_icalcomponent(comp)));
+	for (d_i = detached_recurrences; d_i; d_i = g_slist_next (d_i)) {
+		icalcomponent_add_component (icalcomp,
+				icalcomponent_new_clone (e_cal_component_get_icalcomponent (d_i->data)));
+		g_object_unref (d_i->data);
+	}
 	str = icalcomponent_as_ical_string_r (icalcomp);
 	icalcomponent_free (icalcomp);
 	if (comp)
 		g_object_unref (comp);
+	g_slist_free (detached_recurrences);
 
 	exchange_mapi_util_free_stream_list (&item_data->streams);
 	exchange_mapi_util_free_recipient_list (&item_data->recipients);
diff --git a/src/libexchangemapi/exchange-mapi-cal-utils.h b/src/libexchangemapi/exchange-mapi-cal-utils.h
index d88acfa..0d6a1c3 100644
--- a/src/libexchangemapi/exchange-mapi-cal-utils.h
+++ b/src/libexchangemapi/exchange-mapi-cal-utils.h
@@ -85,7 +85,7 @@ exchange_mapi_cal_util_fetch_attachments (ECalComponent *comp, GSList **attach_l
 ECalComponent *
 exchange_mapi_cal_util_mapi_props_to_comp (ExchangeMapiConnection *conn, icalcomponent_kind kind, const gchar *mid, struct mapi_SPropValue_array *properties,
 					   GSList *streams, GSList *recipients, GSList *attachments,
-					   const gchar *local_store_uri, const icaltimezone *default_zone, gboolean is_reply);
+					   const gchar *local_store_uri, const icaltimezone *default_zone, gboolean is_reply, GSList **detached_components);
 
 void
 exchange_mapi_cal_util_generate_globalobjectid (gboolean is_clean, const gchar *uid, struct Binary_r *sb);



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