commit ed6da0101b60436b221216ad133168ca119f5156
Author: Bharath Acharya <abharath@novell.com>
Date:   Tue Jun 1 10:21:15 2010 +0530

    Free/Busy search implementation in MAPI.
    
    Works for all basic cases. Please find more comments at
    https://bugzilla.gnome.org/show_bug.cgi?id=601486

diff --git a/src/calendar/e-cal-backend-mapi.c b/src/calendar/e-cal-backend-mapi.c
index fe62667..68dddc3 100644
--- a/src/calendar/e-cal-backend-mapi.c
+++ b/src/calendar/e-cal-backend-mapi.c
@@ -2266,9 +2266,15 @@ static ECalBackendSyncStatus
 e_cal_backend_mapi_get_free_busy (ECalBackendSync *backend, EDataCal *cal,
 				  GList *users, time_t start, time_t end, GList **freebusy)
 {
+	ECalBackendMAPI *cbmapi;
+	ECalBackendMAPIPrivate *priv;
 
-	return GNOME_Evolution_Calendar_Success;
+	cbmapi = E_CAL_BACKEND_MAPI (backend);
+	priv = cbmapi->priv;
+
+	exchange_mapi_cal_utils_get_free_busy_data (priv->conn, users, start, end, freebusy);
 
+	return GNOME_Evolution_Calendar_Success;
 }
 
 typedef struct {
diff --git a/src/libexchangemapi/exchange-mapi-cal-utils.c b/src/libexchangemapi/exchange-mapi-cal-utils.c
index f059e17..f8ffcc6 100644
--- a/src/libexchangemapi/exchange-mapi-cal-utils.c
+++ b/src/libexchangemapi/exchange-mapi-cal-utils.c
@@ -2120,6 +2120,236 @@ exchange_mapi_cal_util_get_new_appt_id (ExchangeMapiConnection *conn, mapi_id_t
 	return id;
 }
 
+static time_t
+mapi_get_date_from_string (const gchar *dtstring)
+{
+	time_t t = 0;
+	GTimeVal t_val;
+
+	g_return_val_if_fail (dtstring != NULL, 0);
+
+	if (g_time_val_from_iso8601 (dtstring, &t_val)) {
+		t = (time_t) t_val.tv_sec;
+	} else if (strlen (dtstring) == 8) {
+		/* It might be a date value */
+		GDate date;
+		struct tm tt;
+		guint16 year;
+		guint month;
+		guint8 day;
+
+		g_date_clear (&date, 1);
+#define digit_at(x,y) (x[y] - '0')
+		year = digit_at (dtstring, 0) * 1000
+			+ digit_at (dtstring, 1) * 100
+			+ digit_at (dtstring, 2) * 10
+			+ digit_at (dtstring, 3);
+		month = digit_at (dtstring, 4) * 10 + digit_at (dtstring, 5);
+		day = digit_at (dtstring, 6) * 10 + digit_at (dtstring, 7);
+
+		g_date_set_year (&date, year);
+		g_date_set_month (&date, month);
+		g_date_set_day (&date, day);
+
+		g_date_to_struct_tm (&date, &tt);
+		t = mktime (&tt);
+
+	} else
+		g_warning ("Could not parse the string \n");
+
+        return t;
+}
+
+static void
+populate_freebusy_data (struct Binary_r *bin, uint32_t month, uint32_t year, GList **freebusy, const gchar *accept_type, ECalComponent *comp)
+{
+	uint16_t	event_start;
+	uint16_t	event_end;
+	uint32_t	i;
+	uint32_t       	hour;
+	uint32_t	day;
+	const char	*month_name;
+	uint32_t	minutes;
+	uint32_t	real_month;
+	gchar *date_string = NULL;
+	gchar *start = NULL, *end = NULL;
+	time_t start_date, end_date;
+	icalcomponent *icalcomp = NULL;
+
+	if (!bin)
+		return;
+	/* bin.cb must be a multiple of 4 */
+	if (bin->cb % 4)
+		return;
+
+	year = mapidump_freebusy_year(month, year);
+	month_name = mapidump_freebusy_month(month, year);
+	if (!month_name)
+		return;
+
+	for (i = 0; i < bin->cb; i+= 4) {
+		event_start = (bin->lpb[i + 1] << 8) | bin->lpb[i];
+		event_end = (bin->lpb[i + 3] << 8) | bin->lpb[i + 2];
+
+		for (hour = 0; hour < 24; hour++) {
+			if (!(((event_start - (60 * hour)) % 1440) && (((event_start - (60 * hour)) % 1440) - 30))) {
+				struct icalperiodtype ipt;
+				icalproperty *icalprop;
+				icaltimetype itt;
+
+				day = ((event_start - (60 * hour)) / 1440) + 1;
+				minutes = (event_start - (60 * hour)) % 1440;
+				real_month = month - (year * 16);
+				
+				date_string = g_strdup_printf ("%.2u-%.2u-%.2u", year, real_month, day);
+				start = g_strdup_printf ("%sT%.2u:%.2u:00Z", date_string, hour + daylight, minutes);
+				g_free (date_string);
+
+				day = ((event_end - (60 * hour)) / 1440) + 1;
+				minutes = (event_end - (60 * hour)) % 1440;
+
+				if (minutes >= 60) {
+					hour += minutes / 60;
+					minutes %= 60;
+				}
+
+				date_string = g_strdup_printf ("%.2u-%.2u-%.2u", year, real_month, day);
+				end = g_strdup_printf ("%sT%.2u:%.2u:00Z", date_string, hour + daylight, minutes);
+				g_free (date_string);
+
+				start_date = mapi_get_date_from_string (start);
+				end_date = mapi_get_date_from_string (end);
+
+				memset (&ipt, 0, sizeof (struct icalperiodtype));
+				
+				itt = icaltime_from_timet_with_zone (start_date, 0, icaltimezone_get_utc_timezone ());
+				ipt.start = itt;
+
+				itt = icaltime_from_timet_with_zone (end_date, 0, icaltimezone_get_utc_timezone ());
+				ipt.end = itt;
+			
+				icalcomp = e_cal_component_get_icalcomponent (comp);
+				icalprop = icalproperty_new_freebusy (ipt);
+
+				if (!strcmp (accept_type, "Busy"))
+					icalproperty_set_parameter_from_string (icalprop, "FBTYPE", "BUSY");
+				else if (!strcmp (accept_type, "Tentative"))
+					icalproperty_set_parameter_from_string (icalprop, "FBTYPE", "BUSY-TENTATIVE");
+				else if (!strcmp (accept_type, "OutOfOffice"))
+					icalproperty_set_parameter_from_string (icalprop, "FBTYPE", "BUSY-UNAVAILABLE");
+
+				icalcomponent_add_property(icalcomp, icalprop);
+				g_free (start);
+				g_free (end);
+			}
+		}
+	}
+}
+
+gboolean
+exchange_mapi_cal_utils_get_free_busy_data (ExchangeMapiConnection *conn, GList *users, time_t start, time_t end, GList **freebusy)
+{
+	struct SRow		aRow;
+	enum MAPISTATUS		retval;
+	uint32_t		i;
+	mapi_object_t           obj_store;
+	GList *l;
+
+	const uint32_t			*publish_start;
+	const struct LongArray_r       	*busy_months;
+	const struct BinaryArray_r	*busy_events;
+	const struct LongArray_r       	*tentative_months;
+	const struct BinaryArray_r	*tentative_events;
+	const struct LongArray_r       	*oof_months;
+	const struct BinaryArray_r	*oof_events;
+	uint32_t			year;
+	uint32_t			event_year;
+
+	gchar *name = NULL;
+	ECalComponent *comp;
+	ECalComponentAttendee attendee;
+	GSList *attendee_list = NULL;
+	icalcomponent *icalcomp = NULL;
+	icaltimetype start_time, end_time;
+	icaltimezone *default_zone = NULL;
+
+	exchange_mapi_connection_get_public_folder (conn, &obj_store);
+	
+	for ( l = users; l != NULL; l = g_list_next (l)) {
+		retval = GetUserFreeBusyData (&obj_store, (const gchar *)l->data, &aRow);
+
+		if (retval != MAPI_E_SUCCESS) return false;
+
+		/* Step 2. Dump properties */
+		publish_start = (const uint32_t *) find_SPropValue_data(&aRow, PR_FREEBUSY_START_RANGE);
+		busy_months = (const struct LongArray_r *) find_SPropValue_data(&aRow, PR_FREEBUSY_BUSY_MONTHS);
+		busy_events = (const struct BinaryArray_r *) find_SPropValue_data(&aRow, PR_FREEBUSY_BUSY_EVENTS);
+		tentative_months = (const struct LongArray_r *) find_SPropValue_data(&aRow, PR_FREEBUSY_TENTATIVE_MONTHS);
+		tentative_events = (const struct BinaryArray_r *) find_SPropValue_data(&aRow, PR_FREEBUSY_TENTATIVE_EVENTS);
+		oof_months = (const struct LongArray_r *) find_SPropValue_data(&aRow, PR_FREEBUSY_OOF_MONTHS);
+		oof_events = (const struct BinaryArray_r *) find_SPropValue_data(&aRow, PR_FREEBUSY_OOF_EVENTS);
+
+		year = GetFreeBusyYear(publish_start);
+
+		comp = e_cal_component_new ();
+		e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_FREEBUSY);
+		e_cal_component_commit_sequence (comp);
+		icalcomp = e_cal_component_get_icalcomponent (comp);
+
+		start_time = icaltime_from_timet_with_zone (start, 0, default_zone ? default_zone : NULL);
+		end_time = icaltime_from_timet_with_zone (end, 0, default_zone ? default_zone : NULL);
+		icalcomponent_set_dtstart (icalcomp, start_time);
+		icalcomponent_set_dtend (icalcomp, end_time);
+
+		memset (&attendee, 0, sizeof (ECalComponentAttendee));
+		if (name)
+			attendee.cn = name;
+		if (l->data)
+			attendee.value = l->data;
+
+		attendee.cutype = ICAL_CUTYPE_INDIVIDUAL;
+		attendee.role = ICAL_ROLE_REQPARTICIPANT;
+		attendee.status = ICAL_PARTSTAT_NEEDSACTION;
+
+		attendee_list = g_slist_append (attendee_list, &attendee);
+
+		e_cal_component_set_attendee_list (comp, attendee_list);
+		g_slist_free (attendee_list);
+		g_free ((gchar *) name);
+		g_free ((gchar *) l->data);
+
+		if (busy_months && ((*(const uint32_t *) busy_months) != MAPI_E_NOT_FOUND) &&
+		    busy_events && ((*(const uint32_t *) busy_events) != MAPI_E_NOT_FOUND)) {
+			for (i = 0; i < busy_months->cValues; i++) {
+				event_year = mapidump_freebusy_year(busy_months->lpl[i], year);
+				populate_freebusy_data (&busy_events->lpbin[i], busy_months->lpl[i], event_year, freebusy, "Busy", comp);
+			}
+		}
+		
+		if (tentative_months && ((*(const uint32_t *) tentative_months) != MAPI_E_NOT_FOUND) &&
+		    tentative_events && ((*(const uint32_t *) tentative_events) != MAPI_E_NOT_FOUND)) {
+			for (i = 0; i < tentative_months->cValues; i++) {
+				event_year = mapidump_freebusy_year(tentative_months->lpl[i], year);
+				populate_freebusy_data (&tentative_events->lpbin[i], tentative_months->lpl[i], event_year, freebusy, "Tentative", comp);
+			}
+		}
+
+		if (oof_months && ((*(const uint32_t *) oof_months) != MAPI_E_NOT_FOUND) &&
+		    oof_events && ((*(const uint32_t *) oof_events) != MAPI_E_NOT_FOUND)) {
+			for (i = 0; i < oof_months->cValues; i++) {
+				event_year = mapidump_freebusy_year(oof_months->lpl[i], year);
+				populate_freebusy_data (&oof_events->lpbin[i], oof_months->lpl[i], event_year, freebusy, "OutOfOffice", comp);
+			}
+		}
+
+		e_cal_component_commit_sequence (comp);
+		*freebusy = g_list_append (*freebusy, e_cal_component_get_as_string (comp));
+//		g_object_unref (comp);
+		MAPIFreeBuffer(aRow.lpProps);
+	}
+	return TRUE;
+}
+
 /* beware, the 'data' pointer is an integer of the event kind */
 gboolean
 exchange_mapi_cal_utils_get_props_cb (ExchangeMapiConnection *conn, mapi_id_t fid, TALLOC_CTX *mem_ctx, struct SPropTagArray *props, gpointer data)
diff --git a/src/libexchangemapi/exchange-mapi-cal-utils.h b/src/libexchangemapi/exchange-mapi-cal-utils.h
index 196abd8..bc8416d 100644
--- a/src/libexchangemapi/exchange-mapi-cal-utils.h
+++ b/src/libexchangemapi/exchange-mapi-cal-utils.h
@@ -101,6 +101,8 @@ gboolean exchange_mapi_cal_utils_add_named_ids (ExchangeMapiConnection *conn, ma
 gboolean exchange_mapi_cal_utils_get_props_cb (ExchangeMapiConnection *conn, mapi_id_t fid, TALLOC_CTX *mem_ctx, struct SPropTagArray *props, gpointer data);
 gboolean exchange_mapi_cal_utils_write_props_cb (ExchangeMapiConnection *conn, mapi_id_t fid, TALLOC_CTX *mem_ctx, struct SPropValue **values, uint32_t *n_values, gpointer data);
 
+gboolean exchange_mapi_cal_utils_get_free_busy_data (ExchangeMapiConnection *conn, GList *users, time_t start, time_t end, GList **freebusy);
+
 G_END_DECLS
 
 #endif
diff --git a/src/libexchangemapi/exchange-mapi-connection.c b/src/libexchangemapi/exchange-mapi-connection.c
index fc3c99e..701fe73 100644
--- a/src/libexchangemapi/exchange-mapi-connection.c
+++ b/src/libexchangemapi/exchange-mapi-connection.c
@@ -1088,6 +1088,26 @@ exchange_mapi_connection_fetch_gal (ExchangeMapiConnection *conn, BuildReadProps
 	return retval == MAPI_E_SUCCESS;
 }
 
+gboolean
+exchange_mapi_connection_get_public_folder (ExchangeMapiConnection *conn, mapi_object_t *obj_store)
+{
+	enum MAPISTATUS		retval;
+	
+	CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
+	g_return_val_if_fail (priv->session != NULL, FALSE);
+
+	mapi_object_init (&priv->public_store);
+
+	retval = OpenPublicFolder (priv->session, &priv->public_store);
+
+	if (retval != MAPI_E_SUCCESS) {
+		mapi_errstr ("OpenPublicFolder", GetLastError());
+		exit(1);
+	}
+
+	*obj_store = priv->public_store;
+}
+
 /* Returns TRUE if all recipients were read succcesfully, else returns FALSE */
 static gboolean
 exchange_mapi_util_get_recipients (mapi_object_t *obj_message, GSList **recip_list)
diff --git a/src/libexchangemapi/exchange-mapi-connection.h b/src/libexchangemapi/exchange-mapi-connection.h
index 5f31a5d..8a4a595 100644
--- a/src/libexchangemapi/exchange-mapi-connection.h
+++ b/src/libexchangemapi/exchange-mapi-connection.h
@@ -175,6 +175,8 @@ gboolean		exchange_mapi_connection_fetch_gal (ExchangeMapiConnection *conn,
 					BuildReadPropsCB build_props, gpointer brp_data,
 					FetchGALCallback cb, gpointer data);
 
+gboolean		exchange_mapi_connection_get_public_folder (ExchangeMapiConnection *conn, mapi_object_t *obj_object);
+
 mapi_id_t		exchange_mapi_connection_create_folder (ExchangeMapiConnection *conn, uint32_t olFolder, mapi_id_t pfid, const gchar *name);
 gboolean		exchange_mapi_connection_remove_folder (ExchangeMapiConnection *conn, mapi_id_t fid);
 gboolean		exchange_mapi_connection_empty_folder (ExchangeMapiConnection *conn, mapi_id_t fid);
