[evolution-mapi] Fast-transfer for calendars with fallback to slow fetch



commit 00bc632874d5b67ed3f63d3d458ebcce6ffda447
Author: Milan Crha <mcrha redhat com>
Date:   Fri Nov 18 18:06:12 2011 +0100

    Fast-transfer for calendars with fallback to slow fetch

 src/calendar/e-cal-backend-mapi.c       |  965 ++++++++++++-------------------
 src/camel/camel-mapi-folder.c           |   39 +--
 src/libexchangemapi/e-mapi-cal-utils.c  |    6 +-
 src/libexchangemapi/e-mapi-connection.c |  531 +++++++++++++++++-
 src/libexchangemapi/e-mapi-connection.h |   13 +-
 src/libexchangemapi/e-mapi-mail-utils.c |   17 +-
 src/libexchangemapi/e-mapi-utils.c      |  186 ++++++
 src/libexchangemapi/e-mapi-utils.h      |   27 +-
 8 files changed, 1125 insertions(+), 659 deletions(-)
---
diff --git a/src/calendar/e-cal-backend-mapi.c b/src/calendar/e-cal-backend-mapi.c
index 505fbed..7293eb9 100644
--- a/src/calendar/e-cal-backend-mapi.c
+++ b/src/calendar/e-cal-backend-mapi.c
@@ -55,9 +55,6 @@
 #define EDC_ERROR(_code) e_data_cal_create_error (_code, NULL)
 #define EDC_ERROR_EX(_code, _msg) e_data_cal_create_error (_code, _msg)
 
-#define SERVER_UTC_TIME "server_utc_time"
-#define CACHE_MARKER "populated"
-
 G_DEFINE_TYPE (ECalBackendMAPI, e_cal_backend_mapi, E_TYPE_CAL_BACKEND)
 
 typedef struct {
@@ -73,7 +70,7 @@ struct _ECalBackendMAPIPrivate {
 	mapi_id_t		fid;
 	uint32_t		olFolder;
 	gchar			*profile;
-	EMapiConnection  *conn;
+	EMapiConnection *conn;
 
 	/* These fields are entirely for access rights */
 	gchar			*owner_name;
@@ -87,7 +84,6 @@ struct _ECalBackendMAPIPrivate {
 	gboolean		read_only;
 	gchar			*uri;
 	gboolean		mode_changed;
-	gboolean		populating_cache; /* whether in populate_cache */
 	GMutex			*updating_mutex;
 
 	/* timeout handler for syncing sendoptions */
@@ -97,6 +93,10 @@ struct _ECalBackendMAPIPrivate {
 	guint			timeout_id;
 	GThread			*dthread;
 	SyncDelta		*dlock;
+
+	time_t last_refresh;
+	gint last_obj_total;
+	GCancellable *cancellable;
 };
 
 #define CACHE_REFRESH_INTERVAL 600000
@@ -138,6 +138,23 @@ mapi_error_to_edc_error (GError **perror, const GError *mapi_error, EDataCalCall
 
 /* **** UTILITY FUNCTIONS **** */
 
+static void
+get_comp_mid (icalcomponent *icalcomp, mapi_id_t *mid)
+{
+	gchar *x_mid;
+
+	g_return_if_fail (icalcomp != NULL);
+	g_return_if_fail (mid != NULL);
+
+	x_mid = e_mapi_cal_utils_get_icomp_x_prop (icalcomp, "X-EVOLUTION-MAPI-MID");
+	if (x_mid) {
+		e_mapi_util_mapi_id_from_string (x_mid, mid);
+		g_free (x_mid);
+	} else {
+		e_mapi_util_mapi_id_from_string (icalcomponent_get_uid (icalcomp), mid);
+	}
+}
+
 static const gchar *
 ecbm_get_owner_name (ECalBackendMAPI *cbmapi)
 {
@@ -333,12 +350,16 @@ view_progress_cb (EDataCalView *view, gpointer user_data)
 }
 
 static void
-notify_view_progress (ECalBackendMAPI *cbmapi, guint64 index, guint64 total)
+notify_view_progress (ECalBackendMAPI *cbmapi, guint index, guint total)
 {
 	struct EMAPIProgressData pd = { 0 };
 	gchar *progress_string;
 
-	pd.percent = ((gfloat) index/total) * 100;
+	if (total > 0)
+		pd.percent = index * 100 / total;
+	else
+		pd.percent = -1;
+
 	if (pd.percent > 100)
 		pd.percent = 99;
 
@@ -399,457 +420,403 @@ put_component_to_store (ECalBackendMAPI *cbmapi,
 	e_cal_backend_store_put_component_with_time_range (priv->store, comp, time_start, time_end);
 }
 
-static gboolean
-mapi_cal_get_changes_cb (FetchItemsCallbackData *item_data,
-			 gpointer data,
-			 GCancellable *cancellable,
-			 GError **perror)
-{
-	struct mapi_SPropValue_array *array = item_data->properties;
-	const mapi_id_t mid = item_data->mid;
-	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;
-	gchar *tmp = NULL;
-	ECalComponent *cache_comp = NULL;
-	const gchar *cache_dir;
-	const bool *recurring;
-
-	kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
-	cache_dir = e_cal_backend_get_cache_dir (E_CAL_BACKEND (cbmapi));
-
-	recurring = e_mapi_util_find_array_namedid (array, item_data->conn, item_data->fid, PidLidTaskFRecurring);
-	if (recurring && *recurring) {
-		g_warning ("Encountered a recurring task.");
-		e_mapi_util_free_stream_list (&streams);
-		e_mapi_util_free_recipient_list (&recipients);
-		e_mapi_util_free_attachment_list (&attachments);
-		return TRUE;
-	}
-
-	tmp = e_mapi_util_mapi_id_to_string (mid);
-	cache_comp = e_cal_backend_store_get_component (priv->store, tmp, NULL);
-
-	if (cache_comp == NULL) {
-		ECalComponent *comp = e_mapi_cal_util_mapi_props_to_comp (item_data->conn, item_data->fid, kind, tmp, array,
-									streams, recipients, attachments,
-									cache_dir, icaltimezone_get_utc_timezone (), FALSE, &detached);
-
-		detached = g_slist_prepend (detached, comp);
+#define notify_error(_mmapi_backend, _merror, _mformat)					\
+	G_STMT_START {									\
+		notify_error_ex (_mmapi_backend,					\
+				 &(_merror),						\
+				 _mformat,						\
+				 (_merror) ? (_merror)->message : _("Unknown error"));	\
+	} G_STMT_END
 
-		for (d_i = detached; d_i; d_i = g_slist_next (d_i)) {
-			comp = d_i->data;
+static void notify_error_ex (ECalBackendMAPI *mapi_backend, GError **perror, const gchar *format, ...) G_GNUC_PRINTF (3, 4);
 
-			if (E_IS_CAL_COMPONENT (comp)) {
-				gchar *comp_str;
+static void
+notify_error_ex (ECalBackendMAPI *mapi_backend, GError **perror, const gchar *format, ...)
+{
+	gchar *msg;
+	va_list args;
 
-				e_cal_component_commit_sequence (comp);
-				put_component_to_store (cbmapi, comp);
+	g_return_if_fail (mapi_backend != NULL);
+	g_return_if_fail (format != NULL);
 
-				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);
-			}
+	if (perror && (
+	    g_error_matches (*perror, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
+	    g_error_matches (*perror, E_MAPI_ERROR, MAPI_E_USER_CANCEL)))
+		return;
 
-			g_object_unref (comp);
-		}
-		g_slist_free (detached);
-	} else {
-		struct timeval t;
+	va_start (args, format);
+	msg = g_strdup_vprintf (format, args);
+	va_end (args);
 
-		if (get_mapi_SPropValue_array_date_timeval (&t, array, PR_LAST_MODIFICATION_TIME) == MAPI_E_SUCCESS) {
-			struct icaltimetype itt, *cache_comp_lm = NULL;
+	e_cal_backend_notify_error (E_CAL_BACKEND (mapi_backend), msg);
+	g_free (msg);
 
-			itt = icaltime_from_timet_with_zone (t.tv_sec, 0, 0);
-			icaltime_set_timezone (&itt, icaltimezone_get_utc_timezone ());
+	g_clear_error (perror);
+}
 
-			e_cal_component_get_last_modified (cache_comp, &cache_comp_lm);
-			if (!cache_comp_lm || (icaltime_compare (itt, *cache_comp_lm) != 0)) {
-				ECalComponent *comp;
-				gchar *cache_comp_str = NULL, *modif_comp_str = NULL;
+static void
+free_component_slist (gpointer ptr)
+{
+	g_slist_free_full (ptr, g_object_unref);
+}
 
-				e_cal_component_commit_sequence (cache_comp);
-				cache_comp_str = e_cal_component_get_as_string (cache_comp);
+static void
+drop_removed_comps_cb (gpointer pmid, gpointer slist, gpointer pcbmapi)
+{
+	ECalBackendMAPI *cbmapi = pcbmapi;
+	ECalBackend *backend;
+	GSList *iter;
 
-				comp = e_mapi_cal_util_mapi_props_to_comp (item_data->conn, item_data->fid, kind, tmp, array,
-									streams, recipients, attachments,
-									cache_dir, icaltimezone_get_utc_timezone (), FALSE, NULL);
+	g_return_if_fail (pcbmapi != NULL);
 
-				e_cal_component_commit_sequence (comp);
-				modif_comp_str = e_cal_component_get_as_string (comp);
+	backend = E_CAL_BACKEND (cbmapi);
+	g_return_if_fail (backend != NULL);
 
-				put_component_to_store (cbmapi, comp);
-				e_cal_backend_notify_object_modified (E_CAL_BACKEND (cbmapi), cache_comp_str, modif_comp_str);
+	for (iter = slist; iter; iter = iter->next) {
+		ECalComponent *comp = iter->data;
+		ECalComponentId *id;
 
-				g_object_unref (comp);
-				g_free (cache_comp_str);
-				g_free (modif_comp_str);
-			}
-			g_object_unref (cache_comp);
-			g_free (cache_comp_lm);
+		if (!comp) {
+			g_debug ("%s: NULL component in list", G_STRFUNC);
+			continue;
 		}
-	}
-
-	g_free (tmp);
-	e_mapi_util_free_stream_list (&streams);
-	e_mapi_util_free_recipient_list (&recipients);
-	e_mapi_util_free_attachment_list (&attachments);
-
-	notify_view_progress (cbmapi, item_data->index, item_data->total);
 
-	return TRUE;
-}
+		id = e_cal_component_get_id (comp);
+		if (!id) {
+			g_debug ("%s: Failed to get component's ID", G_STRFUNC);
+			continue;
+		}
 
-static void
-copy_strings_in_slist (GSList *slist)
-{
-	GSList *l;
+		if (e_cal_backend_store_remove_component (cbmapi->priv->store, id->uid, id->rid)) {
+			e_cal_backend_notify_component_removed (backend, id, e_cal_component_get_icalcomponent (comp), NULL);
+		}
 
-	for (l = slist; l; l = l->next) {
-		l->data = g_strdup (l->data);
+		e_cal_component_free_id (id);
 	}
 }
 
-struct deleted_items_data {
-	ECalBackendMAPI *cbmapi;
-	GSList *cache_ids;
-	GSList *unknown_mids; /* MIDs of items not in the cache */
+struct ListCalendarObjectsData
+{
+	GSList *changed_mids;		/* newly allocated mapi_id_t *; these will be fetched again */
+	GHashTable *known_comps;	/* reffed ECalComponent-s from the cache; those left will be removed;
+					   key is 'mapi_id_t *', the mid;
+					   value is GSList of the component and its detached instances
+					*/
+	time_t latest_modified;
 };
 
 static gboolean
-handle_deleted_items_cb (FetchItemsCallbackData *item_data,
-			 gpointer data,
-			 GCancellable *cancellable,
-			 GError **perror)
+list_calendar_objects_cb (EMapiConnection *conn,
+			  mapi_id_t fid,
+			  TALLOC_CTX *mem_ctx,
+			  const ListObjectsData *object_data,
+			  guint32 obj_index,
+			  guint32 obj_total,
+			  gpointer user_data,
+			  GCancellable *cancellable,
+			  GError **perror)
 {
-	const mapi_id_t mid = item_data->mid;
-	struct deleted_items_data *did = data;
-	gchar *tmp = NULL;
-	GSList *cache_comp_uid = NULL;
-	gboolean need_refetch = FALSE;
+	struct ListCalendarObjectsData *lco = user_data;
+	GSList *slist;
+	gboolean need_update = FALSE;
 
-	g_return_val_if_fail (did != NULL, FALSE);
+	g_return_val_if_fail (object_data != NULL, FALSE);
+	g_return_val_if_fail (lco != NULL, FALSE);
 
-	tmp = e_mapi_util_mapi_id_to_string (mid);
-	cache_comp_uid = g_slist_find_custom (did->cache_ids, tmp, (GCompareFunc) (g_ascii_strcasecmp));
-	if (cache_comp_uid != NULL) {
-		ECalBackendMAPIPrivate *priv = did->cbmapi->priv;
-		ECalComponent *comp;
+	if (object_data->msg_class &&
+	    g_ascii_strcasecmp (object_data->msg_class, "IPM.Note") == 0) {
+		return TRUE;
+	}
 
-		comp = e_cal_backend_store_get_component (priv->store, cache_comp_uid->data, NULL);
-		if (comp) {
-			struct icaltimetype *last_mod = NULL;
-			struct timeval t;
-
-			e_cal_component_get_last_modified (comp, &last_mod);
-			if (!last_mod) {
-				need_refetch = TRUE;
-			} else if (get_mapi_SPropValue_array_date_timeval (&t, item_data->properties, PR_LAST_MODIFICATION_TIME) == MAPI_E_SUCCESS
-			    && icaltime_compare (icaltime_from_timet_with_zone (t.tv_sec, 0, icaltimezone_get_utc_timezone ()), *last_mod) != 0) {
-				need_refetch = TRUE;
-			}
+	if (lco->latest_modified < object_data->last_modified)
+		lco->latest_modified = object_data->last_modified;
 
-			if (last_mod)
-				e_cal_component_free_icaltimetype (last_mod);
+	slist = g_hash_table_lookup (lco->known_comps, &object_data->mid);
+	if (!slist) {
+		/* it's a new component on the server */
+		need_update = TRUE;
+	} else {
+		/* known component, which might change */
+		ECalComponent *comp = slist->data;
+		struct icaltimetype *last_mod = NULL;
 
-			g_object_unref (comp);
+		/* pretty bad, but do not avoid fetching of other objects */
+		g_return_val_if_fail (comp != NULL, TRUE);
+
+		e_cal_component_get_last_modified (comp, &last_mod);
+
+		if (!last_mod ||
+		    icaltime_compare (icaltime_from_timet_with_zone (object_data->last_modified, 0, icaltimezone_get_utc_timezone ()), *last_mod) != 0) {
+			need_update = TRUE;
 		}
 
-		g_free (cache_comp_uid->data);
-		did->cache_ids = g_slist_remove_link (did->cache_ids, cache_comp_uid);
-	} else {
-		/* fetch it, as it is not in the cache */
-		need_refetch = TRUE;
+		if (last_mod)
+			e_cal_component_free_icaltimetype (last_mod);
+
+		g_hash_table_remove (lco->known_comps, &object_data->mid);
 	}
 
-	if (need_refetch) {
-		mapi_id_t *nmid = g_new (mapi_id_t, 1);
+	if (need_update) {
+		mapi_id_t *pmid;
 
-		*nmid = mid;
-		did->unknown_mids = g_slist_prepend (did->unknown_mids, nmid);
+		pmid = g_new0 (mapi_id_t, 1);
+		*pmid = object_data->mid;
+
+		lco->changed_mids = g_slist_prepend (lco->changed_mids, pmid);
 	}
 
-	g_free (tmp);
 	return TRUE;
 }
 
 static gboolean
-mapi_cal_get_idlist (EMapiConnection *conn,
-		     mapi_id_t fid,
-		     TALLOC_CTX *mem_ctx,
-		     struct SPropTagArray *props,
-		     gpointer data,
-		     GCancellable *cancellable,
-		     GError **perror)
-{
-	static const uint32_t cal_IDList[] = {
-		PR_FID,
-		PR_MID,
-		PR_LAST_MODIFICATION_TIME
-	};
-
-	g_return_val_if_fail (props != NULL, FALSE);
+transfer_calendar_objects_cb (EMapiConnection *conn,
+			      TALLOC_CTX *mem_ctx,
+			      /* const */ EMapiObject *object,
+			      guint32 obj_index,
+			      guint32 obj_total,
+			      gpointer user_data,
+			      GCancellable *cancellable,
+			      GError **perror)
+{
+	ECalBackendMAPI *cbmapi = user_data;
+	ECalBackend *backend;
+	ECalComponent *comp;
+	const mapi_id_t *pmid;
+	gchar *use_uid;
+	GSList *comps = NULL, *iter;
 
-	return e_mapi_utils_add_props_to_props_array (mem_ctx, props, cal_IDList, G_N_ELEMENTS (cal_IDList));
-}
+	g_return_val_if_fail (cbmapi != NULL, FALSE);
+	g_return_val_if_fail (object != NULL, FALSE);
 
-static gboolean
-ecbm_build_last_modification_restriction (EMapiConnection *conn,
-					  mapi_id_t fid,
-					  TALLOC_CTX *mem_ctx,
-					  struct mapi_SRestriction **restrictions,
-					  gpointer user_data,
-					  GCancellable *cancellable,
-					  GError **perror)
-{
-	icaltimetype *itt_cache = user_data;
+	backend = E_CAL_BACKEND (cbmapi);
+	g_return_val_if_fail (backend != NULL, FALSE);
 
-	g_return_val_if_fail (restrictions != NULL, FALSE);
+	pmid = e_mapi_util_find_array_propval (&object->properties, PidTagMid);
+	if (pmid)
+		use_uid = e_mapi_util_mapi_id_to_string (*pmid);
+	else
+		use_uid = e_cal_component_gen_uid ();
 
-	if (itt_cache && !icaltime_is_null_time (*itt_cache)) {
-		struct mapi_SRestriction *restriction;
-		struct SPropValue sprop;
-		struct timeval t;
+	comp = e_mapi_cal_util_object_to_comp (conn, object,
+		e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi)), FALSE,
+		e_cal_backend_get_cache_dir (E_CAL_BACKEND (cbmapi)),
+		use_uid, &comps);
 
-		restriction = talloc_zero (mem_ctx, struct mapi_SRestriction);
-		g_return_val_if_fail (restriction != NULL, FALSE);
+	g_free (use_uid);
 
-		restriction->rt = RES_PROPERTY;
-		restriction->res.resProperty.relop = RELOP_GE;
-		restriction->res.resProperty.ulPropTag = PR_LAST_MODIFICATION_TIME;
+	if (comp)
+		comps = g_slist_prepend (comps, comp);
 
-		t.tv_sec = icaltime_as_timet_with_zone (*itt_cache, icaltimezone_get_utc_timezone ());
-		t.tv_usec = 0;
-		set_SPropValue_proptag_date_timeval (&sprop, PR_LAST_MODIFICATION_TIME, &t);
-		cast_mapi_SPropValue (mem_ctx, &(restriction->res.resProperty.lpProp), &sprop);
+	for (iter = comps; iter; iter = iter->next) {
+		ECalComponentId *id;
+		ECalComponent *old_comp;
 
-		*restrictions = restriction;
-	}
+		comp = iter->data;
+		if (!comp)
+			continue;
 
-	return TRUE;
-}
+		e_cal_component_commit_sequence (comp);
 
-static gboolean
-ecbm_build_restriction_unknown_mids (EMapiConnection *conn,
-				     mapi_id_t fid,
-				     TALLOC_CTX *mem_ctx,
-				     struct mapi_SRestriction **restrictions,
-				     gpointer user_data,
-				     GCancellable *cancellable,
-				     GError **perror)
-{
-	GSList *unknown_mids = user_data, *iter;
-	gint ii;
+		id = e_cal_component_get_id (comp);
+		if (!id) {
+			g_debug ("%s: Failed to get component's ID", G_STRFUNC);
+			continue;
+		}
 
-	g_return_val_if_fail (restrictions != NULL, FALSE);
+		old_comp = e_cal_backend_store_get_component (cbmapi->priv->store, id->uid, id->rid);
+		if (old_comp) {
+			mapi_id_t old_mid, new_mid;
 
-	if (unknown_mids) {
-		struct mapi_SRestriction *restriction;
-		struct mapi_SRestriction_or *or_res;
+			get_comp_mid (e_cal_component_get_icalcomponent (old_comp), &old_mid);
+			get_comp_mid (e_cal_component_get_icalcomponent (comp), &new_mid);
 
-		or_res = talloc_zero_array (mem_ctx, struct mapi_SRestriction_or, g_slist_length (unknown_mids));
-		g_return_val_if_fail (or_res != NULL, FALSE);
+			if (new_mid && old_mid && new_mid != old_mid) {
+				use_uid = e_mapi_util_mapi_id_to_string (new_mid);
+				e_cal_component_set_uid (comp, use_uid);
+				g_free (use_uid);
 
-		for (ii = 0, iter = unknown_mids; iter; ii++, iter = iter->next) {
-			mapi_id_t *pmid = iter->data;
+				e_cal_component_free_id (id);
+				id = e_cal_component_get_id (comp);
+				if (!id) {
+					g_debug ("%s: Failed to re-get component's ID", G_STRFUNC);
+					continue;
+				}
 
-			or_res[ii].rt = RES_PROPERTY;
-			or_res[ii].res.resProperty.relop = RELOP_EQ;
-			or_res[ii].res.resProperty.ulPropTag = PR_MID;
-			or_res[ii].res.resProperty.lpProp.ulPropTag = PR_MID;
-			or_res[ii].res.resProperty.lpProp.value.dbl = *pmid;
+				old_comp = e_cal_backend_store_get_component (cbmapi->priv->store, id->uid, id->rid);
+			}
 		}
 
-		restriction = talloc_zero (mem_ctx, struct mapi_SRestriction);
-		g_return_val_if_fail (restriction != NULL, FALSE);
+		put_component_to_store (cbmapi, comp);
 
-		restriction->rt = RES_OR;
-		restriction->res.resOr.cRes = g_slist_length (unknown_mids);
-		restriction->res.resOr.res = or_res;
+		if (old_comp) {
+			e_cal_backend_notify_component_modified	(backend, e_cal_component_get_icalcomponent (old_comp), e_cal_component_get_icalcomponent (comp));
+			g_object_unref (old_comp);
+		} else {
+			e_cal_backend_notify_component_created (E_CAL_BACKEND (cbmapi), e_cal_component_get_icalcomponent (comp));
+		}
 
-		*restrictions = restriction;
+		e_cal_component_free_id (id);
 	}
 
+	g_slist_free_full (comps, g_object_unref);
+
+	notify_view_progress (cbmapi, obj_index, obj_total);
+
 	return TRUE;
 }
 
-/* Simple workflow for fetching deltas:
- * Poke cache for server_utc_time -> if exists, fetch all items modified after that time,
- * note current time before fetching and update cache with the same after fetching.
- * If server_utc_time does not exist OR is invalid, fetch all items
- * (we anyway process the results only if last_modified has changed).
- */
+static void
+copy_to_known_comps (gpointer key, gpointer value, gpointer user_data)
+{
+	mapi_id_t *pmid = key, *pmidcopy;
+	GSList *comps = value;
+	GHashTable *known_comps = user_data;
+
+	g_return_if_fail (pmid != NULL);
+	g_return_if_fail (known_comps != NULL);
+
+	pmidcopy = g_new0 (mapi_id_t, 1);
+	*pmidcopy = *pmid;
+
+	/* stealing 'comps' pointer here */
+	g_hash_table_insert (known_comps, pmidcopy, comps);
+}
 
 static gboolean
-get_deltas (gpointer handle)
+update_local_cache (ECalBackendMAPI *cbmapi, GCancellable *cancellable)
 {
-	ECalBackendMAPI *cbmapi;
 	ECalBackendMAPIPrivate *priv;
-	icalcomponent_kind kind;
-	icaltimetype itt_current, itt_cache = icaltime_null_time();
-	time_t current_time;
-	struct tm tm;
-	gchar *time_string = NULL;
-	gchar t_str [26];
-	const gchar *serv_time;
-	gboolean use_restriction = TRUE;
-	GSList *ls = NULL;
-	struct deleted_items_data did;
+	EMapiConnection *conn;
 	ESource *source = NULL;
-	guint32 options= MAPI_OPTIONS_FETCH_ALL;
-	gboolean is_public = FALSE;
+	struct ListCalendarObjectsData lco;
+	GSList *iter, *components;
+	mapi_object_t obj_folder;
+	gboolean success;
 	GError *mapi_error = NULL;
+	GHashTable *comps_by_mids;
+	gboolean partial_update;
+	struct FolderBasicPropertiesData fbp;
 
-	if (!handle)
-		return FALSE;
-
-	cbmapi = (ECalBackendMAPI *) handle;
-	priv= cbmapi->priv;
-	kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
+	priv = cbmapi->priv;
 	source = e_backend_get_source (E_BACKEND (cbmapi));
 	if (!e_backend_get_online (E_BACKEND (cbmapi)))
 		return FALSE;
 
 	g_mutex_lock (priv->updating_mutex);
 
-	serv_time = e_cal_backend_store_get_key_value (priv->store, SERVER_UTC_TIME);
-	if (serv_time)
-		itt_cache = icaltime_from_string (serv_time);
-
-	itt_current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
-	current_time = icaltime_as_timet_with_zone (itt_current, icaltimezone_get_utc_timezone ());
-	gmtime_r (&current_time, &tm);
-	strftime (t_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
+	conn = g_object_ref (priv->conn);
 
 	if (g_strcmp0 (e_source_get_property (source, "public"), "yes") == 0) {
-		options |= MAPI_OPTIONS_USE_PFSTORE;
-		is_public = TRUE;
-		use_restriction = FALSE;
-	}
-
-	if (!e_mapi_connection_fetch_items (priv->conn, priv->fid, use_restriction ? ecbm_build_last_modification_restriction : NULL, &itt_cache, NULL,
-					is_public ? NULL : e_mapi_cal_utils_get_props_cb, GINT_TO_POINTER (kind),
-					mapi_cal_get_changes_cb, cbmapi,
-					options, NULL, &mapi_error)) {
-		if (mapi_error) {
-			gchar *msg = g_strdup_printf (_("Failed to fetch changes from a server: %s"), mapi_error->message);
-			e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), msg);
-			g_free (msg);
-			g_error_free (mapi_error);
-		} else {
-			e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Failed to fetch changes from a server"));
-		}
+		success = e_mapi_connection_open_public_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
+	} else {
+		success = e_mapi_connection_open_personal_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
+	}
 
-		g_mutex_unlock (priv->updating_mutex);
-		return FALSE;
+	if (!success) {
+		notify_error (cbmapi, mapi_error, _("Failed to open folder: %s"));
+		goto cleanup;
 	}
 
-	notify_view_completed (cbmapi);
+	success = e_mapi_connection_get_folder_properties (conn, &obj_folder, NULL, NULL,
+					 e_mapi_utils_get_folder_basic_properties_cb, &fbp,
+					 cancellable, &mapi_error);
+	if (!success) {
+		notify_error (cbmapi, mapi_error, _("Failed to get folder properties: %s"));
+		e_mapi_connection_close_folder (conn, &obj_folder, NULL, NULL);
+		goto cleanup;
+	}
 
-	time_string = g_strdup (t_str);
-	e_cal_backend_store_put_key_value (priv->store, SERVER_UTC_TIME, time_string);
-	g_free (time_string);
+	comps_by_mids = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, NULL);
 
-	/* handle deleted items here by going over the entire cache and
-	 * checking for deleted items.*/
+	components = e_cal_backend_store_get_components (priv->store);
+	for (iter = components; iter; iter = iter->next) {
+		ECalComponent *comp = iter->data;
+		mapi_id_t mid, *pmid;
+		GSList *comps;
 
-	/* e_cal_backend_cache_get_keys returns a list of all the keys.
-	 * The items in the list are pointers to internal data,
-	 * so should not be freed, only the list should. */
-	did.cbmapi = cbmapi;
-	did.cache_ids = e_cal_backend_store_get_component_ids (priv->store);
-	did.unknown_mids = NULL;
-	copy_strings_in_slist (did.cache_ids);
-	options = 0;
+		if (!comp)
+			continue;
 
-	if (g_strcmp0 (e_source_get_property (source, "public"), "yes") == 0)
-		options = MAPI_OPTIONS_USE_PFSTORE;
+		get_comp_mid (e_cal_component_get_icalcomponent (comp), &mid);
 
-	if (!e_mapi_connection_fetch_items (priv->conn, priv->fid, NULL, NULL, NULL,
-						mapi_cal_get_idlist, NULL,
-						handle_deleted_items_cb, &did,
-						options, NULL, &mapi_error)) {
-		if (mapi_error) {
-			gchar *msg = g_strdup_printf (_("Failed to fetch changes from a server: %s"), mapi_error->message);
-			e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), msg);
-			g_free (msg);
-			g_error_free (mapi_error);
-		} else {
-			e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Failed to fetch changes from a server"));
-		}
+		pmid = g_new0 (mapi_id_t, 1);
+		*pmid = mid;
 
-		g_slist_foreach (did.cache_ids, (GFunc) g_free, NULL);
-		g_slist_free (did.cache_ids);
-		g_mutex_unlock (priv->updating_mutex);
-		return FALSE;
+		comps = g_slist_prepend (g_hash_table_lookup (comps_by_mids, pmid), comp);
+		g_hash_table_insert (comps_by_mids, pmid, comps);
 	}
+	/* doesn't call unref, because the hash table holds the components */
+	g_slist_free (components);
 
-	options = MAPI_OPTIONS_FETCH_ALL;
-	e_cal_backend_store_freeze_changes (priv->store);
-	for (ls = did.cache_ids; ls; ls = g_slist_next (ls)) {
-		ECalComponent *comp = NULL;
-		icalcomponent *icalcomp = NULL;
+	lco.changed_mids = NULL;
+	lco.known_comps = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, free_component_slist);
+	lco.latest_modified = priv->last_refresh;
 
-		comp = e_cal_backend_store_get_component (priv->store, (const gchar *) ls->data, NULL);
+	g_hash_table_foreach (comps_by_mids, copy_to_known_comps, lco.known_comps);
+	g_hash_table_destroy (comps_by_mids);
+	comps_by_mids = NULL;
 
-		if (!comp)
-			continue;
+	partial_update = priv->last_refresh > 0 && fbp.obj_total == priv->last_obj_total;
+	success = e_mapi_connection_list_objects (conn, &obj_folder,
+		partial_update ? e_mapi_utils_build_last_modify_restriction : NULL, &priv->last_refresh,
+		list_calendar_objects_cb, &lco,
+		cancellable, &mapi_error);
+	if (!success) {
+		notify_error (cbmapi, mapi_error, _("Failed to list objects: %s"));
 
-		icalcomp = e_cal_component_get_icalcomponent (comp);
-		if (kind == icalcomponent_isa (icalcomp)) {
-			gchar *comp_str = NULL;
-			ECalComponentId *id = e_cal_component_get_id (comp);
+		e_mapi_connection_close_folder (conn, &obj_folder, NULL, NULL);
 
-			comp_str = e_cal_component_get_as_string (comp);
-			e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbmapi),
-					id, comp_str, NULL);
-			e_cal_backend_store_remove_component (priv->store, (const gchar *) id->uid, id->rid);
+		g_slist_free_full (lco.changed_mids, g_free);
+		g_hash_table_destroy (lco.known_comps);
 
-			e_cal_component_free_id (id);
-			g_free (comp_str);
-		}
-		g_object_unref (comp);
+		goto cleanup;
 	}
-	e_cal_backend_store_thaw_changes (priv->store);
 
-	g_slist_foreach (did.cache_ids, (GFunc) g_free, NULL);
-	g_slist_free (did.cache_ids);
+	e_cal_backend_store_freeze_changes (priv->store);
 
-	if (did.unknown_mids) {
-		if (g_strcmp0 (e_source_get_property (source, "public"), "yes") == 0) {
-			options |= MAPI_OPTIONS_USE_PFSTORE;
-			is_public = TRUE;
-		}
+	if (!partial_update)
+		g_hash_table_foreach (lco.known_comps, drop_removed_comps_cb, cbmapi);
+	g_hash_table_destroy (lco.known_comps);
+	lco.known_comps = NULL;
 
-		if (!e_mapi_connection_fetch_items (priv->conn, priv->fid, ecbm_build_restriction_unknown_mids, did.unknown_mids, NULL,
-					is_public ? NULL : e_mapi_cal_utils_get_props_cb, GINT_TO_POINTER (kind),
-					mapi_cal_get_changes_cb, cbmapi,
-					options, NULL, &mapi_error)) {
-			g_slist_foreach (did.unknown_mids, (GFunc) g_free, NULL);
-			g_slist_free (did.unknown_mids);
-
-			if (mapi_error) {
-				gchar *msg = g_strdup_printf (_("Failed to fetch changes from a server: %s"), mapi_error->message);
-				e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), msg);
-				g_free (msg);
-				g_error_free (mapi_error);
-			} else {
-				e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Failed to fetch changes from a server"));
-			}
+	if (lco.changed_mids) {
+		success = e_mapi_connection_transfer_objects (conn, &obj_folder,
+			lco.changed_mids,
+			transfer_calendar_objects_cb, cbmapi,
+			cancellable, &mapi_error);
+
+		e_cal_backend_store_thaw_changes (priv->store);
+
+		if (!success) {
+			notify_error (cbmapi, mapi_error, _("Failed to transfer objects: %s"));
+
+			e_mapi_connection_close_folder (conn, &obj_folder, NULL, NULL);
 
-			g_mutex_unlock (priv->updating_mutex);
-			return FALSE;
+			g_slist_free_full (lco.changed_mids, g_free);
+
+			goto cleanup;
 		}
-		g_slist_foreach (did.unknown_mids, (GFunc) g_free, NULL);
-		g_slist_free (did.unknown_mids);
+
+		g_slist_free_full (lco.changed_mids, g_free);
+	} else {
+		e_cal_backend_store_thaw_changes (priv->store);
+	}
+
+	priv->last_obj_total = fbp.obj_total;
+	priv->last_refresh = lco.latest_modified;
+
+	success = e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
+	if (!success) {
+		notify_error (cbmapi, mapi_error, _("Failed to close folder: %s"));
+
+		goto cleanup;
 	}
 
+ cleanup:
+	g_object_unref (conn);
 	g_mutex_unlock (priv->updating_mutex);
-	return TRUE;
+
+	return success;
 }
 
 static void
@@ -874,7 +841,7 @@ ecbm_get_object (ECalBackend *backend, EDataCal *cal, GCancellable *cancellable,
 		 * also not on the server to prevent for a race condition where we
 		 * might otherwise mistakenly generate a new UID */
 		g_mutex_unlock (priv->mutex);
-		get_deltas (cbmapi);
+		update_local_cache (cbmapi, cancellable);
 		g_mutex_lock (priv->mutex);
 		comp = e_cal_backend_store_get_component (priv->store, uid, rid);
 	}
@@ -913,8 +880,6 @@ ecbm_get_object_list (ECalBackend *backend, EDataCal *cal, GCancellable *cancell
 
 	g_mutex_lock (priv->mutex);
 
-//	d(g_message (G_STRLOC ": Getting object list (%s)", sexp));
-
 	if (!strcmp (sexp, "#t"))
 		search_needed = FALSE;
 
@@ -978,24 +943,28 @@ delta_thread (gpointer data)
 {
 	ECalBackendMAPI *cbmapi;
 	ECalBackendMAPIPrivate *priv;
+	GCancellable *cancellable;
 	GTimeVal timeout;
 
 	cbmapi = (ECalBackendMAPI *)(data);
 	g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), NULL);
 
 	priv = cbmapi->priv;
+	cancellable = g_object_ref (priv->cancellable);
 
 	timeout.tv_sec = 0;
 	timeout.tv_usec = 0;
 
-	while (TRUE)	{
-		gboolean succeeded = get_deltas (cbmapi);
+	while (!g_cancellable_is_cancelled (cancellable)) {
+		update_local_cache (cbmapi, cancellable);
 
 		g_mutex_lock (priv->dlock->mutex);
 
-		if (!succeeded || priv->dlock->exit)
+		if (priv->dlock->exit)
 			break;
 
+		notify_view_completed (cbmapi);
+
 		g_get_current_time (&timeout);
 		g_time_val_add (&timeout, get_cache_refresh_interval () * 1000);
 		g_cond_timed_wait (priv->dlock->cond, priv->dlock->mutex, &timeout);
@@ -1006,24 +975,28 @@ delta_thread (gpointer data)
 		g_mutex_unlock (priv->dlock->mutex);
 	}
 
+	g_object_unref (cancellable);
 	g_mutex_unlock (priv->dlock->mutex);
 	priv->dthread = NULL;
+
 	return NULL;
 }
 
-static gboolean
-fetch_deltas (ECalBackendMAPI *cbmapi)
+static void
+run_delta_thread (ECalBackendMAPI *cbmapi)
 {
 	ECalBackendMAPIPrivate *priv;
 	GError *error = NULL;
 
-	g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), FALSE);
+	g_return_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi));
 
 	priv = cbmapi->priv;
 
 	/* If the thread is already running just return back */
-	if (priv->dthread)
-		return FALSE;
+	if (priv->dthread) {
+		g_cond_signal (priv->dlock->cond);
+		return;
+	}
 
 	if (!priv->dlock) {
 		priv->dlock = g_new0 (SyncDelta, 1);
@@ -1037,207 +1010,12 @@ fetch_deltas (ECalBackendMAPI *cbmapi)
 		g_warning (G_STRLOC ": %s", error->message);
 		g_error_free (error);
 	}
-
-	return TRUE;
-}
-
-static gboolean
-start_fetch_deltas (gpointer data)
-{
-	ECalBackendMAPI *cbmapi;
-	ECalBackendMAPIPrivate *priv;
-
-	cbmapi = (ECalBackendMAPI *)(data);
-	g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), FALSE);
-
-	priv = cbmapi->priv;
-
-	fetch_deltas (cbmapi);
-
-	priv->timeout_id = 0;
-
-	return FALSE;
-}
-
-static gboolean
-mapi_cal_cache_create_cb (FetchItemsCallbackData *item_data,
-			  gpointer data,
-			  GCancellable *cancellable,
-			  GError **perror)
-{
-	struct mapi_SPropValue_array *properties = item_data->properties;
-	const mapi_id_t mid = item_data->mid;
-	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);
-	icalcomponent_kind kind;
-        ECalComponent *comp = NULL;
-	gchar *tmp = NULL;
-	const bool *recurring = NULL;
-	const gchar *cache_dir;
-
-	kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
-	cache_dir = e_cal_backend_get_cache_dir (E_CAL_BACKEND (cbmapi));
-
-//	e_mapi_debug_property_dump (properties);
-
-	switch (kind) {
-		case ICAL_VTODO_COMPONENT:
-			/* FIXME: Evolution does not support recurring tasks */
-			recurring = e_mapi_util_find_array_namedid (properties, item_data->conn, item_data->fid, PidLidTaskFRecurring);
-			if (recurring && *recurring) {
-				g_warning ("Encountered a recurring task.");
-				e_mapi_util_free_stream_list (&streams);
-				e_mapi_util_free_recipient_list (&recipients);
-				e_mapi_util_free_attachment_list (&attachments);
-				return TRUE;
-			}
-			break;
-		case ICAL_VEVENT_COMPONENT  :
-		case ICAL_VJOURNAL_COMPONENT:
-			break;
-		default:
-			return FALSE;
-	}
-
-	tmp = e_mapi_util_mapi_id_to_string (mid);
-	comp = e_mapi_cal_util_mapi_props_to_comp (item_data->conn, item_data->fid, kind, tmp, properties,
-							streams, recipients, attachments,
-							cache_dir, icaltimezone_get_utc_timezone (), FALSE, &detached);
-	g_free (tmp);
-
-	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);
-
-	e_mapi_util_free_stream_list (&streams);
-	e_mapi_util_free_recipient_list (&recipients);
-	e_mapi_util_free_attachment_list (&attachments);
-
-	notify_view_progress (cbmapi, item_data->index, item_data->total);
-
-	return TRUE;
-}
-
-static gboolean
-populate_cache (ECalBackendMAPI *cbmapi, GError **perror)
-{
-	ECalBackendMAPIPrivate *priv;
-	ESource *source = NULL;
-	icalcomponent_kind kind;
-	icaltimetype itt_current;
-	time_t current_time;
-	struct tm tm;
-	gchar *time_string = NULL;
-	gchar t_str [26];
-	guint32 options= MAPI_OPTIONS_FETCH_ALL;
-	gboolean is_public = FALSE;
-	GError *mapi_error = NULL;
-
-	priv = cbmapi->priv;
-
-	g_mutex_lock (priv->mutex);
-	if (priv->populating_cache) {
-		g_mutex_unlock (priv->mutex);
-		return TRUE; /* Success */
-	}
-	priv->populating_cache = TRUE;
-	g_mutex_unlock (priv->mutex);
-
-	source = e_backend_get_source (E_BACKEND (cbmapi));
-	kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
-
-	itt_current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
-	current_time = icaltime_as_timet_with_zone (itt_current, icaltimezone_get_utc_timezone ());
-	gmtime_r (&current_time, &tm);
-	strftime (t_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
-
-	if (g_strcmp0 (e_source_get_property (source, "public"), "yes") == 0) {
-		options |= MAPI_OPTIONS_USE_PFSTORE;
-		is_public = TRUE;
-	}
-
-	if (!e_mapi_connection_fetch_items (priv->conn, priv->fid, NULL, NULL, NULL,
-					is_public ? NULL : e_mapi_cal_utils_get_props_cb, GINT_TO_POINTER (kind),
-					mapi_cal_cache_create_cb, cbmapi,
-					options, NULL, &mapi_error)) {
-		e_cal_backend_store_thaw_changes (priv->store);
-		g_mutex_lock (priv->mutex);
-		priv->populating_cache = FALSE;
-		g_mutex_unlock (priv->mutex);
-
-		mapi_error_to_edc_error (perror, mapi_error, OtherError, _("Failed to fetch items from a server"));
-		if (mapi_error)
-			g_error_free (mapi_error);
-
-		return FALSE;
-	}
-
-	notify_view_completed (cbmapi);
-
-	time_string = g_strdup (t_str);
-	e_cal_backend_store_put_key_value (priv->store, SERVER_UTC_TIME, time_string);
-
-	g_free (time_string);
-
-	e_cal_backend_store_put_key_value (priv->store, CACHE_MARKER, "1");
-
-	g_mutex_lock (priv->mutex);
-	priv->populating_cache = FALSE;
-	g_mutex_unlock (priv->mutex);
-
-	return TRUE;
-}
-
-static gpointer
-cache_init (ECalBackendMAPI *cbmapi)
-{
-	ECalBackendMAPIPrivate *priv = cbmapi->priv;
-
-	if (!e_cal_backend_store_get_key_value (priv->store, CACHE_MARKER)) {
-		/* Populate the cache for the first time.*/
-		if (!populate_cache (cbmapi, NULL)) {
-			g_warning (G_STRLOC ": Could not populate the cache");
-			/*FIXME  why dont we do a notify here */
-			return NULL;
-		} else {
-			/*  Set delta fetch timeout */
-			priv->timeout_id = g_timeout_add (get_cache_refresh_interval (), start_fetch_deltas, (gpointer) cbmapi);
-
-			return NULL;
-		}
-	}
-
-	g_mutex_lock (priv->mutex);
-	fetch_deltas (cbmapi);
-	g_mutex_unlock (priv->mutex);
-
-	return NULL;
 }
 
 static void
 ecbm_connect (ECalBackendMAPI *cbmapi, GError **perror)
 {
 	ECalBackendMAPIPrivate *priv;
-	GThread *thread;
-	GError *error = NULL;
 
 	priv = cbmapi->priv;
 
@@ -1252,27 +1030,16 @@ ecbm_connect (ECalBackendMAPI *cbmapi, GError **perror)
 	}
 
 	/* We have established a connection */
-	if (priv->store && priv->fid && e_cal_backend_store_put_key_value (priv->store, CACHE_MARKER, "1")) {
+	if (priv->store && priv->fid) {
 		e_cal_backend_notify_online (E_CAL_BACKEND (cbmapi), TRUE);
 
 		if (priv->mode_changed && !priv->dthread) {
 			priv->mode_changed = FALSE;
-			fetch_deltas (cbmapi);
+			run_delta_thread (cbmapi);
 		}
-
-		/* FIXME: put server UTC time in cache */
-		return /* Success */;
 	}
 
 	priv->mode_changed = FALSE;
-
-	/* spawn a new thread for caching the calendar items */
-	thread = g_thread_create ((GThreadFunc) cache_init, cbmapi, FALSE, &error);
-	if (!thread) {
-		g_warning (G_STRLOC ": %s", error->message);
-		g_error_free (error);
-		g_propagate_error (perror, EDC_ERROR_EX (OtherError, _("Could not create thread for populating cache")));
-	}
 }
 
 static void
@@ -1504,23 +1271,6 @@ capture_req_props (FetchItemsCallbackData *item_data,
 	return TRUE;
 }
 
-static void
-get_comp_mid (icalcomponent *icalcomp, mapi_id_t *mid)
-{
-	gchar *x_mid;
-
-	g_return_if_fail (icalcomp != NULL);
-	g_return_if_fail (mid != NULL);
-
-	x_mid = e_mapi_cal_utils_get_icomp_x_prop (icalcomp, "X-EVOLUTION-MAPI-MID");
-	if (x_mid) {
-		e_mapi_util_mapi_id_from_string (x_mid, mid);
-		g_free (x_mid);
-	} else {
-		e_mapi_util_mapi_id_from_string (icalcomponent_get_uid (icalcomp), mid);
-	}
-}
-
 static gboolean
 ecbm_build_global_id_restriction (EMapiConnection *conn,
 				  mapi_id_t fid,
@@ -1760,8 +1510,7 @@ ecbm_create_object (ECalBackend *backend, EDataCal *cal, GCancellable *cancellab
 		return;
 	}
 
-	if (!fetch_deltas(cbmapi))
-		g_cond_signal (priv->dlock->cond);
+	run_delta_thread (cbmapi);
 
 	g_object_unref (comp);
 	e_mapi_util_free_recipient_list (&recipients);
@@ -1917,7 +1666,7 @@ ecbm_modify_object (ECalBackend *backend, EDataCal *cal, GCancellable *cancellab
 		/* check if the object exists */
 		cache_comp = e_cal_backend_store_get_component (priv->store, uid, rid);
 		if (!cache_comp) {
-			get_deltas (cbmapi);
+			update_local_cache (cbmapi, cancellable);
 			cache_comp = e_cal_backend_store_get_component (priv->store, uid, rid);
 		}
 
@@ -2582,6 +2331,9 @@ ecbm_notify_online_cb (ECalBackend *backend, GParamSpec *pspec)
 		}
 	} else {
 		priv->read_only = TRUE;
+
+		g_object_unref (priv->conn);
+		priv->conn = NULL;
 	}
 
 	e_cal_backend_notify_readonly (backend, priv->read_only);
@@ -3408,6 +3160,28 @@ ecbm_op_get_free_busy (ECalBackend *backend, EDataCal *cal, guint32 opid, GCance
 }
 
 static void
+ecbm_dispose (GObject *object)
+{
+	ECalBackendMAPI *cbmapi;
+	ECalBackendMAPIPrivate *priv;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (E_IS_CAL_BACKEND_MAPI (object));
+
+	cbmapi = E_CAL_BACKEND_MAPI (object);
+	priv = cbmapi->priv;
+
+	if (priv && priv->cancellable) {
+		g_cancellable_cancel (priv->cancellable);
+		g_object_unref (priv->cancellable);
+		priv->cancellable = NULL;
+	}
+
+	if (G_OBJECT_CLASS (e_cal_backend_mapi_parent_class)->dispose)
+		(* G_OBJECT_CLASS (e_cal_backend_mapi_parent_class)->dispose) (object);
+}
+
+static void
 ecbm_finalize (GObject *object)
 {
 	ECalBackendMAPI *cbmapi;
@@ -3514,6 +3288,7 @@ e_cal_backend_mapi_class_init (ECalBackendMAPIClass *class)
 	backend_class = (ECalBackendClass *) class;
 
 	object_class->finalize = ecbm_finalize;
+	object_class->dispose = ecbm_dispose;
 
 	/* functions done asynchronously */
 	backend_class->get_backend_property = ecbm_op_get_backend_property;
@@ -3552,8 +3327,10 @@ e_cal_backend_mapi_init (ECalBackendMAPI *cbmapi)
 	/* create the mutex for thread safety */
 	priv->mutex = g_mutex_new ();
 	priv->updating_mutex = g_mutex_new ();
-	priv->populating_cache = FALSE;
 	priv->op_queue = e_mapi_operation_queue_new ((EMapiOperationQueueFunc) ecbm_operation_cb, cbmapi);
+	priv->last_refresh = -1;
+	priv->last_obj_total = -1;
+	priv->cancellable = g_cancellable_new ();
 
 	cbmapi->priv = priv;
 
diff --git a/src/camel/camel-mapi-folder.c b/src/camel/camel-mapi-folder.c
index 2d45a56..c5c5e0a 100644
--- a/src/camel/camel-mapi-folder.c
+++ b/src/camel/camel-mapi-folder.c
@@ -419,43 +419,6 @@ add_message_to_cache (CamelMapiFolder *mapi_folder, const gchar *uid, CamelMimeM
 	camel_folder_summary_unlock (folder->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
 }
 
-static gboolean
-build_last_modify_restriction (EMapiConnection *conn,
-		               mapi_id_t fid,
-			       TALLOC_CTX *mem_ctx,
-			       struct mapi_SRestriction **restrictions,
-			       gpointer user_data,
-			       GCancellable *cancellable,
-			       GError **perror)
-{
-	const time_t *latest_last_modify = user_data;
-	struct mapi_SRestriction *restriction = NULL;
-
-	g_return_val_if_fail (restrictions != NULL, FALSE);
-
-	if (latest_last_modify && *latest_last_modify > 0) {
-		struct SPropValue sprop;
-		struct timeval t;
-
-		restriction = talloc_zero (mem_ctx, struct mapi_SRestriction);
-		g_return_val_if_fail (restriction != NULL, FALSE);
-
-		restriction->rt = RES_PROPERTY;
-		restriction->res.resProperty.relop = RELOP_GT;
-		restriction->res.resProperty.ulPropTag = PR_LAST_MODIFICATION_TIME;
-
-		t.tv_sec = *latest_last_modify;
-		t.tv_usec = 0;
-
-		set_SPropValue_proptag_date_timeval (&sprop, PR_LAST_MODIFICATION_TIME, &t);
-		cast_mapi_SPropValue (mem_ctx, &(restriction->res.resProperty.lpProp), &sprop);
-	}
-
-	*restrictions = restriction;
-
-	return TRUE;
-}
-
 struct GatherChangedObjectsData
 {
 	CamelFolderSummary *summary;
@@ -924,7 +887,7 @@ camel_mapi_folder_fetch_summary (CamelFolder *folder, GCancellable *cancellable,
 
 	if (status) {
 		status = e_mapi_connection_list_objects (conn, &obj_folder,
-			full_download ? NULL : build_last_modify_restriction, &mapi_summary->latest_last_modify,
+			full_download ? NULL : e_mapi_utils_build_last_modify_restriction, &mapi_summary->latest_last_modify,
 			gather_changed_objects_to_slist, &gco, cancellable, mapi_error);
 	}
 
diff --git a/src/libexchangemapi/e-mapi-cal-utils.c b/src/libexchangemapi/e-mapi-cal-utils.c
index 79f884b..c231075 100644
--- a/src/libexchangemapi/e-mapi-cal-utils.c
+++ b/src/libexchangemapi/e-mapi-cal-utils.c
@@ -2067,9 +2067,9 @@ static gboolean
 emcu_check_id_exists_cb (EMapiConnection *conn,
 			 mapi_id_t fid,
 			 TALLOC_CTX *mem_ctx,
-			 const ListObjectsData *item_data,
-			 guint32 item_index,
-			 guint32 items_total,
+			 const ListObjectsData *object_data,
+			 guint32 obj_index,
+			 guint32 obj_total,
 			 gpointer user_data,
 			 GCancellable *cancellable,
 			 GError **perror)
diff --git a/src/libexchangemapi/e-mapi-connection.c b/src/libexchangemapi/e-mapi-connection.c
index a02692f..efc4840 100644
--- a/src/libexchangemapi/e-mapi-connection.c
+++ b/src/libexchangemapi/e-mapi-connection.c
@@ -40,6 +40,8 @@
 #include "e-mapi-utils.h"
 #include "e-mapi-mail-utils.h"
 #include "e-mapi-fast-transfer.h"
+#include "e-mapi-openchange.h"
+
 #include <param.h>
 
 static void register_connection (EMapiConnection *conn);
@@ -1711,7 +1713,7 @@ e_mapi_connection_get_folder_properties (EMapiConnection *conn,
 	if (g_cancellable_set_error_if_cancelled (cancellable, perror))
 		goto cleanup;
 
-	spropTagArray = set_SPropTagArray (mem_ctx, 1, PR_FID);
+	spropTagArray = set_SPropTagArray (mem_ctx, 3, PidTagFolderId, PidTagLastModificationTime, PidTagContentCount);
 	if (brp_cb) {
 		if (!brp_cb (conn, mapi_object_get_id (obj_folder), mem_ctx, spropTagArray, brp_cb_user_data, cancellable, perror)) {
 			goto cleanup;
@@ -1809,7 +1811,7 @@ e_mapi_connection_get_folder_properties (EMapiConnection *conn,
 			}
 		}
 	} else {
-		ms = GetPropsAll (obj_folder, MAPI_PROPS_SKIP_NAMEDID_CHECK | MAPI_UNICODE, properties);
+		ms = GetPropsAll (obj_folder, MAPI_UNICODE, properties);
 		if (ms != MAPI_E_SUCCESS) {
 			make_mapi_error (perror, "GetPropsAll", ms);
 			goto cleanup;
@@ -1904,6 +1906,7 @@ list_objects_internal_cb (EMapiConnection *conn,
 	struct ListObjectsInternalData *loi_data = user_data;
 	ListObjectsData lod;
 	const mapi_id_t	*pmid;
+	const gchar *msg_class;
 	const uint32_t *pmsg_flags;
 	struct SPropValue *last_modified;
 	struct timeval t;
@@ -1913,11 +1916,13 @@ list_objects_internal_cb (EMapiConnection *conn,
 	e_return_val_mapi_error_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 	e_return_val_mapi_error_if_fail (srow != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 
-	pmid = get_SPropValue_SRow_data (srow, PR_MID);
-	pmsg_flags = get_SPropValue_SRow_data (srow, PR_MESSAGE_FLAGS);
-	last_modified = get_SPropValue_SRow (srow, PR_LAST_MODIFICATION_TIME);
+	pmid = get_SPropValue_SRow_data (srow, PidTagMid);
+	msg_class = get_SPropValue_SRow_data (srow, PidTagMessageClass);
+	pmsg_flags = get_SPropValue_SRow_data (srow, PidTagMessageFlags);
+	last_modified = get_SPropValue_SRow (srow, PidTagLastModificationTime);
 
 	lod.mid = pmid ? *pmid : 0;
+	lod.msg_class = msg_class;
 	lod.msg_flags = pmsg_flags ? *pmsg_flags : 0;
 
 	if (last_modified && get_mapi_SPropValue_date_timeval (&t, *last_modified) == MAPI_E_SUCCESS)
@@ -2136,10 +2141,11 @@ e_mapi_connection_list_objects (EMapiConnection *conn,
 		goto cleanup;
 	}
 
-	propTagArray = set_SPropTagArray (mem_ctx, 0x3,
-					  PR_MID,
-					  PR_MESSAGE_FLAGS,
-					  PR_LAST_MODIFICATION_TIME);
+	propTagArray = set_SPropTagArray (mem_ctx, 0x4,
+					  PidTagMid,
+					  PidTagMessageClass,
+					  PidTagMessageFlags,
+					  PidTagLastModificationTime);
 
 	/* Set primary columns to be fetched */
 	ms = SetColumns (&obj_table, propTagArray);
@@ -2357,6 +2363,7 @@ struct EnsureAdditionalPropertiesData
 	TransferObjectCB cb;
 	gpointer cb_user_data;
 	mapi_object_t *obj_folder;
+	guint32 downloaded;
 };
 
 static gboolean
@@ -2457,9 +2464,503 @@ ensure_additional_properties_cb (EMapiConnection *conn,
 		}
 	}
 
+	eap->downloaded++;
+
 	return eap->cb (conn, mem_ctx, object, obj_index, obj_total, eap->cb_user_data, cancellable, perror);
 }
 
+static enum MAPISTATUS
+fetch_object_property_as_stream (EMapiConnection *conn,
+				 TALLOC_CTX *mem_ctx,
+				 mapi_object_t *obj_message,
+				 uint32_t proptag,
+				 struct SBinary_short *bin,
+				 GCancellable *cancellable,
+				 GError **perror)
+{
+	enum MAPISTATUS ms;
+	mapi_object_t obj_stream;
+	uint32_t buf_size, max_read;
+	uint16_t off_data, cn_read;
+	gboolean done = FALSE;
+
+	g_return_val_if_fail (conn != NULL, MAPI_E_INVALID_PARAMETER);
+	g_return_val_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER);
+	g_return_val_if_fail (obj_message != NULL, MAPI_E_INVALID_PARAMETER);
+	g_return_val_if_fail (bin != NULL, MAPI_E_INVALID_PARAMETER);
+
+	mapi_object_init (&obj_stream);
+
+	ms = OpenStream (obj_message, proptag, STREAM_ACCESS_READ, &obj_stream);
+	if (ms != MAPI_E_SUCCESS) {
+		make_mapi_error (perror, "OpenStream", ms);
+		goto cleanup;
+	}
+
+	bin->cb = 0;
+
+	ms = GetStreamSize (&obj_stream, &buf_size);
+	if (ms != MAPI_E_SUCCESS) {
+		make_mapi_error (perror, "GetStreamSize", ms);
+		goto cleanup;
+	}
+
+	bin->cb = buf_size;
+	bin->lpb = talloc_size (mem_ctx, bin->cb + 1);
+	if (!bin->lpb || !bin->cb)
+		goto cleanup;
+
+	/* determine max_read first, to read by chunks as long as possible */
+	off_data = 0;
+	max_read = buf_size > STREAM_MAX_READ_SIZE ? STREAM_MAX_READ_SIZE : buf_size;
+	do {
+		ms = ReadStream (&obj_stream, (bin->lpb) + off_data, max_read, &cn_read);
+		if (ms == MAPI_E_SUCCESS) {
+			if (cn_read == 0) {
+				done = TRUE;
+			} else {
+				off_data += cn_read;
+				if (off_data >= buf_size)
+					done = TRUE;
+			}
+			break;
+		}
+
+		if (ms == 0x2c80)
+			max_read = max_read >> 1;
+		else
+			max_read = STREAM_MAX_READ_SIZE_DF;
+
+		if (max_read < STREAM_MAX_READ_SIZE_DF)
+			max_read = STREAM_MAX_READ_SIZE_DF;
+	} while (ms == 0x2c80); /* an error when max_read is too large? */
+
+	while (!done) {
+		ms = ReadStream (&obj_stream, bin->lpb + off_data, max_read, &cn_read);
+		if (ms != MAPI_E_SUCCESS) {
+			make_mapi_error (perror, "ReadStream", ms);
+			done = TRUE;
+		} else if (cn_read == 0) {
+			done = TRUE;
+		} else {
+			off_data += cn_read;
+			if (off_data >= buf_size)
+				done = TRUE;
+		}
+	}
+
+ cleanup:
+	mapi_object_release (&obj_stream);
+
+	return ms;
+}
+
+static enum MAPISTATUS
+e_mapi_connection_fetch_object_internal (EMapiConnection *conn,
+					 TALLOC_CTX *mem_ctx,
+					 mapi_object_t *obj_message,
+					 struct EnsureAdditionalPropertiesData *eap,
+					 EMapiObject **out_object,
+					 GCancellable *cancellable,
+					 GError **perror);
+
+struct FetchObjectAttachmentData
+{
+	mapi_object_t *obj_message;
+	struct EnsureAdditionalPropertiesData *eap;
+	EMapiObject *object; /* to add attachments to */
+};
+
+static gboolean
+fetch_object_attachment_cb (EMapiConnection *conn,
+			    mapi_id_t fid,
+			    TALLOC_CTX *mem_ctx,
+			    struct SRow *srow,
+			    guint32 row_index,
+			    guint32 rows_total,
+			    gpointer user_data,
+			    GCancellable *cancellable,
+			    GError **perror)
+{
+	enum MAPISTATUS ms;
+	struct FetchObjectAttachmentData *foa = user_data;
+	EMapiAttachment *attachment = NULL;
+	mapi_object_t obj_attach;
+	const uint32_t *attach_num, *attach_method;
+
+	g_return_val_if_fail (conn != NULL, FALSE);
+	g_return_val_if_fail (mem_ctx != NULL, FALSE);
+	g_return_val_if_fail (srow != NULL, FALSE);
+	g_return_val_if_fail (user_data != NULL, FALSE);
+	g_return_val_if_fail (foa->obj_message != NULL, FALSE);
+	g_return_val_if_fail (foa->object != NULL, FALSE);
+
+	mapi_object_init (&obj_attach);
+
+	attach_num = e_mapi_util_find_row_propval (srow, PidTagAttachNumber);
+	if (!attach_num)
+		return FALSE;
+
+	ms = OpenAttach (foa->obj_message, *attach_num, &obj_attach);
+	if (ms != MAPI_E_SUCCESS) {
+		make_mapi_error (perror, "OpenAttach", ms);
+		goto cleanup;
+	}
+
+	attachment = e_mapi_attachment_new (foa->object);
+
+	ms = GetPropsAll (&obj_attach, MAPI_UNICODE, &attachment->properties);
+	if (ms != MAPI_E_SUCCESS) {
+		make_mapi_error (perror, "Attachment::GetPropsAll", ms);
+		goto cleanup;
+	}
+
+	attach_method = e_mapi_util_find_row_propval (srow, PidTagAttachMethod);
+	if (attach_method && *attach_method == ATTACH_BY_VALUE) {
+		if (!e_mapi_util_find_array_propval (&attachment->properties, PidTagAttachDataBinary)) {
+			struct SBinary_short bin;
+
+			ms = fetch_object_property_as_stream (conn, mem_ctx, &obj_attach, PidTagAttachDataBinary, &bin, cancellable, perror);
+			if (ms != MAPI_E_SUCCESS) {
+				make_mapi_error (perror, "Attachment::fetch PidTagAttachDataBinary", ms);
+				goto cleanup;
+			}
+
+			attachment->properties.cValues++;
+			attachment->properties.lpProps = talloc_realloc (mem_ctx,
+									 attachment->properties.lpProps,
+									 struct mapi_SPropValue,
+									 attachment->properties.cValues + 1);
+			attachment->properties.lpProps[attachment->properties.cValues - 1].ulPropTag = PidTagAttachDataBinary;
+			attachment->properties.lpProps[attachment->properties.cValues - 1].value.bin = bin;
+			attachment->properties.lpProps[attachment->properties.cValues].ulPropTag = 0;
+		}
+	} else if (attach_method && *attach_method == ATTACH_EMBEDDED_MSG) {
+		mapi_object_t obj_emb_msg;
+
+		mapi_object_init (&obj_emb_msg);
+
+		if (OpenEmbeddedMessage (&obj_attach, &obj_emb_msg, MAPI_READONLY) == MAPI_E_SUCCESS) {
+			e_mapi_connection_fetch_object_internal (conn, mem_ctx, &obj_emb_msg, foa->eap, &attachment->embedded_object, cancellable, perror);
+		}
+
+		mapi_object_release (&obj_emb_msg);
+	}
+
+ cleanup:
+	mapi_object_release (&obj_attach);
+
+	if (ms == MAPI_E_SUCCESS) {
+		if (!foa->object->attachments) {
+			foa->object->attachments = attachment;
+		} else {
+			EMapiAttachment *attach = foa->object->attachments;
+			while (attach->next)
+				attach = attach->next;
+			attach->next = attachment;
+		}
+	} else {
+		e_mapi_attachment_free (attachment);
+	}
+
+	return ms == MAPI_E_SUCCESS;
+}
+
+static enum MAPISTATUS
+e_mapi_connection_fetch_object_internal (EMapiConnection *conn,
+					 TALLOC_CTX *mem_ctx,
+					 mapi_object_t *obj_message,
+					 struct EnsureAdditionalPropertiesData *eap,
+					 EMapiObject **out_object,
+					 GCancellable *cancellable,
+					 GError **perror)
+{
+	enum MAPISTATUS ms;
+	EMapiObject *object;
+	uint16_t ui16, uj16, np_count = 0, *np_propID = NULL;
+	uint32_t ui32;
+	struct MAPINAMEID *np_nameid = NULL;
+	const bool *has_attachments;
+	struct SPropTagArray recipient_proptags;
+	struct SRowSet recipient_rows;
+	mapi_object_t attach_table;
+
+	g_return_val_if_fail (conn != NULL, MAPI_E_INVALID_PARAMETER);
+	g_return_val_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER);
+	g_return_val_if_fail (obj_message != NULL, MAPI_E_INVALID_PARAMETER);
+	g_return_val_if_fail (eap != NULL, MAPI_E_INVALID_PARAMETER);
+	g_return_val_if_fail (out_object != NULL, MAPI_E_INVALID_PARAMETER);
+
+	mapi_object_init (&attach_table);
+
+	object = e_mapi_object_new (mem_ctx);
+
+	ms = GetPropsAll (obj_message, MAPI_UNICODE, &object->properties);
+	if (ms != MAPI_E_SUCCESS) {
+		make_mapi_error (perror, "GetPropsAll", ms);
+		goto cleanup;
+	}
+
+	/* to transform named ids to their PidLid or PidName tags, like the fast-transfer does */
+	ms = QueryNamedProperties (obj_message, 0, NULL, &np_count, &np_propID, &np_nameid);
+	if (ms != MAPI_E_SUCCESS) {
+		make_mapi_error (perror, "QueryNamedProperties", ms);
+		goto cleanup;
+	}
+
+	if (np_count && np_propID && np_nameid) {
+		for (ui16 = 0; ui16 < np_count; ui16++) {
+			uint32_t proptag = np_propID[ui16];
+
+			for (uj16 = 0; uj16 < object->properties.cValues; uj16++) {
+				if (object->properties.lpProps[uj16].ulPropTag == proptag) {
+					uint32_t lid = MAPI_E_RESERVED;
+					char *guid;
+
+					guid = GUID_string (mem_ctx, &(np_nameid[ui16].lpguid));
+
+					if (np_nameid[ui16].ulKind == MNID_ID) {
+						if (e_mapi_nameid_lid_lookup_canonical (np_nameid[ui16].kind.lid, guid, &lid) != MAPI_E_SUCCESS)
+							lid = MAPI_E_RESERVED;
+					} else if (np_nameid[ui16].ulKind == MNID_STRING) {
+						if (e_mapi_nameid_string_lookup_canonical (np_nameid[ui16].kind.lpwstr.Name, guid, &lid) != MAPI_E_SUCCESS)
+							lid = MAPI_E_RESERVED;
+					}
+
+					talloc_free (guid);
+
+					if (lid != MAPI_E_RESERVED && (lid & 0xFFFF) == (proptag & 0xFFFF)) {
+						object->properties.lpProps[uj16].ulPropTag = lid;
+					}
+
+					break;
+				}
+			}
+		}
+	}
+
+	talloc_free (np_propID);
+	talloc_free (np_nameid);
+
+	/* ensure certain properties */
+	if (!e_mapi_util_find_array_propval (&object->properties, PidTagHtml)) {
+		uint8_t best_body = 0;
+
+		if (GetBestBody (obj_message, &best_body) == MAPI_E_SUCCESS && best_body == olEditorHTML) {
+			struct SBinary_short bin;
+
+			ms = fetch_object_property_as_stream (conn, mem_ctx, obj_message, PidTagHtml, &bin, cancellable, perror);
+			if (ms != MAPI_E_SUCCESS) {
+				make_mapi_error (perror, "Object::fetch PidTagHtml", ms);
+				goto cleanup;
+			}
+
+			object->properties.cValues++;
+			object->properties.lpProps = talloc_realloc (mem_ctx,
+								     object->properties.lpProps,
+								     struct mapi_SPropValue,
+								     object->properties.cValues + 1);
+			object->properties.lpProps[object->properties.cValues - 1].ulPropTag = PidTagHtml;
+			object->properties.lpProps[object->properties.cValues - 1].value.bin = bin;
+			object->properties.lpProps[object->properties.cValues].ulPropTag = 0;
+		}
+	}
+
+	if (!e_mapi_util_find_array_propval (&object->properties, PidTagBody)) {
+		struct SBinary_short bin;
+
+		if (fetch_object_property_as_stream (conn, mem_ctx, obj_message, PidTagBody, &bin, cancellable, NULL) == MAPI_E_SUCCESS) {
+			object->properties.cValues++;
+			object->properties.lpProps = talloc_realloc (mem_ctx,
+								     object->properties.lpProps,
+								     struct mapi_SPropValue,
+								     object->properties.cValues + 1);
+			object->properties.lpProps[object->properties.cValues - 1].ulPropTag = PidTagBody;
+			if (bin.cb > 0 && bin.lpb[bin.cb - 1] == 0)
+				object->properties.lpProps[object->properties.cValues - 1].value.lpszW = (const char *) talloc_steal (object, bin.lpb);
+			else
+				object->properties.lpProps[object->properties.cValues - 1].value.lpszW = talloc_strndup (object, (char *) bin.lpb, bin.cb);
+			object->properties.lpProps[object->properties.cValues].ulPropTag = 0;
+		}
+	}
+
+	if (!e_mapi_util_find_array_propval (&object->properties, PidNameContentClass)) {
+		uint32_t prop = PidNameContentClass;
+
+		prop = e_mapi_connection_resolve_named_prop (conn, mapi_object_get_id (eap->obj_folder), prop, cancellable, NULL);
+		if (prop != MAPI_E_RESERVED) {
+			struct SPropTagArray *tags;
+			struct SPropValue *lpProps = NULL;
+			uint32_t prop_count = 0;
+
+			tags = set_SPropTagArray (mem_ctx, 1, prop);
+
+			if (GetProps (obj_message, MAPI_PROPS_SKIP_NAMEDID_CHECK | MAPI_UNICODE, tags, &lpProps, &prop_count) == MAPI_E_SUCCESS && lpProps) {
+				if (lpProps[0].ulPropTag == prop) {
+					object->properties.cValues++;
+					object->properties.lpProps = talloc_realloc (mem_ctx,
+										     object->properties.lpProps,
+										     struct mapi_SPropValue,
+										     object->properties.cValues + 1);
+					object->properties.lpProps[object->properties.cValues - 1].ulPropTag = PidNameContentClass;
+					object->properties.lpProps[object->properties.cValues - 1].value.lpszW = talloc_strdup (object, lpProps[0].value.lpszW);
+					object->properties.lpProps[object->properties.cValues].ulPropTag = 0;
+				}
+			}
+
+			talloc_free (tags);
+			talloc_free (lpProps);
+		}
+	}
+
+	/* fetch attachments */
+	has_attachments = e_mapi_util_find_array_propval (&object->properties, PidTagHasAttachments);
+	if (has_attachments && *has_attachments) {
+		struct SPropTagArray *attach_columns;
+		struct FetchObjectAttachmentData foa;
+
+		ms = GetAttachmentTable (obj_message, &attach_table);
+		if (ms != MAPI_E_SUCCESS) {
+			make_mapi_error (perror, "GetAttachmentTable", ms);
+			goto cleanup;
+		}
+
+		attach_columns = set_SPropTagArray (mem_ctx, 1, PidTagAttachNumber);
+		ms = SetColumns (&attach_table, attach_columns);
+		if (ms != MAPI_E_SUCCESS) {
+			make_mapi_error (perror, "AttachTable::SetColumns", ms);
+			talloc_free (attach_columns);
+			goto cleanup;
+		}
+		talloc_free (attach_columns);
+
+		foa.obj_message = obj_message;
+		foa.eap = eap;
+		foa.object = object;
+
+		ms = foreach_tablerow (conn, mapi_object_get_id (eap->obj_folder), mem_ctx, &attach_table, fetch_object_attachment_cb, &foa, cancellable, perror);
+		if (ms != MAPI_E_SUCCESS) {
+			make_mapi_error (perror, "AttachTable::foreach_tablerow", ms);
+			goto cleanup;
+		}
+	}
+
+	/* get recipients */
+	ms = GetRecipientTable (obj_message, &recipient_rows, &recipient_proptags);
+	if (ms != MAPI_E_SUCCESS) {
+		make_mapi_error (perror, "GetRecipientTable", ms);
+		goto cleanup;
+	}
+
+	if (recipient_rows.cRows > 0) {
+		uint32_t uj32, uk32;
+		EMapiRecipient *first_recipient = NULL;
+
+		for (ui32 = 0; ui32 < recipient_rows.cRows; ui32++) {
+			struct SRow *row = &recipient_rows.aRow[ui32];
+			EMapiRecipient *recipient;
+
+			recipient = e_mapi_recipient_new (object);
+			recipient->properties.cValues = row->cValues;
+			recipient->properties.lpProps = talloc_zero_array (recipient, struct mapi_SPropValue, recipient->properties.cValues + 1);
+
+			for (uj32 = 0, uk32 = 0; uj32 < row->cValues; uj32++, uk32++) {
+				if (may_skip_property (row->lpProps[uj32].ulPropTag) ||
+				    !e_mapi_utils_copy_to_mapi_SPropValue (recipient, &recipient->properties.lpProps[uk32], &row->lpProps[uj32])) {
+					uk32--;
+					recipient->properties.cValues--;
+					recipient->properties.lpProps[recipient->properties.cValues].ulPropTag = 0;
+				}
+			}
+
+			recipient->properties.lpProps[recipient->properties.cValues].ulPropTag = 0;
+		}
+
+		object->recipients = first_recipient;
+	}
+
+ cleanup:
+	mapi_object_release (&attach_table);
+
+	if (ms == MAPI_E_SUCCESS) {
+		*out_object = object;
+	} else {
+		*out_object = NULL;
+		e_mapi_object_free (object);
+	}
+
+	return ms;
+}
+
+static enum MAPISTATUS
+e_mapi_connection_fetch_objects_internal (EMapiConnection *conn,
+					  TALLOC_CTX *mem_ctx,
+					  mapi_id_array_t *ids,
+					  struct EnsureAdditionalPropertiesData *eap,
+					  GCancellable *cancellable,
+					  GError **perror)
+{
+	enum MAPISTATUS ms;
+	guint32 idx;
+	mapi_container_list_t *element;
+
+	g_return_val_if_fail (conn != NULL, MAPI_E_INVALID_PARAMETER);
+	g_return_val_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER);
+	g_return_val_if_fail (ids != NULL, MAPI_E_INVALID_PARAMETER);
+	g_return_val_if_fail (eap != NULL, MAPI_E_INVALID_PARAMETER);
+	g_return_val_if_fail (eap->obj_folder != NULL, MAPI_E_INVALID_PARAMETER);
+	g_return_val_if_fail (eap->downloaded < ids->count, MAPI_E_INVALID_PARAMETER);
+
+	for (idx = 0, element = ids->lpContainerList; idx < ids->count && idx < eap->downloaded && element; idx++) {
+		element = element->next;
+	}
+
+	g_return_val_if_fail (idx < ids->count, MAPI_E_INVALID_PARAMETER);
+
+	ms = MAPI_E_SUCCESS;
+	while (element && ms == MAPI_E_SUCCESS) {
+		mapi_object_t obj_message;
+		EMapiObject *object = NULL;
+		GError *local_error = NULL;
+
+		if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
+			ms = MAPI_E_USER_CANCEL;
+			break;
+		}
+
+		mapi_object_init (&obj_message);
+
+		ms = OpenMessage (eap->obj_folder, mapi_object_get_id (eap->obj_folder), element->id, &obj_message, 0 /* read-only */);
+		if (ms != MAPI_E_SUCCESS) {
+			make_mapi_error (perror, "OpenMessage", ms);
+			mapi_object_release (&obj_message);
+			break;
+		}
+
+		/* silently skip broken objects */
+		ms = e_mapi_connection_fetch_object_internal (conn, mem_ctx, &obj_message, eap, &object, cancellable, &local_error);
+		if (ms == MAPI_E_SUCCESS) {
+			if (!eap->cb (conn, mem_ctx, object, eap->downloaded, ids->count, eap->cb_user_data, cancellable, perror)) {
+				ms = MAPI_E_USER_CANCEL;
+				make_mapi_error (perror, "Object processing", ms);
+			}
+		} else {
+			e_mapi_debug_print ("%s: Skipping object %016" G_GINT64_MODIFIER "X because its fetch failed: %s",
+				G_STRFUNC, element->id, local_error ? local_error->message : mapi_get_errstr (ms));
+			ms = MAPI_E_SUCCESS;
+		}
+
+		e_mapi_object_free (object);
+		mapi_object_release (&obj_message);
+
+		eap->downloaded++;
+
+		element = element->next;
+	}
+
+	return ms;
+}
+
 /* deals with named IDs transparently, thus it's OK to check with PidLid and PidName constants only */
 gboolean
 e_mapi_connection_transfer_objects (EMapiConnection *conn,
@@ -2506,8 +3007,20 @@ e_mapi_connection_transfer_objects (EMapiConnection *conn,
 	eap.cb = cb;
 	eap.cb_user_data = cb_user_data;
 	eap.obj_folder = obj_folder;
+	eap.downloaded = 0;
 
 	ms = e_mapi_fast_transfer_objects (conn, mem_ctx, obj_folder, &ids, ensure_additional_properties_cb, &eap, cancellable, perror);
+	if (ms == MAPI_E_CALL_FAILED) {
+		/* err, fallback to slow transfer, probably FXGetBuffer failed;
+		   see http://tracker.openchange.org/issues/378
+		*/
+
+		g_clear_error (perror);
+
+		e_mapi_debug_print ("%s: Failed to fast-transfer, fallback to slow fetch from %d of %d objects\n", G_STRFUNC, eap.downloaded, ids.count);
+
+		ms = e_mapi_connection_fetch_objects_internal (conn, mem_ctx, &ids, &eap, cancellable, perror);
+	}
 
 	mapi_id_array_release (&ids);
 
diff --git a/src/libexchangemapi/e-mapi-connection.h b/src/libexchangemapi/e-mapi-connection.h
index 46ce521..eb7cfdd 100644
--- a/src/libexchangemapi/e-mapi-connection.h
+++ b/src/libexchangemapi/e-mapi-connection.h
@@ -143,9 +143,10 @@ typedef struct {
 } ResolveNamedIDsData;
 
 typedef struct {
-	mapi_id_t mid; /* message ID, from PR_MID */
-	uint32_t msg_flags; /* MAPI MSGFLAG_* bit OR, from PR_MESSAGE_FLAGS */
-	time_t last_modified; /* PR_LAST_MODIFICATION_TIME as UTC */
+	mapi_id_t mid; /* message ID, from PidTagMid */
+	const gchar *msg_class; /* PidTagMessageClass */
+	uint32_t msg_flags; /* MAPI MSGFLAG_* bit OR, from PidTagMessageFlags */
+	time_t last_modified; /* PidTagLastModificationTime as UTC */
 } ListObjectsData;
 
 struct _EMapiObject;
@@ -225,9 +226,9 @@ typedef gboolean (*BuildRestrictionsCB)		(EMapiConnection *conn,
 typedef gboolean (*ListObjectsCB)		(EMapiConnection *conn,
 						 mapi_id_t fid,
 						 TALLOC_CTX *mem_ctx,
-						 const ListObjectsData *item_data,
-						 guint32 item_index,
-						 guint32 items_total,
+						 const ListObjectsData *object_data,
+						 guint32 obj_index,
+						 guint32 obj_total,
 						 gpointer user_data,
 						 GCancellable *cancellable,
 						 GError **perror);
diff --git a/src/libexchangemapi/e-mapi-mail-utils.c b/src/libexchangemapi/e-mapi-mail-utils.c
index 2f12040..34181c1 100644
--- a/src/libexchangemapi/e-mapi-mail-utils.c
+++ b/src/libexchangemapi/e-mapi-mail-utils.c
@@ -992,6 +992,7 @@ e_mapi_mail_utils_decode_email_address (EMapiConnection *conn,
 {
 	gint ii;
 	const gchar *cname = NULL, *cemail = NULL;
+	const gchar *addr_type, *email_addr;
 
 	g_return_if_fail (conn != NULL);
 	g_return_if_fail (properties != NULL);
@@ -1007,18 +1008,18 @@ e_mapi_mail_utils_decode_email_address (EMapiConnection *conn,
 		cname = e_mapi_util_find_array_propval (properties, name_proptags[ii]);
 	}
 
+	addr_type = e_mapi_util_find_array_propval (properties, email_type_proptag);
+	email_addr = e_mapi_util_find_array_propval (properties, email_proptag);
+
+	if (addr_type && g_ascii_strcasecmp (addr_type, "SMTP") == 0)
+		cemail = email_addr;
+
 	for (ii = 0; ii < smtp_proptags_len && !cemail; ii++) {
 		cemail = e_mapi_util_find_array_propval (properties, smtp_proptags[ii]);
 	}
 
-	if (!cemail) {
-		const gchar *addr_type = e_mapi_util_find_array_propval (properties, email_type_proptag);
-		const gchar *email_addr = e_mapi_util_find_array_propval (properties, email_proptag);
-
-		if (addr_type && g_ascii_strcasecmp (addr_type, "EX") == 0 && email_addr)
-			*email = e_mapi_connection_ex_to_smtp (conn, email_addr, name, NULL, NULL);
-		else if (addr_type && g_ascii_strcasecmp (addr_type, "SMTP") == 0)
-			cemail = email_addr;
+	if (!cemail && addr_type && g_ascii_strcasecmp (addr_type, "EX") == 0 && email_addr) {
+		*email = e_mapi_connection_ex_to_smtp (conn, email_addr, name, NULL, NULL);
 	}
 
 	if (!*email) {
diff --git a/src/libexchangemapi/e-mapi-utils.c b/src/libexchangemapi/e-mapi-utils.c
index 95318dc..58ac349 100644
--- a/src/libexchangemapi/e-mapi-utils.c
+++ b/src/libexchangemapi/e-mapi-utils.c
@@ -1318,3 +1318,189 @@ e_mapi_utils_ensure_utf8_string (uint32_t proptag,
 
 	return TRUE;
 }
+
+/* takes pointer to a time_t and populates restrictions
+   with a restriction on PidTagLastModificationTime
+*/
+gboolean
+e_mapi_utils_build_last_modify_restriction (EMapiConnection *conn,
+					    mapi_id_t fid,
+					    TALLOC_CTX *mem_ctx,
+					    struct mapi_SRestriction **restrictions,
+					    gpointer user_data,
+					    GCancellable *cancellable,
+					    GError **perror)
+{
+	const time_t *latest_last_modify = user_data;
+	struct mapi_SRestriction *restriction = NULL;
+
+	g_return_val_if_fail (restrictions != NULL, FALSE);
+
+	if (latest_last_modify && *latest_last_modify > 0) {
+		struct SPropValue sprop;
+		struct timeval t;
+
+		restriction = talloc_zero (mem_ctx, struct mapi_SRestriction);
+		g_return_val_if_fail (restriction != NULL, FALSE);
+
+		restriction->rt = RES_PROPERTY;
+		restriction->res.resProperty.relop = RELOP_GT;
+		restriction->res.resProperty.ulPropTag = PidTagLastModificationTime;
+
+		t.tv_sec = *latest_last_modify;
+		t.tv_usec = 0;
+
+		set_SPropValue_proptag_date_timeval (&sprop, PidTagLastModificationTime, &t);
+		cast_mapi_SPropValue (mem_ctx, &(restriction->res.resProperty.lpProp), &sprop);
+	}
+
+	*restrictions = restriction;
+
+	return TRUE;
+}
+
+gboolean
+e_mapi_utils_get_folder_basic_properties_cb (EMapiConnection *conn,
+					     mapi_id_t fid,
+					     TALLOC_CTX *mem_ctx,
+					     /* const */ struct mapi_SPropValue_array *properties,
+					     gpointer user_data,
+					     GCancellable *cancellable,
+					     GError **perror)
+{
+	struct FolderBasicPropertiesData *fbp = user_data;
+	const mapi_id_t *pfid;
+	const struct FILETIME *plast_modified;
+	const uint32_t *pcontent_count;
+
+	g_return_val_if_fail (properties != NULL, FALSE);
+	g_return_val_if_fail (user_data != NULL, FALSE);
+
+	pfid = e_mapi_util_find_array_propval (properties, PidTagFolderId);
+	plast_modified = e_mapi_util_find_array_propval (properties, PidTagLastModificationTime);
+	pcontent_count = e_mapi_util_find_array_propval (properties, PidTagContentCount);
+
+	if (pfid)
+		fbp->fid = *pfid;
+	else
+		fbp->fid = 0;
+
+	if (pcontent_count)
+		fbp->obj_total = *pcontent_count;
+	else
+		fbp->obj_total = 0;
+
+	if (plast_modified)
+		fbp->last_modified = e_mapi_util_filetime_to_time_t (plast_modified);
+	else
+		fbp->last_modified = 0;
+
+	return TRUE;
+}
+
+gboolean
+e_mapi_utils_copy_to_mapi_SPropValue (TALLOC_CTX *mem_ctx,
+				      struct mapi_SPropValue *mapi_sprop, 
+				      struct SPropValue *sprop)
+{
+	mapi_sprop->ulPropTag = sprop->ulPropTag;
+
+	switch (sprop->ulPropTag & 0xFFFF) {
+	case PT_BOOLEAN:
+		mapi_sprop->value.b = sprop->value.b;
+		return TRUE;
+	case PT_I2:
+		mapi_sprop->value.i = sprop->value.i;
+		return TRUE;
+	case PT_LONG:
+		mapi_sprop->value.l = sprop->value.l;
+		return TRUE;
+	case PT_DOUBLE:
+		memcpy (&mapi_sprop->value.dbl, (uint8_t *) &sprop->value.dbl, 8);
+		return TRUE;
+	case PT_I8:
+		mapi_sprop->value.d = sprop->value.d;
+		return TRUE;
+	case PT_STRING8:
+		mapi_sprop->value.lpszA = talloc_strdup (mem_ctx, sprop->value.lpszA);
+		return TRUE;
+	case PT_UNICODE:
+		mapi_sprop->value.lpszW = talloc_strdup (mem_ctx, sprop->value.lpszW);
+		return TRUE;
+	case PT_SYSTIME:
+		mapi_sprop->value.ft.dwLowDateTime = sprop->value.ft.dwLowDateTime;
+		mapi_sprop->value.ft.dwHighDateTime = sprop->value.ft.dwHighDateTime;
+		return TRUE;
+	case PT_BINARY:
+		mapi_sprop->value.bin.cb = sprop->value.bin.cb;
+		mapi_sprop->value.bin.lpb = talloc_memdup (mem_ctx, sprop->value.bin.lpb, sprop->value.bin.cb);
+		return TRUE;
+        case PT_ERROR:
+                mapi_sprop->value.err = sprop->value.err;
+                return TRUE;
+	case PT_CLSID:
+	{
+		DATA_BLOB	b;
+
+		b.data = sprop->value.lpguid->ab;
+		b.length = 16;
+
+		GUID_from_ndr_blob (&b, &mapi_sprop->value.lpguid);
+
+		return TRUE;
+	}
+	case PT_SVREID:
+		mapi_sprop->value.bin.cb = sprop->value.bin.cb;
+		mapi_sprop->value.bin.lpb = talloc_memdup (mem_ctx, sprop->value.bin.lpb, sprop->value.bin.cb);
+		return TRUE;
+	case PT_MV_STRING8:
+	{
+		uint32_t i;
+
+		mapi_sprop->value.MVszA.cValues = sprop->value.MVszA.cValues;
+		mapi_sprop->value.MVszA.strings = talloc_array (mem_ctx, struct mapi_LPSTR, mapi_sprop->value.MVszA.cValues);
+		for (i = 0; i < mapi_sprop->value.MVszA.cValues; i++) {
+			mapi_sprop->value.MVszA.strings[i].lppszA = talloc_strdup (mem_ctx, sprop->value.MVszA.lppszA[i]);
+		}
+		return TRUE;
+	}
+	case PT_MV_UNICODE:
+	{
+		uint32_t i;
+
+		mapi_sprop->value.MVszW.cValues = sprop->value.MVszW.cValues;
+		mapi_sprop->value.MVszW.strings = talloc_array (mem_ctx, struct mapi_LPWSTR, mapi_sprop->value.MVszW.cValues);
+		for (i = 0; i < mapi_sprop->value.MVszW.cValues; i++) {
+			mapi_sprop->value.MVszW.strings[i].lppszW = talloc_strdup (mem_ctx, sprop->value.MVszW.lppszW[i]);
+		}
+		return TRUE;
+	}
+	case PT_MV_BINARY:
+	{
+		uint32_t i;
+
+		mapi_sprop->value.MVbin.cValues = sprop->value.MVbin.cValues;
+		mapi_sprop->value.MVbin.bin = talloc_array (mem_ctx, struct SBinary_short, mapi_sprop->value.MVbin.cValues);
+		for (i = 0; i < mapi_sprop->value.MVbin.cValues; i++) {
+			mapi_sprop->value.MVbin.bin[i].cb = sprop->value.MVbin.lpbin[i].cb;
+			mapi_sprop->value.MVbin.bin[i].lpb = talloc_memdup (mem_ctx, sprop->value.MVbin.lpbin[i].lpb, sprop->value.MVbin.lpbin[i].cb);
+		}
+		return TRUE;
+	}
+	case PT_MV_LONG:
+	{
+		uint32_t i;
+
+		mapi_sprop->value.MVl.cValues = sprop->value.MVl.cValues;
+		mapi_sprop->value.MVl.lpl = talloc_array (mem_ctx, uint32_t, mapi_sprop->value.MVl.cValues);
+		for (i = 0; i < mapi_sprop->value.MVl.cValues; i++) {
+			mapi_sprop->value.MVl.lpl[i] = sprop->value.MVl.lpl[i];
+		}
+		return TRUE;
+	}
+        default:
+		break;
+	}
+
+	return FALSE;
+}
diff --git a/src/libexchangemapi/e-mapi-utils.h b/src/libexchangemapi/e-mapi-utils.h
index 83fd432..79fd661 100644
--- a/src/libexchangemapi/e-mapi-utils.h
+++ b/src/libexchangemapi/e-mapi-utils.h
@@ -108,7 +108,32 @@ void e_mapi_util_time_t_to_filetime (const time_t tt, struct FILETIME *filetime)
 
 void		e_mapi_utils_global_lock			(void);
 void		e_mapi_utils_global_unlock			(void);
-gboolean	e_mapi_utils_create_mapi_context		(struct mapi_context **mapi_ctx, GError **perror);
+gboolean	e_mapi_utils_create_mapi_context		(struct mapi_context **mapi_ctx,
+								 GError **perror);
 void		e_mapi_utils_destroy_mapi_context		(struct mapi_context *mapi_ctx);
 
+gboolean	e_mapi_utils_build_last_modify_restriction	(EMapiConnection *conn,
+								 mapi_id_t fid,
+								 TALLOC_CTX *mem_ctx,
+								 struct mapi_SRestriction **restrictions,
+								 gpointer user_data,
+								 GCancellable *cancellable,
+								 GError **perror);
+struct FolderBasicPropertiesData
+{
+	mapi_id_t fid;
+	time_t last_modified;
+	guint32 obj_total;
+};
+
+gboolean	e_mapi_utils_get_folder_basic_properties_cb	(EMapiConnection *conn,
+								 mapi_id_t fid,
+								 TALLOC_CTX *mem_ctx,
+								 /* const */ struct mapi_SPropValue_array *properties,
+								 gpointer user_data, /* struct FolderBasicPropertiesData * */
+								 GCancellable *cancellable,
+								 GError **perror);
+gboolean	e_mapi_utils_copy_to_mapi_SPropValue		(TALLOC_CTX *mem_ctx,
+								 struct mapi_SPropValue *mapi_sprop, 
+								 struct SPropValue *sprop);
 #endif



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