[evolution-mapi] Implement fast-transfer for address book backend



commit 9843a63eb02f4da54c21e20f4d1db4d3e220d8df
Author: Milan Crha <mcrha redhat com>
Date:   Tue Nov 22 15:40:45 2011 +0100

    Implement fast-transfer for address book backend

 src/addressbook/e-book-backend-mapi-contacts.c |  438 ++++++++++++++++--------
 src/addressbook/e-book-backend-mapi-gal.c      |  102 ++++---
 src/addressbook/e-book-backend-mapi.c          |  354 +++++++++-----------
 src/addressbook/e-book-backend-mapi.h          |   32 ++-
 src/libexchangemapi/e-mapi-utils.c             |   20 --
 src/libexchangemapi/e-mapi-utils.h             |    5 +-
 6 files changed, 524 insertions(+), 427 deletions(-)
---
diff --git a/src/addressbook/e-book-backend-mapi-contacts.c b/src/addressbook/e-book-backend-mapi-contacts.c
index ddac53c..4f0df9e 100644
--- a/src/addressbook/e-book-backend-mapi-contacts.c
+++ b/src/addressbook/e-book-backend-mapi-contacts.c
@@ -555,137 +555,152 @@ struct FetchContactItemData
 };
 
 static gboolean
-fetch_contact_item_cb (FetchItemsCallbackData *item_data,
-		       gpointer data,
-		       GCancellable *cancellable,
-		       GError **perror)
+transfer_contact_cb (EMapiConnection *conn,
+		     TALLOC_CTX *mem_ctx,
+		     /* const */ EMapiObject *object,
+		     guint32 obj_index,
+		     guint32 obj_total,
+		     gpointer user_data,
+		     GCancellable *cancellable,
+		     GError **perror)
 {
-	struct FetchContactItemData *fcid = data;
+	struct FetchContactItemData *fcid = user_data;
 
-	g_return_val_if_fail (data != NULL, FALSE);
+	g_return_val_if_fail (fcid != NULL, FALSE);
 	g_return_val_if_fail (fcid->ebma != NULL, FALSE);
+	g_return_val_if_fail (object != NULL, FALSE);
 
-	fcid->contact = mapi_book_utils_contact_from_props (item_data->conn, item_data->fid, e_book_backend_mapi_get_book_uri (fcid->ebma), item_data->properties, NULL);
+	fcid->contact = mapi_book_utils_contact_from_props (conn, E_BOOK_BACKEND_MAPI_CONTACTS (fcid->ebma)->priv->fid, e_book_backend_mapi_get_book_uri (fcid->ebma), &object->properties, NULL);
 
 	if (fcid->contact) {
-		gchar *suid = e_mapi_util_mapi_ids_to_uid (item_data->fid, item_data->mid);
+		const mapi_id_t *pmid;
+
+		pmid = e_mapi_util_find_array_propval (&object->properties, PidTagMid);
+		if (pmid) {
+			gchar *suid = e_mapi_util_mapi_id_to_string (*pmid);
 
-		/* UID of the contact is nothing but the concatenated string of hex id of folder and the message.*/
-		e_contact_set (fcid->contact, E_CONTACT_UID, suid);
+			/* UID of the contact is nothing but the concatenated string of hex id of folder and the message.*/
+			e_contact_set (fcid->contact, E_CONTACT_UID, suid);
+
+			if (!e_book_backend_mapi_notify_contact_update (fcid->ebma, NULL, fcid->contact, obj_index, obj_total, NULL)) {
+				g_free (suid);
+				return FALSE;
+			}
 
-		if (!e_book_backend_mapi_notify_contact_update (fcid->ebma, NULL, fcid->contact, NULL, item_data->index, item_data->total, NULL)) {
 			g_free (suid);
-			return FALSE;
+		} else {
+			g_debug ("%s: No PidTagMid found", G_STRFUNC);
 		}
-
-		g_free (suid);
 	}
 
 	return TRUE;
 }
 
-struct CreateContactListData
-{
-	EBookBackendMAPI *ebma;
-	GSList **vCards;
-};
-
 static gboolean
-create_contact_list_cb (FetchItemsCallbackData *item_data,
-			gpointer data,
+gather_contact_mids_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)
 {
-	struct CreateContactListData *ccld = data;
-	EContact *contact;
-
-	g_return_val_if_fail (data != NULL, FALSE);
-	g_return_val_if_fail (ccld->ebma != NULL, FALSE);
-	g_return_val_if_fail (ccld->vCards != NULL, FALSE);
-
-	contact = mapi_book_utils_contact_from_props (item_data->conn, item_data->fid, e_book_backend_mapi_get_book_uri (ccld->ebma), item_data->properties, NULL);
-	if (contact) {
-		gchar *suid = e_mapi_util_mapi_ids_to_uid (item_data->fid, item_data->mid);
-
-		e_contact_set (contact, E_CONTACT_UID, suid);
+	GSList *pmids = user_data;
+	mapi_id_t *pmid;
 
-		*ccld->vCards = g_slist_prepend (*ccld->vCards, e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30));
+	g_return_val_if_fail (object_data != NULL, FALSE);
+	g_return_val_if_fail (pmids != NULL, FALSE);
 
-		e_book_backend_mapi_notify_contact_update (ccld->ebma, NULL, contact, NULL, -1, -1, NULL);
+	pmid = g_new0 (mapi_id_t, 1);
+	*pmid = object_data->mid;
 
-		g_object_unref (contact);
-		g_free (suid);
-	}
+	pmids = g_slist_prepend (pmids, pmid);
 
 	return TRUE;
 }
 
-struct FetchContactsData
+struct TransferContactsData
 {
 	EBookBackendMAPI *ebma;
 	EDataBookView *book_view;
 	gpointer notify_contact_data;
+	GSList **cards;
 };
 
 static gboolean
-fetch_contacts_cb (FetchItemsCallbackData *item_data,
-		   gpointer data,
-		   GCancellable *cancellable,
-		   GError **perror)
+transfer_contacts_cb (EMapiConnection *conn,
+		      TALLOC_CTX *mem_ctx,
+		      /* const */ EMapiObject *object,
+		      guint32 obj_index,
+		      guint32 obj_total,
+		      gpointer user_data,
+		      GCancellable *cancellable,
+		      GError **perror)
 {
-	struct FetchContactsData *fcd = data;
+	struct TransferContactsData *tcd = user_data;
 	EContact *contact;
 
-	g_return_val_if_fail (data != NULL, FALSE);
-	g_return_val_if_fail (fcd->ebma != NULL, FALSE);
-	g_return_val_if_fail (fcd->ebma->priv != NULL, FALSE);
-
-	contact = mapi_book_utils_contact_from_props (item_data->conn, item_data->fid, e_book_backend_mapi_get_book_uri (fcd->ebma), item_data->properties, NULL);
+	g_return_val_if_fail (tcd != NULL, FALSE);
+	g_return_val_if_fail (object != NULL, FALSE);
+	g_return_val_if_fail (tcd->ebma != NULL, FALSE);
 
+	contact = mapi_book_utils_contact_from_props (conn, E_BOOK_BACKEND_MAPI_CONTACTS (tcd->ebma)->priv->fid, e_book_backend_mapi_get_book_uri (tcd->ebma), &object->properties, NULL);
 	if (contact) {
-		gchar *suid;
-		struct timeval *last_modification = NULL, tv = { 0 };
+		const mapi_id_t *pmid;
+
+		pmid = e_mapi_util_find_array_propval (&object->properties, PidTagMid);
+		if (pmid) {
+			gchar *suid = e_mapi_util_mapi_id_to_string (*pmid);
+
+			e_contact_set (contact, E_CONTACT_UID, suid);
+			g_free (suid);
 
-		suid = e_mapi_util_mapi_ids_to_uid (item_data->fid, item_data->mid);
-		e_contact_set (contact, E_CONTACT_UID, suid);
-		g_free (suid);
+			if (tcd->cards)
+				*tcd->cards = g_slist_prepend (*tcd->cards, e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30));
 
-		if (get_mapi_SPropValue_array_date_timeval (&tv, item_data->properties, PR_LAST_MODIFICATION_TIME) == MAPI_E_SUCCESS)
-			last_modification = &tv;
+			if (!e_book_backend_mapi_notify_contact_update (tcd->ebma, tcd->book_view, contact, obj_index, obj_total, tcd->notify_contact_data)) {
+				g_object_unref (contact);
+				return FALSE;
+			}
 
-		if (!e_book_backend_mapi_notify_contact_update (fcd->ebma, fcd->book_view, contact, last_modification, item_data->index, item_data->total, fcd->notify_contact_data)) {
 			g_object_unref (contact);
-			return FALSE;
+		} else {
+			g_debug ("%s: No PidTagMid found", G_STRFUNC);
 		}
-
-		g_object_unref (contact);
+	} else {
+		g_debug ("%s: [%d/%d] Failed to transform to contact", G_STRFUNC, obj_index, obj_total);
 	}
 
 	return TRUE;
 }
 
-struct FetchContactsUidsData
-{
-	GCancellable *cancellable;
-	GHashTable *uids;
-};
-
 static gboolean
-fetch_contacts_uids_cb (FetchItemsCallbackData *item_data,
-			gpointer data,
-			GCancellable *cancellable,
-			GError **perror)
+gather_known_uids_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)
 {
-	struct FetchContactsUidsData *fcud = data;
+	struct ListKnownUidsData *lku = user_data;
 	gchar *suid;
 
-	g_return_val_if_fail (data != NULL, FALSE);
+	g_return_val_if_fail (lku != NULL, FALSE);
+	g_return_val_if_fail (lku->uid_to_rev != NULL, FALSE);
 
-	suid = e_mapi_util_mapi_ids_to_uid (item_data->fid, item_data->mid);
-	if (suid)
-		g_hash_table_insert (fcud->uids, suid, GINT_TO_POINTER (1));
+	suid = e_mapi_util_mapi_id_to_string (object_data->mid);
+	if (suid) {
+		g_hash_table_insert (lku->uid_to_rev, suid, mapi_book_utils_timet_to_string (object_data->last_modified));
+		if (lku->latest_last_modify < object_data->last_modified)
+			lku->latest_last_modify = object_data->last_modified;
+	}
 
-	return !g_cancellable_is_cancelled (fcud->cancellable);
+	return TRUE;
 }
 
 static void
@@ -825,7 +840,7 @@ ebbm_contacts_create_contacts (EBookBackendMAPI *ebma, GCancellable *cancellable
 		return;
 	}
 
-	id = e_mapi_util_mapi_ids_to_uid (priv->fid, mid);
+	id = e_mapi_util_mapi_id_to_string (mid);
 
 	/* UID of the contact is nothing but the concatenated string of hex id of folder and the message.*/
 	e_contact_set (contact, E_CONTACT_UID, id);
@@ -868,16 +883,18 @@ ebbm_contacts_remove_contacts (EBookBackendMAPI *ebma, GCancellable *cancellable
 
 	to_remove = NULL;
 	for (l = id_list; l; l = l->next) {
-		mapi_id_t mid, fid;
+		mapi_id_t mid;
 		const gchar *uid = l->data;
 		struct id_list *idl = g_new0 (struct id_list, 1);
 
-		e_mapi_util_mapi_ids_from_uid (uid, &fid, &mid);
-
-		idl->id = mid;
-		to_remove = g_slist_prepend (to_remove, idl);
+		if (e_mapi_util_mapi_id_from_string (uid, &mid)) {
+			idl->id = mid;
+			to_remove = g_slist_prepend (to_remove, idl);
 
-		*removed_ids = g_slist_prepend (*removed_ids, g_strdup (uid));
+			*removed_ids = g_slist_prepend (*removed_ids, g_strdup (uid));
+		} else {
+			g_debug ("%s: Failed to decode MID from '%s'", G_STRFUNC, uid);
+		}
 	}
 
 	e_mapi_connection_remove_items (conn, olFolderContacts, priv->fid, priv->is_public_folder ? MAPI_OPTIONS_USE_PFSTORE : 0, to_remove, cancellable, &mapi_error);
@@ -907,7 +924,7 @@ ebbm_contacts_modify_contacts (EBookBackendMAPI *ebma, GCancellable *cancellable
 	MapiCreateitemData mcd;
 	EContact *contact;
 	GError *mapi_error = NULL;
-	mapi_id_t fid, mid;
+	mapi_id_t mid;
 
 	e_return_data_book_error_if_fail (ebma != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
 	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_MAPI_CONTACTS (ebma), E_DATA_BOOK_STATUS_INVALID_ARG);
@@ -944,18 +961,20 @@ ebbm_contacts_modify_contacts (EBookBackendMAPI *ebma, GCancellable *cancellable
 	e_book_backend_mapi_get_db (ebma, &mcd.db);
 	mcd.contact = contact;
 
-	e_mapi_util_mapi_ids_from_uid (e_contact_get_const (contact, E_CONTACT_UID), &fid, &mid);
-
-	if (!e_mapi_connection_modify_item (conn, olFolderContacts, priv->fid, mid,
-		mapi_book_write_props, &mcd, NULL, NULL, NULL, priv->is_public_folder ? MAPI_OPTIONS_USE_PFSTORE : 0, cancellable, &mapi_error)) {
+	if (e_mapi_util_mapi_id_from_string (e_contact_get_const (contact, E_CONTACT_UID), &mid)) {
+		if (!e_mapi_connection_modify_item (conn, olFolderContacts, priv->fid, mid,
+			mapi_book_write_props, &mcd, NULL, NULL, NULL, priv->is_public_folder ? MAPI_OPTIONS_USE_PFSTORE : 0, cancellable, &mapi_error)) {
 
-		mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_OTHER_ERROR, _("Failed to modify item on a server"));
-		if (mapi_error)
-			g_error_free (mapi_error);
+			mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_OTHER_ERROR, _("Failed to modify item on a server"));
+			if (mapi_error)
+				g_error_free (mapi_error);
 
-		g_object_unref (contact);
+			g_object_unref (contact);
+		} else {
+			*modified_contacts = g_slist_append (NULL, contact);
+		}
 	} else {
-		*modified_contacts = g_slist_append (NULL, contact);
+		g_debug ("%s: Failed to decode MID from '%s'", G_STRFUNC, (const gchar *) e_contact_get_const (contact, E_CONTACT_UID));
 	}
 
 	e_book_backend_mapi_unlock_connection (ebma);
@@ -967,9 +986,10 @@ ebbm_contacts_get_contact (EBookBackendMAPI *ebma, GCancellable *cancellable, co
 	EBookBackendMAPIContacts *ebmac;
 	EBookBackendMAPIContactsPrivate *priv;
 	EMapiConnection *conn;
-	mapi_id_t fid, mid;
-	guint32 options;
+	mapi_id_t mid;
+	mapi_object_t obj_folder;
 	struct FetchContactItemData fcid = { 0 };
+	gboolean status;
 	GError *mapi_error = NULL;
 
 	e_return_data_book_error_if_fail (ebma != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
@@ -1004,18 +1024,26 @@ ebbm_contacts_get_contact (EBookBackendMAPI *ebma, GCancellable *cancellable, co
 		return;
 	}
 
-	options = MAPI_OPTIONS_FETCH_ALL;
 	if (priv->is_public_folder)
-		options |= MAPI_OPTIONS_USE_PFSTORE;
+		status = e_mapi_connection_open_public_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
+	else
+		status = e_mapi_connection_open_personal_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
+
+	if (status) {
+		status = e_mapi_util_mapi_id_from_string (id, &mid);
+		if (!status) {
+			g_debug ("%s: Failed to decode MID from '%s'", G_STRFUNC, id);
+		}
+	}
 
-	fcid.ebma = ebma;
-	fcid.contact = NULL;
-	e_mapi_util_mapi_ids_from_uid (id, &fid, &mid);
+	if (status) {
+		fcid.ebma = ebma;
+		fcid.contact = NULL;
 
-	e_mapi_connection_fetch_item (conn, priv->fid, mid,
-		priv->is_public_folder ? NULL : mapi_book_utils_get_prop_list, GET_ALL_KNOWN_IDS,
-		fetch_contact_item_cb, &fcid,
-		options, cancellable, &mapi_error);
+		e_mapi_connection_transfer_object (conn, &obj_folder, mid, transfer_contact_cb, &fcid, cancellable, &mapi_error);
+	}
+
+	e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
 
 	if (fcid.contact) {
 		*vcard =  e_vcard_to_string (E_VCARD (fcid.contact), EVC_FORMAT_VCARD_30);
@@ -1040,10 +1068,11 @@ ebbm_contacts_get_contact_list (EBookBackendMAPI *ebma, GCancellable *cancellabl
 	EBookBackendMAPIContacts *ebmac;
 	EBookBackendMAPIContactsPrivate *priv;
 	EMapiConnection *conn;
-	guint32 options;
-	struct CreateContactListData ccld = { 0 };
 	GError *mapi_error = NULL;
-	gboolean get_all;
+	gboolean get_all, status;
+	mapi_object_t obj_folder;
+	GSList *mids = NULL;
+	struct TransferContactsData tcd = { 0 };
 
 	e_return_data_book_error_if_fail (ebma != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
 	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_MAPI_CONTACTS (ebma), E_DATA_BOOK_STATUS_INVALID_ARG);
@@ -1068,10 +1097,6 @@ ebbm_contacts_get_contact_list (EBookBackendMAPI *ebma, GCancellable *cancellabl
 	if (*vCards)
 		return;
 
-	options = MAPI_OPTIONS_FETCH_ALL;
-	if (priv->is_public_folder)
-		options |= MAPI_OPTIONS_USE_PFSTORE;
-
 	e_book_backend_mapi_lock_connection (ebma);
 
 	conn = e_book_backend_mapi_get_connection (ebma);
@@ -1082,8 +1107,8 @@ ebbm_contacts_get_contact_list (EBookBackendMAPI *ebma, GCancellable *cancellabl
 		return;
 	}
 
-	ccld.ebma = ebma;
-	ccld.vCards = vCards;
+	tcd.ebma = ebma;
+	tcd.cards = vCards;
 
 	get_all = g_ascii_strcasecmp (query, "(contains \"x-evolution-any-field\" \"\")") == 0;
 	if (!get_all && !build_restriction_emails_contains (NULL, 0, NULL, NULL, (gpointer) query, NULL, NULL)) {
@@ -1093,9 +1118,26 @@ ebbm_contacts_get_contact_list (EBookBackendMAPI *ebma, GCancellable *cancellabl
 		return;
 	}
 
-	if (!e_mapi_connection_fetch_items (conn, priv->fid, get_all ? NULL : build_restriction_emails_contains, (gpointer) query, NULL,
-		priv->is_public_folder ? NULL : mapi_book_utils_get_prop_list, GET_ALL_KNOWN_IDS,
-		create_contact_list_cb, &ccld, options, cancellable, &mapi_error)) {
+	if (priv->is_public_folder)
+		status = e_mapi_connection_open_public_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
+	else
+		status = e_mapi_connection_open_personal_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
+
+	if (status) {
+		status = e_mapi_connection_list_objects (conn, &obj_folder,
+							 get_all ? NULL : build_restriction_emails_contains, (gpointer) query,
+							 gather_contact_mids_cb, &mids,
+							 cancellable, &mapi_error);
+
+		if (mids)
+			status = e_mapi_connection_transfer_objects (conn, &obj_folder, mids, transfer_contacts_cb, &tcd, cancellable, &mapi_error);
+
+		e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
+
+		g_slist_free_full (mids, g_free);
+	}
+
+	if (!status) {
 		mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_OTHER_ERROR, _("Failed to fetch items from a server"));
 		if (mapi_error)
 			g_error_free (mapi_error);
@@ -1123,23 +1165,26 @@ ebbm_contacts_get_status_message (EBookBackendMAPI *ebma, gint index, gint total
 }
 
 static void
-ebbm_contacts_fetch_contacts (EBookBackendMAPI *ebma, BuildRestrictionsCB build_rs_cb, gpointer build_rs_cb_data, EDataBookView *book_view, gpointer notify_contact_data, GError **error)
+ebbm_contacts_get_contacts_count (EBookBackendMAPI *ebma,
+				  guint32 *obj_total,
+				  GCancellable *cancellable,
+				  GError **error)
 {
 	EBookBackendMAPIContacts *ebmac;
 	EBookBackendMAPIContactsPrivate *priv;
 	EMapiConnection *conn;
-	guint32 options;
-	struct FetchContactsData fcd = { 0 };
+	gboolean status;
+	mapi_object_t obj_folder;
 	GError *mapi_error = NULL;
 
 	e_return_data_book_error_if_fail (ebma != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
-	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_MAPI_CONTACTS (ebma), E_DATA_BOOK_STATUS_INVALID_ARG);
+	e_return_data_book_error_if_fail (obj_total != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
 
 	ebmac = E_BOOK_BACKEND_MAPI_CONTACTS (ebma);
 	e_return_data_book_error_if_fail (ebmac != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
+	e_return_data_book_error_if_fail (ebmac->priv != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
 
 	priv = ebmac->priv;
-	e_return_data_book_error_if_fail (priv != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
 
 	e_book_backend_mapi_lock_connection (ebma);
 
@@ -1147,44 +1192,53 @@ ebbm_contacts_fetch_contacts (EBookBackendMAPI *ebma, BuildRestrictionsCB build_
 	if (!conn) {
 		e_book_backend_mapi_unlock_connection (ebma);
 		g_propagate_error (error, EDB_ERROR (REPOSITORY_OFFLINE));
-
 		return;
 	}
 
-	fcd.ebma = ebma;
-	fcd.book_view = book_view;
-	fcd.notify_contact_data = notify_contact_data;
-
-	options = MAPI_OPTIONS_FETCH_ALL;
 	if (priv->is_public_folder)
-		options |= MAPI_OPTIONS_USE_PFSTORE;
-
-	if (!e_mapi_connection_fetch_items (conn, priv->fid, build_rs_cb, build_rs_cb_data, NULL,
-		mapi_book_utils_get_prop_list, GET_ALL_KNOWN_IDS,
-		fetch_contacts_cb, &fcd, options, NULL, &mapi_error)) {
-		mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_OTHER_ERROR, _("Failed to fetch items from a server"));
+		status = e_mapi_connection_open_public_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
+	else
+		status = e_mapi_connection_open_personal_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
+
+	if (status) {
+		struct FolderBasicPropertiesData fbp = { 0 };
+
+		status = e_mapi_connection_get_folder_properties (conn, &obj_folder, NULL, NULL,
+			e_mapi_utils_get_folder_basic_properties_cb, &fbp,
+			cancellable, &mapi_error);
+		if (status)
+			*obj_total = fbp.obj_total;
+		
+		e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
+	}
 
-		if (mapi_error)
-			g_error_free (mapi_error);
+	if (mapi_error) {
+		mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_OTHER_ERROR, _("Failed to count server contacts"));
+		g_error_free (mapi_error);
 	}
 
 	e_book_backend_mapi_unlock_connection (ebma);
 }
 
 static void
-ebbm_contacts_fetch_known_uids (EBookBackendMAPI *ebma, GCancellable *cancellable, GHashTable *uids, GError **error)
+ebbm_contacts_list_known_uids (EBookBackendMAPI *ebma,
+			       BuildRestrictionsCB build_rs_cb,
+			       gpointer build_rs_cb_data,
+			       struct ListKnownUidsData *lku,
+			       GCancellable *cancellable,
+			       GError **error)
 {
 	EBookBackendMAPIContacts *ebmac;
 	EBookBackendMAPIContactsPrivate *priv;
 	EMapiConnection *conn;
+	gboolean status;
+	mapi_object_t obj_folder;
 	GError *mapi_error = NULL;
-	struct FetchContactsUidsData fcud = { 0 };
-	guint32 options;
 
 	e_return_data_book_error_if_fail (ebma != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
 	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_MAPI_CONTACTS (ebma), E_DATA_BOOK_STATUS_INVALID_ARG);
-	e_return_data_book_error_if_fail (cancellable != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
-	e_return_data_book_error_if_fail (uids != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
+	e_return_data_book_error_if_fail (lku != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
+	e_return_data_book_error_if_fail (lku->uid_to_rev != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
 
 	ebmac = E_BOOK_BACKEND_MAPI_CONTACTS (ebma);
 	e_return_data_book_error_if_fail (ebmac != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
@@ -1201,19 +1255,21 @@ ebbm_contacts_fetch_known_uids (EBookBackendMAPI *ebma, GCancellable *cancellabl
 		return;
 	}
 
-	options = MAPI_OPTIONS_DONT_OPEN_MESSAGE;
 	if (priv->is_public_folder)
-		options |= MAPI_OPTIONS_USE_PFSTORE;
+		status = e_mapi_connection_open_public_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
+	else
+		status = e_mapi_connection_open_personal_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
 
-	fcud.cancellable = cancellable;
-	fcud.uids = uids;
+	if (status) {
+		status = e_mapi_connection_list_objects (conn, &obj_folder, build_rs_cb, build_rs_cb_data,
+							 gather_known_uids_cb, lku,
+							 cancellable, &mapi_error);
 
-	e_mapi_connection_fetch_items (conn, priv->fid, NULL, NULL, NULL,
-		mapi_book_utils_get_prop_list, GET_UIDS_ONLY,
-		fetch_contacts_uids_cb, &fcud, options, cancellable, &mapi_error);
+		e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
+	}
 
 	if (mapi_error) {
-		mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_OTHER_ERROR, _("Failed to fetch items from a server"));
+		mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_OTHER_ERROR, _("Failed to list items from a server"));
 		g_error_free (mapi_error);
 	}
 
@@ -1221,6 +1277,85 @@ ebbm_contacts_fetch_known_uids (EBookBackendMAPI *ebma, GCancellable *cancellabl
 }
 
 static void
+ebbm_contacts_transfer_contacts (EBookBackendMAPI *ebma,
+				 const GSList *uids,
+				 EDataBookView *book_view,
+				 gpointer notify_contact_data,
+				 GCancellable *cancellable,
+				 GError **error)
+{
+	EBookBackendMAPIContacts *ebmac;
+	EBookBackendMAPIContactsPrivate *priv;
+	EMapiConnection *conn;
+	struct TransferContactsData tcd = { 0 };
+	mapi_object_t obj_folder;
+	gboolean status;
+	GError *mapi_error = NULL;
+
+	e_return_data_book_error_if_fail (ebma != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
+	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_MAPI_CONTACTS (ebma), E_DATA_BOOK_STATUS_INVALID_ARG);
+
+	ebmac = E_BOOK_BACKEND_MAPI_CONTACTS (ebma);
+	e_return_data_book_error_if_fail (ebmac != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
+
+	priv = ebmac->priv;
+	e_return_data_book_error_if_fail (priv != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
+
+	e_book_backend_mapi_lock_connection (ebma);
+
+	conn = e_book_backend_mapi_get_connection (ebma);
+	if (!conn) {
+		e_book_backend_mapi_unlock_connection (ebma);
+		g_propagate_error (error, EDB_ERROR (REPOSITORY_OFFLINE));
+
+		return;
+	}
+
+	tcd.ebma = ebma;
+	tcd.book_view = book_view;
+	tcd.notify_contact_data = notify_contact_data;
+
+	if (priv->is_public_folder)
+		status = e_mapi_connection_open_public_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
+	else
+		status = e_mapi_connection_open_personal_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
+
+	if (status) {
+		GSList *mids = NULL;
+		const GSList *iter;
+
+		for (iter = uids; iter; iter = iter->next) {
+			const gchar *uid_str = iter->data;
+			mapi_id_t mid, *pmid;
+
+			if (!uid_str || !e_mapi_util_mapi_id_from_string (uid_str, &mid))
+				continue;
+
+			pmid = g_new0 (mapi_id_t, 1);
+			*pmid = mid;
+
+			mids = g_slist_prepend (mids, pmid);
+		}
+
+		if (mids)
+			status = e_mapi_connection_transfer_objects (conn, &obj_folder, mids, transfer_contacts_cb, &tcd, cancellable, &mapi_error);
+
+		e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
+
+		g_slist_free_full (mids, g_free);
+	}
+
+	if (!status) {
+		mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_OTHER_ERROR, _("Failed to transfer contacts from a server"));
+
+		if (mapi_error)
+			g_error_free (mapi_error);
+	}
+
+	e_book_backend_mapi_unlock_connection (ebma);
+}
+
+static void
 e_book_backend_mapi_contacts_init (EBookBackendMAPIContacts *backend)
 {
 	backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (backend, E_TYPE_BOOK_BACKEND_MAPI_CONTACTS, EBookBackendMAPIContactsPrivate);
@@ -1246,8 +1381,9 @@ e_book_backend_mapi_contacts_class_init (EBookBackendMAPIContactsClass *klass)
 
 	parent_class->op_connection_status_changed	= ebbm_contacts_connection_status_changed;
 	parent_class->op_get_status_message		= ebbm_contacts_get_status_message;
-	parent_class->op_fetch_contacts			= ebbm_contacts_fetch_contacts;
-	parent_class->op_fetch_known_uids		= ebbm_contacts_fetch_known_uids;
+	parent_class->op_get_contacts_count		= ebbm_contacts_get_contacts_count;
+	parent_class->op_list_known_uids		= ebbm_contacts_list_known_uids;
+	parent_class->op_transfer_contacts		= ebbm_contacts_transfer_contacts;
 }
 
 EBookBackend *
diff --git a/src/addressbook/e-book-backend-mapi-gal.c b/src/addressbook/e-book-backend-mapi-gal.c
index bf37fab..da4ac39 100644
--- a/src/addressbook/e-book-backend-mapi-gal.c
+++ b/src/addressbook/e-book-backend-mapi-gal.c
@@ -32,7 +32,7 @@ struct _EBookBackendMAPIGALPrivate
 };
 
 static gchar *
-get_uid_from_row (struct SRow *aRow, uint32_t row_index, mapi_id_t fid)
+get_uid_from_row (struct SRow *aRow, uint32_t row_index)
 {
 	gchar *suid = NULL;
 	const gchar *str;
@@ -48,7 +48,7 @@ get_uid_from_row (struct SRow *aRow, uint32_t row_index, mapi_id_t fid)
 
 		midptr = e_mapi_util_find_row_propval (aRow, PR_MID);
 
-		suid = e_mapi_util_mapi_ids_to_uid (fid, midptr ? *midptr : row_index);
+		suid = e_mapi_util_mapi_id_to_string (midptr ? *midptr : row_index);
 	}
 
 	return suid;
@@ -72,8 +72,6 @@ fetch_gal_cb (EMapiConnection *conn,
 	      GError **perror)
 {
 	struct FetchGalData *fgd = data;
-	struct timeval *last_modification = NULL, tv = { 0 };
-	struct SPropValue *spropval;
 	EContact *contact;
 
 	g_return_val_if_fail (conn != NULL, FALSE);
@@ -89,16 +87,12 @@ fetch_gal_cb (EMapiConnection *conn,
 	if (!e_contact_get_const (contact, E_CONTACT_UID)) {
 		gchar *suid;
 
-		suid = get_uid_from_row (aRow, row_index, fgd->fid);
+		suid = get_uid_from_row (aRow, row_index);
 		e_contact_set (contact, E_CONTACT_UID, suid);
 		g_free (suid);
 	}
 
-	spropval = get_SPropValue_SRow (aRow, PR_LAST_MODIFICATION_TIME);
-	if (spropval && get_mapi_SPropValue_date_timeval (&tv, *spropval) == MAPI_E_SUCCESS)
-		last_modification = &tv;
-
-	if (!e_book_backend_mapi_notify_contact_update (fgd->ebma, fgd->book_view, contact, last_modification, row_index, n_rows, fgd->notify_contact_data)) {
+	if (!e_book_backend_mapi_notify_contact_update (fgd->ebma, fgd->book_view, contact, row_index, n_rows, fgd->notify_contact_data)) {
 		g_object_unref (contact);
 		return FALSE;
 	}
@@ -108,34 +102,37 @@ fetch_gal_cb (EMapiConnection *conn,
 	return TRUE;
 }
 
-struct FetchGalUidsData
-{
-	GCancellable *cancellable;
-	GHashTable *uids;
-	mapi_id_t fid; /* folder ID of contacts */
-};
-
 static gboolean
-fetch_gal_uids_cb (EMapiConnection *conn,
-		   uint32_t row_index,
-		   uint32_t n_rows,
-		   struct SRow *aRow,
-		   gpointer data,
-		   GCancellable *cancellable,
-		   GError **perror)
+list_gal_uids_cb (EMapiConnection *conn,
+		  uint32_t row_index,
+		  uint32_t n_rows,
+		  struct SRow *aRow,
+		  gpointer user_data,
+		  GCancellable *cancellable,
+		  GError **perror)
 {
 	gchar *uid;
-	struct FetchGalUidsData *fgud = data;
+	struct ListKnownUidsData *lku = user_data;
 
 	g_return_val_if_fail (conn != NULL, FALSE);
 	g_return_val_if_fail (aRow != NULL, FALSE);
-	g_return_val_if_fail (data != NULL, FALSE);
+	g_return_val_if_fail (lku != NULL, FALSE);
+
+	uid = get_uid_from_row (aRow, row_index);
+	if (uid) {
+		const struct FILETIME *ft;
+		time_t tt;
 
-	uid = get_uid_from_row (aRow, row_index, fgud->fid);
-	if (uid)
-		g_hash_table_insert (fgud->uids, uid, GINT_TO_POINTER (1));
+		ft = e_mapi_util_find_row_propval (aRow, PidTagLastModificationTime);
+		tt = ft ? e_mapi_util_filetime_to_time_t (ft) : 1;
 
-	return !g_cancellable_is_cancelled (fgud->cancellable);
+		if (lku->latest_last_modify < tt)
+			lku->latest_last_modify = tt;
+
+		g_hash_table_insert (lku->uid_to_rev, uid, mapi_book_utils_timet_to_string (tt));
+	}
+
+	return !g_cancellable_is_cancelled (cancellable);
 }
 
 static void
@@ -175,7 +172,12 @@ ebbm_gal_get_status_message (EBookBackendMAPI *ebma, gint index, gint total)
 }
 
 static void
-ebbm_gal_fetch_contacts (EBookBackendMAPI *ebma, BuildRestrictionsCB build_rs_cb, gpointer build_rs_cb_data, EDataBookView *book_view, gpointer notify_contact_data, GError **error)
+ebbm_gal_transfer_contacts (EBookBackendMAPI *ebma,
+			    const GSList *uids,
+			    EDataBookView *book_view,
+			    gpointer notify_contact_data,
+			    GCancellable *cancellable,
+			    GError **error)
 {
 	GError *mapi_error = NULL;
 	struct FetchGalData fgd = { 0 };
@@ -215,7 +217,7 @@ ebbm_gal_fetch_contacts (EBookBackendMAPI *ebma, BuildRestrictionsCB build_rs_cb
 	fgd.notify_contact_data = notify_contact_data;
 	fgd.fid = e_mapi_connection_get_default_folder_id (conn, olFolderContacts, NULL, NULL);
 
-	fetch_successful = e_mapi_connection_fetch_gal (conn, build_rs_cb, build_rs_cb_data,
+	fetch_successful = e_mapi_connection_fetch_gal (conn, NULL, NULL,
 		mapi_book_utils_get_prop_list, GET_ALL_KNOWN_IDS,
 		fetch_gal_cb, &fgd, NULL, &mapi_error);
 
@@ -240,15 +242,32 @@ ebbm_gal_fetch_contacts (EBookBackendMAPI *ebma, BuildRestrictionsCB build_rs_cb
 }
 
 static void
-ebbm_gal_fetch_known_uids (EBookBackendMAPI *ebma, GCancellable *cancellable, GHashTable *uids, GError **error)
+ebbm_gal_get_contacts_count (EBookBackendMAPI *ebma,
+			     guint32 *obj_total,
+			     GCancellable *cancellable,
+			     GError **error)
+{
+	e_return_data_book_error_if_fail (ebma != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
+	e_return_data_book_error_if_fail (obj_total != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
+
+	/* just a fake value, to check by ids */
+	*obj_total = -1;
+}
+
+static void
+ebbm_gal_list_known_uids (EBookBackendMAPI *ebma,
+			  BuildRestrictionsCB build_rs_cb,
+			  gpointer build_rs_cb_data,
+			  struct ListKnownUidsData *lku,
+			  GCancellable *cancellable,
+			  GError **error)
 {
 	EMapiConnection *conn;
 	GError *mapi_error = NULL;
-	struct FetchGalUidsData fgud = { 0 };
 
 	g_return_if_fail (ebma != NULL);
-	g_return_if_fail (cancellable != NULL);
-	g_return_if_fail (uids != NULL);
+	g_return_if_fail (lku != NULL);
+	g_return_if_fail (lku->uid_to_rev != NULL);
 
 	e_book_backend_mapi_lock_connection (ebma);
 
@@ -259,13 +278,9 @@ ebbm_gal_fetch_known_uids (EBookBackendMAPI *ebma, GCancellable *cancellable, GH
 		return;
 	}
 
-	fgud.cancellable = cancellable;
-	fgud.uids = uids;
-	fgud.fid = e_mapi_connection_get_default_folder_id (conn, olFolderContacts, cancellable, NULL);
-
 	e_mapi_connection_fetch_gal (conn, NULL, NULL,
 		mapi_book_utils_get_prop_list, GET_UIDS_ONLY,
-		fetch_gal_uids_cb, &fgud, cancellable, &mapi_error);
+		list_gal_uids_cb, lku, cancellable, &mapi_error);
 
 	if (mapi_error) {
 		mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_OTHER_ERROR, _("Failed to fetch GAL entries"));
@@ -296,8 +311,9 @@ e_book_backend_mapi_gal_class_init (EBookBackendMAPIGALClass *klass)
 	parent_class->op_modify_contacts	= ebbm_gal_modify_contacts;
 
 	parent_class->op_get_status_message	= ebbm_gal_get_status_message;
-	parent_class->op_fetch_contacts		= ebbm_gal_fetch_contacts;
-	parent_class->op_fetch_known_uids	= ebbm_gal_fetch_known_uids;
+	parent_class->op_get_contacts_count	= ebbm_gal_get_contacts_count;
+	parent_class->op_list_known_uids	= ebbm_gal_list_known_uids;
+	parent_class->op_transfer_contacts	= ebbm_gal_transfer_contacts;
 }
 
 /**
diff --git a/src/addressbook/e-book-backend-mapi.c b/src/addressbook/e-book-backend-mapi.c
index cdc3901..3e74584 100644
--- a/src/addressbook/e-book-backend-mapi.c
+++ b/src/addressbook/e-book-backend-mapi.c
@@ -57,9 +57,13 @@ struct _EBookBackendMAPIPrivate
 
 	GThread *update_cache_thread;
 	GCancellable *update_cache;
+	time_t last_update_cache;
 
 	EBookBackendSqliteDB *db;
 	GHashTable *running_book_views;
+
+	guint32 last_server_contact_count;
+	time_t last_modify_time;
 };
 
 #define ELEMENT_TYPE_MASK   0xF /* mask where the real type of the element is stored */
@@ -124,60 +128,6 @@ static const struct field_element_mapping {
 	};
 
 static gboolean
-ebbm_get_cache_time (EBookBackendMAPI *ebma, glong *cache_seconds)
-{
-	GError *error = NULL;
-	GTimeVal tv = { 0 };
-	gchar *last_update;
-	gboolean ret = TRUE;
-
-	g_return_val_if_fail (ebma != NULL, FALSE);
-	g_return_val_if_fail (ebma->priv != NULL, FALSE);
-	g_return_val_if_fail (ebma->priv->db != NULL, FALSE);
-	g_return_val_if_fail (cache_seconds != NULL, FALSE);
-
-	last_update = e_book_backend_sqlitedb_get_sync_data (ebma->priv->db, EMA_EBB_CACHE_FOLDERID, &error);
-	ret = !error && last_update && g_time_val_from_iso8601 (last_update, &tv);
-	if (error)
-		g_error_free (error);
-	g_free (last_update);
-
-	if (ret)
-		*cache_seconds = tv.tv_sec;
-
-	return ret;
-}
-
-static void
-ebbm_set_cache_time (EBookBackendMAPI *ebma, glong cache_seconds)
-{
-	GError *error = NULL;
-	gchar *iso_time = NULL;
-
-	g_return_if_fail (ebma != NULL);
-	g_return_if_fail (ebma->priv != NULL);
-	g_return_if_fail (ebma->priv->db != NULL);
-
-	if (cache_seconds > 0) {
-		GTimeVal tv = { 0 };
-
-		tv.tv_sec = cache_seconds;
-		iso_time = g_time_val_to_iso8601 (&tv);
-	}
-
-	e_book_backend_sqlitedb_set_sync_data (ebma->priv->db,
-					       EMA_EBB_CACHE_FOLDERID,
-					       iso_time ? iso_time : "0",
-					       &error);
-	g_free (iso_time);
-
-	if (error) {
-		e_mapi_debug_print ("%s: Failed to set value: %s", G_STRFUNC, error->message);
-		g_error_free (error);
-	}
-}
-
-static gboolean
 pick_view_cb (EDataBookView *view, gpointer user_data)
 {
 	EDataBookView **pick = user_data;
@@ -213,12 +163,6 @@ complete_views (EBookBackendMAPI *ebma)
 	e_book_backend_foreach_view (E_BOOK_BACKEND (ebma), complete_view_cb, NULL);
 }
 
-struct FetchContactsData
-{
-	glong last_notification;
-	glong last_modification;
-};
-
 static void
 ebbm_notify_connection_status (EBookBackendMAPI *ebma, gboolean is_online)
 {
@@ -235,10 +179,14 @@ ebbm_notify_connection_status (EBookBackendMAPI *ebma, gboolean is_online)
 }
 
 static void
-ebbm_fetch_contacts (EBookBackendMAPI *ebma, BuildRestrictionsCB build_rs_cb, gpointer build_rs_cb_data, EDataBookView *book_view, glong *last_modification_secs, GError **error)
+ebbm_transfer_contacts (EBookBackendMAPI *ebma,
+			const GSList *uids,
+			EDataBookView *book_view,
+			GCancellable *cancellable,
+			GError **error)
 {
 	EBookBackendMAPIClass *ebmac;
-	struct FetchContactsData notify_data = { 0 };
+	glong last_notification = 0;
 
 	g_return_if_fail (ebma != NULL);
 	g_return_if_fail (ebma->priv != NULL);
@@ -246,12 +194,9 @@ ebbm_fetch_contacts (EBookBackendMAPI *ebma, BuildRestrictionsCB build_rs_cb, gp
 
 	ebmac = E_BOOK_BACKEND_MAPI_GET_CLASS (ebma);
 	g_return_if_fail (ebmac != NULL);
-	g_return_if_fail (ebmac->op_fetch_contacts != NULL);
+	g_return_if_fail (ebmac->op_transfer_contacts != NULL);
 
-	ebmac->op_fetch_contacts (ebma, build_rs_cb, build_rs_cb_data, book_view, &notify_data, error);
-
-	if (last_modification_secs && *last_modification_secs < notify_data.last_modification)
-		*last_modification_secs = notify_data.last_modification;
+	ebmac->op_transfer_contacts (ebma, uids, book_view, &last_notification, cancellable, error);
 }
 
 static gboolean
@@ -266,59 +211,17 @@ unref_backend_idle_cb (gpointer data)
 	return FALSE;
 }
 
-static gboolean
-ebbm_build_cache_update_restriction (EMapiConnection *conn,
-				     mapi_id_t fid,
-				     TALLOC_CTX *mem_ctx,
-				     struct mapi_SRestriction **restrictions,
-				     gpointer user_data,
-				     GCancellable *cancellable,
-				     GError **perror)
-{
-	EBookBackendMAPI *ebma = user_data;
-	struct mapi_SRestriction *restriction;
-	EBookBackendMAPIPrivate *priv;
-	struct SPropValue sprop;
-	struct timeval t = { 0 };
-	glong last_update_secs = 0;
-
-	g_return_val_if_fail (ebma != NULL, FALSE);
-	g_return_val_if_fail (mem_ctx != NULL, FALSE);
-	g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (ebma), FALSE);
-	g_return_val_if_fail (restrictions != NULL, FALSE);
-
-	priv = ebma->priv;
-	g_return_val_if_fail (priv != NULL, FALSE);
-	g_return_val_if_fail (priv->db != NULL, FALSE);
-
-	if (!ebbm_get_cache_time (ebma, &last_update_secs) || last_update_secs <= 0)
-		return TRUE;
-
-	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_GE;
-	restriction->res.resProperty.ulPropTag = PR_LAST_MODIFICATION_TIME;
-
-	t.tv_sec = last_update_secs;
-	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;
-}
-
 static gpointer
 ebbm_update_cache_cb (gpointer data)
 {
 	EBookBackendMAPI *ebma = (EBookBackendMAPI *) data;
 	EBookBackendMAPIPrivate *priv;
 	EBookBackendMAPIClass *ebmac;
-	glong last_modification_secs = 0;
+	guint32 server_stored_contacts = 0;
+	GHashTable *local_known_uids, *server_known_uids;
+	time_t restr_tt = 0;
+	gboolean partial_update = FALSE;
+	GCancellable *cancellable;
 	GError *error = NULL;
 
 	g_return_val_if_fail (ebma != NULL, NULL);
@@ -332,45 +235,74 @@ ebbm_update_cache_cb (gpointer data)
 	ebmac = E_BOOK_BACKEND_MAPI_GET_CLASS (ebma);
 	g_return_val_if_fail (ebmac != NULL, NULL);
 
-	g_cancellable_reset (priv->update_cache);
+	cancellable = priv->update_cache;
+	g_cancellable_reset (cancellable);
 
-	if (!g_cancellable_is_cancelled (priv->update_cache) && ebmac->op_fetch_contacts) {
-		/* get time stored in a cache, to always use the latest last modification time */
-		if (!ebbm_get_cache_time (ebma, &last_modification_secs))
-			last_modification_secs = 0;
+	local_known_uids = e_book_backend_sqlitedb_get_uids_and_rev (priv->db, EMA_EBB_CACHE_FOLDERID, &error);
+	server_known_uids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
-		ebbm_fetch_contacts (ebma, ebbm_build_cache_update_restriction, ebma, NULL, &last_modification_secs, &error);
-		e_book_backend_sqlitedb_set_is_populated (priv->db, EMA_EBB_CACHE_FOLDERID, error != NULL, NULL);
+	if (!error && !g_cancellable_is_cancelled (cancellable) && ebmac->op_get_contacts_count) {
+		ebmac->op_get_contacts_count (ebma, &server_stored_contacts, cancellable, &error);
 	}
 
-	if (!error && !g_cancellable_is_cancelled (priv->update_cache) && ebmac->op_fetch_known_uids) {
-		GHashTable *uids = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
+	if (!error && !g_cancellable_is_cancelled (cancellable) && ebmac->op_list_known_uids) {
+		struct ListKnownUidsData lku;
 
-		ebmac->op_fetch_known_uids (ebma, priv->update_cache, uids, &error);
+		restr_tt = priv->last_modify_time && server_stored_contacts == g_hash_table_size (local_known_uids) ? priv->last_modify_time + 1 : 0;
+		partial_update = restr_tt > 0;
 
-		if (!error && !g_cancellable_is_cancelled (priv->update_cache)) {
-			GSList *cache_keys, *c;
+		lku.uid_to_rev = server_known_uids;
+		lku.latest_last_modify = priv->last_modify_time;
 
-			cache_keys = e_book_backend_sqlitedb_search_uids (priv->db, EMA_EBB_CACHE_FOLDERID, NULL, NULL, NULL);
+		ebmac->op_list_known_uids (ebma, partial_update ? e_mapi_utils_build_last_modify_restriction : NULL, &restr_tt, &lku, cancellable, &error);
 
-			for (c = cache_keys; c; c = c->next) {
-				const gchar *uid = c->data;
+		restr_tt = lku.latest_last_modify;
+	}
 
-				if (!uid || g_hash_table_lookup (uids, uid))
-					continue;
+	if (!error && !g_cancellable_is_cancelled (cancellable) && ebmac->op_transfer_contacts && local_known_uids) {
+		GSList *uids = NULL;
+		GHashTableIter iter;
+		gpointer key, value;
 
-				e_book_backend_mapi_notify_contact_removed (ebma, uid);
+		g_hash_table_iter_init (&iter, server_known_uids);
+		while (g_hash_table_iter_next (&iter, &key, &value)) {
+			const gchar *uid = key, *rev = value, *local_rev;
+
+			local_rev = g_hash_table_lookup (local_known_uids, uid);
+			if (g_strcmp0 (local_rev, rev) != 0) {
+				uids = g_slist_prepend (uids, (gpointer) uid);
 			}
 
-			ebbm_set_cache_time (ebma, last_modification_secs);
+			g_hash_table_remove (local_known_uids, uid);
+		}
+
+		if (uids)
+			ebbm_transfer_contacts (ebma, uids, NULL, cancellable, &error);
+
+		if (!error && !g_cancellable_is_cancelled (cancellable) && !partial_update) {
+			g_hash_table_iter_init (&iter, local_known_uids);
+			while (g_hash_table_iter_next (&iter, &key, &value)) {
+				const gchar *uid = key;
 
-			g_slist_foreach (cache_keys, (GFunc) g_free, NULL);
-			g_slist_free (cache_keys);
+				if (!uid)
+					continue;
+
+				e_book_backend_mapi_notify_contact_removed (ebma, uid);
+			}
 		}
 
-		g_hash_table_destroy (uids);
+		priv->last_server_contact_count = server_stored_contacts;
+		priv->last_modify_time = restr_tt;
+
+		/* has borrowed data from server_known_uids */
+		g_slist_free (uids);
 	}
 
+	priv->last_update_cache = time(NULL);
+
+	g_hash_table_destroy (server_known_uids);
+	if (local_known_uids)
+		g_hash_table_destroy (local_known_uids);
 	if (error)
 		g_error_free (error);
 
@@ -386,6 +318,34 @@ ebbm_update_cache_cb (gpointer data)
 }
 
 static void
+ebbm_maybe_invoke_cache_update (EBookBackendMAPI *ebma)
+{
+	EBookBackendMAPIPrivate *priv;
+
+	g_return_if_fail (ebma != NULL);
+	g_return_if_fail (ebma->priv != NULL);
+
+	priv = ebma->priv;
+
+	if (priv->update_cache_thread) {
+		if (!g_cancellable_is_cancelled (priv->update_cache))
+			return;
+
+		g_thread_join (priv->update_cache_thread);
+		priv->update_cache_thread = NULL;
+	}
+
+	/* do not update more often than each 10 minutes */
+	if (time (NULL) - priv->last_update_cache >= 60 * 10) {
+		g_object_ref (ebma);
+
+		priv->update_cache_thread = g_thread_create (ebbm_update_cache_cb, ebma, TRUE, NULL);
+		if (!priv->update_cache_thread)
+			g_object_unref (ebma);
+	}
+}
+
+static void
 ebbm_connect_user (EBookBackendMAPI *ebma, GCancellable *cancellable, const gchar *password, GError **error)
 {
 	EBookBackendMAPIPrivate *priv = ebma->priv;
@@ -445,11 +405,7 @@ ebbm_connect_user (EBookBackendMAPI *ebma, GCancellable *cancellable, const gcha
 		ebbm_notify_connection_status (ebma, TRUE);
 
 		if (!g_cancellable_is_cancelled (cancellable) /* && priv->marked_for_offline */) {
-			g_object_ref (ebma);
-
-			priv->update_cache_thread = g_thread_create (ebbm_update_cache_cb, ebma, TRUE, NULL);
-			if (!priv->update_cache_thread)
-				g_object_unref (ebma);
+			ebbm_maybe_invoke_cache_update (ebma);
 		}
 	}
 }
@@ -745,19 +701,9 @@ ebbm_book_view_thread (gpointer data)
 
 		ebmac = E_BOOK_BACKEND_MAPI_GET_CLASS (bvtd->ebma);
 		if (ebmac && ebmac->op_book_view_thread)
-			ebmac->op_book_view_thread (bvtd->ebma, bvtd->book_view, &error);
-
-		if (!error && !e_book_backend_sqlitedb_get_is_populated (priv->db, EMA_EBB_CACHE_FOLDERID, &error)) {
-			/* todo: create restriction based on the book_view */
-			if (!error) {
-				g_cancellable_reset (priv->update_cache);
-				ebbm_fetch_contacts (bvtd->ebma, NULL, NULL, bvtd->book_view, NULL, &error);
-				g_cancellable_cancel (priv->update_cache);
-			}
+			ebmac->op_book_view_thread (bvtd->ebma, bvtd->book_view, priv->update_cache, &error);
 
-			if (!error)
-				e_book_backend_sqlitedb_set_is_populated (priv->db, EMA_EBB_CACHE_FOLDERID, TRUE, &error);
-		}
+		ebbm_maybe_invoke_cache_update (bvtd->ebma);
 	}
 
 	if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
@@ -891,7 +837,7 @@ ebbm_operation_cb (OperationBase *op, gboolean cancelled, EBookBackend *backend)
 				const GSList *l;
 
 				for (l = added_contacts; l; l = l->next) {
-					e_book_backend_mapi_notify_contact_update (ebma, NULL, E_CONTACT (l->data), NULL, -1, -1, NULL);
+					e_book_backend_mapi_notify_contact_update (ebma, NULL, E_CONTACT (l->data), -1, -1, NULL);
 				}
 			}
 
@@ -947,7 +893,7 @@ ebbm_operation_cb (OperationBase *op, gboolean cancelled, EBookBackend *backend)
 				const GSList *l;
 
 				for (l = modified_contacts; l; l = l->next) {
-					e_book_backend_mapi_notify_contact_update (ebma, NULL, E_CONTACT (l->data), NULL, -1, -1, NULL);
+					e_book_backend_mapi_notify_contact_update (ebma, NULL, E_CONTACT (l->data), -1, -1, NULL);
 				}
 			}
 
@@ -1317,6 +1263,9 @@ e_book_backend_mapi_init (EBookBackendMAPI *ebma)
 
 	ebma->priv->update_cache = g_cancellable_new ();
 	ebma->priv->update_cache_thread = NULL;
+	ebma->priv->last_update_cache = 0;
+	ebma->priv->last_server_contact_count = 0;
+	ebma->priv->last_modify_time = 0;
 
 	g_signal_connect (
 		ebma, "notify::online",
@@ -1371,30 +1320,31 @@ e_book_backend_mapi_class_init (EBookBackendMAPIClass *klass)
 
 	g_type_class_add_private (klass, sizeof (EBookBackendMAPIPrivate));
 
-	object_class->dispose                     = ebbm_dispose;
-
-	backend_class->open			  = ebbm_op_open;
-	backend_class->remove			  = ebbm_op_remove;
-	backend_class->create_contacts		  = ebbm_op_create_contacts;
-	backend_class->remove_contacts		  = ebbm_op_remove_contacts;
-	backend_class->modify_contacts		  = ebbm_op_modify_contacts;
-	backend_class->get_contact                = ebbm_op_get_contact;
-	backend_class->get_contact_list           = ebbm_op_get_contact_list;
-	backend_class->start_book_view            = ebbm_op_start_book_view;
-	backend_class->stop_book_view             = ebbm_op_stop_book_view;
-	backend_class->authenticate_user          = ebbm_op_authenticate_user;
-	backend_class->get_backend_property	  = ebbm_op_get_backend_property;
-	klass->op_open				  = ebbm_open;
-	klass->op_remove                          = ebbm_remove;
-	klass->op_authenticate_user               = ebbm_authenticate_user;
-	klass->op_get_contact                     = ebbm_get_contact;
-	klass->op_get_contact_list                = ebbm_get_contact_list;
-
-	klass->op_connection_status_changed       = NULL;
-	klass->op_get_status_message              = NULL;
-	klass->op_book_view_thread                = NULL;
-	klass->op_fetch_contacts                  = NULL;
-	klass->op_fetch_known_uids                = NULL;
+	object_class->dispose			= ebbm_dispose;
+
+	backend_class->open			= ebbm_op_open;
+	backend_class->remove			= ebbm_op_remove;
+	backend_class->create_contacts		= ebbm_op_create_contacts;
+	backend_class->remove_contacts		= ebbm_op_remove_contacts;
+	backend_class->modify_contacts		= ebbm_op_modify_contacts;
+	backend_class->get_contact		= ebbm_op_get_contact;
+	backend_class->get_contact_list		= ebbm_op_get_contact_list;
+	backend_class->start_book_view		= ebbm_op_start_book_view;
+	backend_class->stop_book_view		= ebbm_op_stop_book_view;
+	backend_class->authenticate_user	= ebbm_op_authenticate_user;
+	backend_class->get_backend_property	= ebbm_op_get_backend_property;
+	klass->op_open				= ebbm_open;
+	klass->op_remove			= ebbm_remove;
+	klass->op_authenticate_user		= ebbm_authenticate_user;
+	klass->op_get_contact			= ebbm_get_contact;
+	klass->op_get_contact_list		= ebbm_get_contact_list;
+
+	klass->op_connection_status_changed	= NULL;
+	klass->op_get_status_message		= NULL;
+	klass->op_book_view_thread		= NULL;
+	klass->op_get_contacts_count		= NULL;
+	klass->op_list_known_uids		= NULL;
+	klass->op_transfer_contacts		= NULL;
 }
 
 gboolean
@@ -1529,14 +1479,14 @@ get_current_time_ms (void)
 	return tv.tv_sec * 1000 + tv.tv_usec / 1000;
 }
 
-/* called from op_fetch_contacts - book_view and notify_contact_data are taken from there;
-   notify_contact_data is a pointer to FetchContactsData, if not NULL;
+/* called from op_transfer_contacts - book_view and notify_contact_data are taken from there;
+   notify_contact_data is a pointer to glong last_notification, if not NULL;
    returns whether can continue with fetching */
 gboolean
-e_book_backend_mapi_notify_contact_update (EBookBackendMAPI *ebma, EDataBookView *pbook_view, EContact *contact, const struct timeval *pr_last_modification_time, gint index, gint total, gpointer notify_contact_data)
+e_book_backend_mapi_notify_contact_update (EBookBackendMAPI *ebma, EDataBookView *pbook_view, EContact *contact, gint index, gint total, gpointer notify_contact_data)
 {
 	EBookBackendMAPIPrivate *priv;
-	struct FetchContactsData *fcd = notify_contact_data;
+	glong *last_notification = notify_contact_data;
 	EDataBookView *book_view = pbook_view;
 	GError *error = NULL;
 
@@ -1553,13 +1503,13 @@ e_book_backend_mapi_notify_contact_update (EBookBackendMAPI *ebma, EDataBookView
 		book_view = ebbm_pick_book_view (ebma);
 
 	if (book_view) {
-		guint32 current_time;
+		glong current_time;
 
 		if (!e_book_backend_mapi_book_view_is_running (ebma, book_view))
 			return FALSE;
 
 		current_time = get_current_time_ms ();
-		if (index > 0 && fcd && current_time - fcd->last_notification > 333) {
+		if (index > 0 && last_notification && current_time - *last_notification > 333) {
 			gchar *status_msg = NULL;
 			EBookBackendMAPIClass *ebmac = E_BOOK_BACKEND_MAPI_GET_CLASS (ebma);
 
@@ -1571,7 +1521,7 @@ e_book_backend_mapi_notify_contact_update (EBookBackendMAPI *ebma, EDataBookView
 
 			g_free (status_msg);
 
-			fcd->last_notification = current_time;
+			*last_notification = current_time;
 		}
 	}
 
@@ -1583,14 +1533,11 @@ e_book_backend_mapi_notify_contact_update (EBookBackendMAPI *ebma, EDataBookView
 					     FALSE, &error);
 	if (!error) {
 		e_book_backend_notify_update (E_BOOK_BACKEND (ebma), contact);
-
-		if (fcd && pr_last_modification_time) {
-			if (fcd->last_modification < pr_last_modification_time->tv_sec)
-				fcd->last_modification = pr_last_modification_time->tv_sec;
-		}
 		return TRUE;
 	}
+
 	g_error_free (error);
+
 	return FALSE;
 }
 
@@ -1708,9 +1655,10 @@ mapi_book_utils_get_prop_list (EMapiConnection *conn,
 	};
 
 	static uint32_t uids_only_ids[] = {
-		PR_FID,
-		PR_MID,
-		PR_EMAIL_ADDRESS_UNICODE
+		PidTagFolderId,
+		PidTagMid,
+		PidTagLastModificationTime,
+		PidTagEmailAddress
 	};
 
 	/* do not make this array static, the function modifies it on run */
@@ -1879,13 +1827,8 @@ mapi_book_utils_contact_from_props (EMapiConnection *conn, mapi_id_t fid, const
 			if (value && mappings[i].element_type == PT_SYSTIME) {
 				const struct FILETIME *t = value;
 				gchar *buff = NULL;
-				GTimeVal tv;
-
-				tv.tv_sec = e_mapi_util_filetime_to_time_t (t);
-				tv.tv_usec = 0;
-				
-				buff = g_time_val_to_iso8601 (&tv);
 
+				buff = mapi_book_utils_timet_to_string (e_mapi_util_filetime_to_time_t (t));
 				if (buff)
 					e_contact_set (contact, mappings[i].field_id, buff);
 
@@ -2006,3 +1949,14 @@ mapi_error_to_edb_error (GError **perror, const GError *mapi_error, EDataBookSta
 
 	g_free (err_msg);
 }
+
+gchar *
+mapi_book_utils_timet_to_string (time_t tt)
+{
+	GTimeVal tv;
+
+	tv.tv_sec = tt;
+	tv.tv_usec = 0;
+
+	return g_time_val_to_iso8601 (&tv);
+}
diff --git a/src/addressbook/e-book-backend-mapi.h b/src/addressbook/e-book-backend-mapi.h
index ffce6d5..5a65f95 100644
--- a/src/addressbook/e-book-backend-mapi.h
+++ b/src/addressbook/e-book-backend-mapi.h
@@ -50,6 +50,12 @@ typedef struct
 	EBookBackendMAPIPrivate *priv;
 } EBookBackendMAPI;
 
+struct ListKnownUidsData
+{
+	GHashTable *uid_to_rev;
+	time_t latest_last_modify;
+};
+
 typedef struct
 {
 	EBookBackendClass parent_class;
@@ -74,17 +80,21 @@ typedef struct
 	/* function called for each new book_view, in a separate thread;
 	   this function is optional, contacts from cache are always processed
 	   before this function call */
-	void (*op_book_view_thread) (EBookBackendMAPI *ebma, EDataBookView *book_view, GError **error);
+	void (*op_book_view_thread) (EBookBackendMAPI *ebma, EDataBookView *book_view, GCancellable *cancellable, GError **error);
 
-	/* function called to populate cache or similar operations;
-	   restriction and book_view can be NULL, call e_book_backend_mapi_notify_contact_update for each
-	   fetched contact with this book_view and notify_contact_data */
-	void (*op_fetch_contacts) (EBookBackendMAPI *ebma, BuildRestrictionsCB build_rs_cb, gpointer build_rs_cb_data, EDataBookView *book_view, gpointer notify_contact_data, GError **error);
+	/* gets current count of contacts in the folder corresponding to the backend */
+	void (*op_get_contacts_count) (EBookBackendMAPI *ebma, guint32 *obj_total, GCancellable *cancellable, GError **error);
 
 	/* function to fetch list of known uids (strings) on the server;
-	   it's used to synchronize local cache with deleted items;
-	   uids has the uid key, as a newly allocated string; value should be GINT_TO_POINTER(1) always */
-	void (*op_fetch_known_uids) (EBookBackendMAPI *ebma, GCancellable *cancellable, GHashTable *uids, GError **error);
+	   it's used to synchronize local cache with available items;
+	   uids has the uid key, as a newly allocated string;
+	   value is a revision (REV) field value as newly allocated string */
+	void (*op_list_known_uids) (EBookBackendMAPI *ebma, BuildRestrictionsCB build_rs_cb, gpointer build_rs_cb_data, struct ListKnownUidsData *lku, GCancellable *cancellable, GError **error);
+
+	/* function called to populate cache or similar operations;
+	   book_view can be NULL, call e_book_backend_mapi_notify_contact_update for each
+	   transferred contact with this book_view and notify_contact_data */
+	void (*op_transfer_contacts) (EBookBackendMAPI *ebma, const GSList *uids, EDataBookView *book_view, gpointer notify_contact_data, GCancellable *cancellable, GError **error);
 } EBookBackendMAPIClass;
 
 GType e_book_backend_mapi_get_type (void);
@@ -98,7 +108,7 @@ void e_book_backend_mapi_get_db (EBookBackendMAPI *ebma, EBookBackendSqliteDB **
 gboolean e_book_backend_mapi_book_view_is_running (EBookBackendMAPI *ebma, EDataBookView *book_view);
 void e_book_backend_mapi_update_view_by_cache (EBookBackendMAPI *ebma, EDataBookView *book_view, GError **error);
 gboolean e_book_backend_mapi_is_marked_for_offline (EBookBackendMAPI *ebma);
-gboolean e_book_backend_mapi_notify_contact_update (EBookBackendMAPI *ebma, EDataBookView *book_view, EContact *contact, const struct timeval *pr_last_modification_time, gint index, gint total, gpointer notify_contact_data);
+gboolean e_book_backend_mapi_notify_contact_update (EBookBackendMAPI *ebma, EDataBookView *book_view, EContact *contact, gint index, gint total, gpointer notify_contact_data);
 void e_book_backend_mapi_notify_contact_removed (EBookBackendMAPI *ebma, const gchar *uid);
 void   e_book_backend_mapi_cache_set (EBookBackendMAPI *ebma, const gchar *key, const gchar *value);
 gchar *e_book_backend_mapi_cache_get (EBookBackendMAPI *ebma, const gchar *key);
@@ -135,6 +145,10 @@ gboolean mapi_book_utils_get_prop_list (EMapiConnection *conn,
 /* only one of mapi_properties and aRow can be set */
 EContact *mapi_book_utils_contact_from_props (EMapiConnection *conn, mapi_id_t fid, const gchar *book_uri, struct mapi_SPropValue_array *mapi_properties, struct SRow *aRow);
 
+/* converts time_t to string, suitable for E_CONTACT_REV field value;
+   free returned pointer with g_free() */
+gchar *mapi_book_utils_timet_to_string (time_t tt);
+
 G_END_DECLS
 
 #endif /* __E_BOOK_BACKEND_MAPI_H__ */
diff --git a/src/libexchangemapi/e-mapi-utils.c b/src/libexchangemapi/e-mapi-utils.c
index 58ac349..6d3ff42 100644
--- a/src/libexchangemapi/e-mapi-utils.c
+++ b/src/libexchangemapi/e-mapi-utils.c
@@ -66,26 +66,6 @@ e_mapi_util_mapi_id_from_string (const gchar *str, mapi_id_t *id)
 	return (n == 1);
 }
 
-/* NOTE: We use the UID as a combination of the folder-id and the message-id.
- * Specifically, it is in this format: ("%016" G_GINT64_MODIFIER "X%016" G_GINT64_MODIFIER "X", fid, mid).
- */
-inline gchar *
-e_mapi_util_mapi_ids_to_uid (mapi_id_t fid, mapi_id_t mid)
-{
-	return g_strdup_printf ("%016" G_GINT64_MODIFIER "X%016" G_GINT64_MODIFIER "X", fid, mid);
-}
-
-inline gboolean
-e_mapi_util_mapi_ids_from_uid (const gchar *str, mapi_id_t *fid, mapi_id_t *mid)
-{
-	gint n = 0;
-
-	if (str && *str)
-		n = sscanf (str, "%016" G_GINT64_MODIFIER "X%016" G_GINT64_MODIFIER "X", fid, mid);
-
-	return (n == 2);
-}
-
 /*
  * Retrieve the property value for a given SPropValue and property tag.
  *
diff --git a/src/libexchangemapi/e-mapi-utils.h b/src/libexchangemapi/e-mapi-utils.h
index 79fd661..a40d39f 100644
--- a/src/libexchangemapi/e-mapi-utils.h
+++ b/src/libexchangemapi/e-mapi-utils.h
@@ -31,9 +31,6 @@
 gchar *  e_mapi_util_mapi_id_to_string (mapi_id_t id);
 gboolean e_mapi_util_mapi_id_from_string (const gchar *str, mapi_id_t *id);
 
-gchar *  e_mapi_util_mapi_ids_to_uid (mapi_id_t fid, mapi_id_t mid);
-gboolean e_mapi_util_mapi_ids_from_uid (const gchar *str, mapi_id_t *fid, mapi_id_t *mid);
-
 gconstpointer	e_mapi_util_find_SPropVal_array_propval (struct SPropValue *values, uint32_t proptag);
 gconstpointer	e_mapi_util_find_SPropVal_array_namedid (struct SPropValue *values, EMapiConnection *conn, mapi_id_t fid, uint32_t namedid);
 gconstpointer	e_mapi_util_find_row_propval (struct SRow *aRow, uint32_t proptag);
@@ -116,7 +113,7 @@ 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,
+								 gpointer user_data, /* const time_t * */
 								 GCancellable *cancellable,
 								 GError **perror);
 struct FolderBasicPropertiesData



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