[evolution-mapi] Rewrite GAL to support restrictions and partial search results



commit 8d3ba4d496770cb78a87a16979f30186b016daaa
Author: Milan Crha <mcrha redhat com>
Date:   Wed Dec 21 19:33:49 2011 +0100

    Rewrite GAL to support restrictions and partial search results

 .../e-mapi-account-listener.c                      |    5 +-
 src/account-setup-eplugin/e-mapi-account-setup.c   |    4 +
 src/addressbook/e-book-backend-mapi-contacts.c     |  389 +------
 src/addressbook/e-book-backend-mapi-gal.c          |  201 ++--
 src/addressbook/e-book-backend-mapi.c              |  938 ++--------------
 src/addressbook/e-book-backend-mapi.h              |   28 +-
 src/libexchangemapi/Makefile.am                    |    5 +
 src/libexchangemapi/e-mapi-book-utils.c            | 1134 ++++++++++++++++++
 src/libexchangemapi/e-mapi-book-utils.h            |   67 ++
 src/libexchangemapi/e-mapi-connection.c            |  942 +++++++++++++---
 src/libexchangemapi/e-mapi-connection.h            |   50 +-
 src/libexchangemapi/e-mapi-debug.c                 | 1200 ++++++++++----------
 src/libexchangemapi/e-mapi-debug.h                 |    4 +-
 src/libexchangemapi/e-mapi-utils.c                 |  119 --
 src/libexchangemapi/e-mapi-utils.h                 |  145 ++--
 15 files changed, 2967 insertions(+), 2264 deletions(-)
---
diff --git a/src/account-setup-eplugin/e-mapi-account-listener.c b/src/account-setup-eplugin/e-mapi-account-listener.c
index f223c54..85ebe54 100644
--- a/src/account-setup-eplugin/e-mapi-account-listener.c
+++ b/src/account-setup-eplugin/e-mapi-account-listener.c
@@ -843,7 +843,7 @@ add_addressbook_sources (EAccount *account, GSList *folders, mapi_id_t trash_fid
 		g_free (uri);
 		e_source_set_property (source, "auth", "plain/password");
 
-		//FIXME: Offline handling
+		/* FIXME: Offline handling */
 		e_source_set_property(source, "user", NULL);
 		e_source_set_property(source, "username", url->user);
 		e_source_set_property(source, "profile", profile);
@@ -852,7 +852,8 @@ add_addressbook_sources (EAccount *account, GSList *folders, mapi_id_t trash_fid
 		SET_KRB_SSO(source, kerberos);
 
 		if (is_new_source) {
-			e_source_set_property(source, "offline_sync", "1");
+			e_source_set_property(source, "offline_sync", "0");
+			e_source_set_property(source, "allow-partial", "true");
 			e_source_set_property (source, "completion", "true");
 		}
 
diff --git a/src/account-setup-eplugin/e-mapi-account-setup.c b/src/account-setup-eplugin/e-mapi-account-setup.c
index d009229..10cd1f4 100644
--- a/src/account-setup-eplugin/e-mapi-account-setup.c
+++ b/src/account-setup-eplugin/e-mapi-account-setup.c
@@ -706,6 +706,10 @@ e_mapi_create (GtkWidget *parent, ESource *source, EMapiFolderType folder_type)
 
 	uri_text = e_source_get_uri (source);
 	if (uri_text && g_ascii_strncasecmp (uri_text, MAPI_URI_PREFIX, MAPI_PREFIX_LENGTH)) {
+		if (uri_text && g_ascii_strncasecmp (uri_text, "mapigal://", strlen ("mapigal://")) == 0) {
+			e_plugin_util_add_check (parent, _("Allow _partial search results"), source, "allow-partial", "true", NULL);
+		}
+
 		return NULL;
 	}
 
diff --git a/src/addressbook/e-book-backend-mapi-contacts.c b/src/addressbook/e-book-backend-mapi-contacts.c
index 5ecb253..0b77f9f 100644
--- a/src/addressbook/e-book-backend-mapi-contacts.c
+++ b/src/addressbook/e-book-backend-mapi-contacts.c
@@ -56,76 +56,6 @@ struct _EBookBackendMAPIContactsPrivate
 	gboolean is_public_folder;
 };
 
-static gboolean
-build_restriction_from_sexp_query (EMapiConnection *conn,
-				   TALLOC_CTX *mem_ctx,
-				   struct mapi_SRestriction **restrictions,
-				   gpointer user_data,
-				   GCancellable *cancellable,
-				   GError **perror)
-{
-	const gchar *sexp_query = user_data;
-
-	g_return_val_if_fail (sexp_query != NULL, FALSE);
-
-	*restrictions = mapi_book_utils_sexp_to_restriction (mem_ctx, sexp_query);
-
-	return TRUE;
-}
-
-static uint32_t
-string_to_bin (TALLOC_CTX *mem_ctx, const gchar *str, uint8_t **lpb)
-{
-	uint32_t len, i;
-
-	g_return_val_if_fail (str != NULL, 0);
-	g_return_val_if_fail (lpb != NULL, 0);
-
-	len = strlen (str);
-	g_return_val_if_fail ((len & 1) == 0, 0);
-
-	len = len / 2;
-	*lpb = talloc_zero_array (mem_ctx, uint8_t, len);
-
-	i = 0;
-	while (*str && i < len) {
-		gchar c1 = str[0], c2 = str[1];
-		str += 2;
-
-		g_return_val_if_fail ((c1 >= '0' && c1 <= '9') || (c1 >= 'a' && c1 <= 'f') || (c1 >= 'A' && c1 <= 'F'), 0);
-		g_return_val_if_fail ((c2 >= '0' && c2 <= '9') || (c2 >= 'a' && c2 <= 'f') || (c2 >= 'A' && c2 <= 'F'), 0);
-
-		#define deHex(x) (((x) >= '0' && (x) <= '9') ? ((x) - '0') : (((x) >= 'a' && (x) <= 'f') ? (x) - 'a' + 10 : (x) - 'A' + 10))
-		(*lpb)[i] = (deHex (c1) << 4) | (deHex (c2));
-		#undef deHex
-		i++;
-	}
-
-	return len;
-}
-
-static gint
-cmp_member_id (gconstpointer a, gconstpointer b, gpointer ht)
-{
-	gchar *va, *vb;
-	gint res;
-
-	if (!a)
-		return b ? -1 : 0;
-	if (!b)
-		return 1;
-
-	va = e_vcard_attribute_get_value ((EVCardAttribute *) a);
-	vb = e_vcard_attribute_get_value ((EVCardAttribute *) b);
-
-	res = GPOINTER_TO_INT (g_hash_table_lookup (ht, va)) - GPOINTER_TO_INT (g_hash_table_lookup (ht, vb));
-
-	g_free (va);
-	g_free (vb);
-
-	return res;
-}
-
 typedef struct {
 	EContact *contact;
 	EBookBackendSqliteDB *db;
@@ -140,17 +70,10 @@ ebbm_contact_to_object (EMapiConnection *conn,
 			GError **perror)
 {
 	EMapiCreateitemData *mcd = user_data;
-	EMapiObject *object;
-
-	#define set_value(hex, val) G_STMT_START { \
-		if (!e_mapi_utils_add_property (&object->properties, hex, val, object)) \
-			return FALSE;	\
-		} G_STMT_END
-
-	#define set_con_value(hex, field_id) G_STMT_START { \
-		if (e_contact_get (mcd->contact, field_id)) { \
-			set_value (hex, e_contact_get (mcd->contact, field_id)); \
-		} } G_STMT_END
+	const gchar *uid = NULL;
+	EContact *old_contact = NULL;
+	gboolean res;
+	GError *error = NULL;
 
 	g_return_val_if_fail (mcd != NULL, FALSE);
 	g_return_val_if_fail (mcd->contact != NULL, FALSE);
@@ -159,228 +82,24 @@ ebbm_contact_to_object (EMapiConnection *conn,
 	g_return_val_if_fail (mem_ctx != NULL, FALSE);
 	g_return_val_if_fail (pobject != NULL, FALSE);
 
-	object = e_mapi_object_new (mem_ctx);
-	*pobject = object;
-
-	if (GPOINTER_TO_INT (e_contact_get (mcd->contact, E_CONTACT_IS_LIST))) {
-		const gchar *uid = NULL;
-		EContact *old_contact = NULL;
-		GList *local, *l;
-		struct BinaryArray_r *members, *oneoff_members;
-		uint32_t u32, crc32 = 0;
-		GHashTable *member_values = NULL, *member_ids = NULL;
-		GError *error = NULL;
-
-		uid = e_contact_get_const (mcd->contact, E_CONTACT_UID);
-		if (uid)
-			old_contact = e_book_backend_sqlitedb_get_contact (mcd->db, EMA_EBB_CACHE_FOLDERID, uid, NULL, NULL, &error);
-
-		if (!error && old_contact) {
-			member_values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-			member_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-
-			local = e_contact_get_attributes (old_contact, E_CONTACT_EMAIL);
-			for (l = local; l; l = l->next) {
-				EVCardAttribute *attr = l->data;
-				GList *param;
-
-				if (!attr)
-					continue;
-
-				param = e_vcard_attribute_get_param (attr, EMA_X_MEMBERVALUE);
-				if (param && param->data && !param->next) {
-					g_hash_table_insert (member_values, e_vcard_attribute_get_value (attr), g_strdup (param->data));
-				}
-
-				param = e_vcard_attribute_get_param (attr, EMA_X_MEMBERID);
-				if (param && param->data && !param->next) {
-					g_hash_table_insert (member_ids, e_vcard_attribute_get_value (attr), GINT_TO_POINTER (atoi (param->data)));
-				}
-			}
-
-			g_object_unref (old_contact);
-			g_list_foreach (local, (GFunc)e_vcard_attribute_free, NULL);
-			g_list_free (local);
-		}
-
-		if (error)
-			g_error_free (error);
-
-		set_value (PidTagMessageClass, IPM_DISTLIST);
-		u32 = 0xFFFFFFFF;
-		set_value (PidLidFileUnderId, &u32);
-		set_con_value (PidLidFileUnder, E_CONTACT_FILE_AS);
-		set_con_value (PidLidDistributionListName, E_CONTACT_FILE_AS);
-		set_con_value (PidTagDisplayName, E_CONTACT_FILE_AS);
-		set_con_value (PidTagNormalizedSubject, E_CONTACT_FILE_AS);
-
-		local = e_contact_get_attributes (mcd->contact, E_CONTACT_EMAIL);
-		if (member_ids)
-			local = g_list_sort_with_data (local, cmp_member_id, member_ids);
-
-		members = talloc_zero (mem_ctx, struct BinaryArray_r);
-		members->cValues = 0;
-		members->lpbin = talloc_zero_array (mem_ctx, struct Binary_r, g_list_length (local));
-
-		oneoff_members = talloc_zero (mem_ctx, struct BinaryArray_r);
-		oneoff_members->cValues = 0;
-		oneoff_members->lpbin = talloc_zero_array (mem_ctx, struct Binary_r, g_list_length (local));
-
-		for (l = local; l; l = l->next) {
-			EVCardAttribute *attr = (EVCardAttribute *) l->data;
-			gchar *raw;
-			CamelInternetAddress *addr;
-
-			if (!attr)
-				continue;
-
-			raw = e_vcard_attribute_get_value (attr);
-			if (!raw)
-				continue;
-
-			addr = camel_internet_address_new ();
-			if (camel_address_decode (CAMEL_ADDRESS (addr), raw) > 0) {
-				const gchar *nm = NULL, *eml = NULL;
-
-				camel_internet_address_get (addr, 0, &nm, &eml);
-				if (eml) {
-					/* keep both lists in sync */
-					if (member_values && g_hash_table_lookup (member_values, raw)) {
-						/* stored ListMembers values when contact's value didn't change */
-						members->lpbin[members->cValues].cb = string_to_bin (mem_ctx, g_hash_table_lookup (member_values, raw), &members->lpbin[members->cValues].lpb);
-						members->cValues++;
-					} else {
-						e_mapi_util_recip_entryid_generate_smtp (mem_ctx, &members->lpbin[members->cValues], nm ? nm : "", eml);
-						members->cValues++;
-					}
-
-					e_mapi_util_recip_entryid_generate_smtp (mem_ctx, &oneoff_members->lpbin[oneoff_members->cValues], nm ? nm : "", eml);
-					oneoff_members->cValues++;
-
-					crc32 = e_mapi_utils_push_crc32 (crc32, members->lpbin[members->cValues - 1].lpb, members->lpbin[members->cValues - 1].cb);
-				}
-			}
-
-			g_object_unref (addr);
-			g_free (raw);
-		}
-
-		if (member_values)
-			g_hash_table_destroy (member_values);
-		if (member_ids)
-			g_hash_table_destroy (member_ids);
-		g_list_foreach (local, (GFunc)e_vcard_attribute_free, NULL);
-		g_list_free (local);
-
-		set_value (PidLidDistributionListOneOffMembers, oneoff_members);
-		set_value (PidLidDistributionListMembers, members);
-		set_value (PidLidDistributionListChecksum, &crc32);
-
-		return TRUE;
-	}
-
-	set_value (PidTagMessageClass, IPM_CONTACT);
-	set_con_value (PidLidFileUnder, E_CONTACT_FILE_AS);
-
-	set_con_value (PidTagDisplayName, E_CONTACT_FULL_NAME);
-	set_con_value (PidTagNormalizedSubject, E_CONTACT_FILE_AS);
-	set_con_value (PidLidEmail1OriginalDisplayName, E_CONTACT_EMAIL_1);
-	/*set_con_value (PidLidEmail1EmailAddress, E_CONTACT_EMAIL_1);*/
-
-	/*set_con_value (0x8083001e, E_CONTACT_EMAIL_1);*/
-	set_con_value (PidLidEmail2EmailAddress, E_CONTACT_EMAIL_2);
-
-	set_con_value (PidLidEmail3EmailAddress, E_CONTACT_EMAIL_3);
-	/*set_con_value (PidLidEmail3OriginalDisplayName, E_CONTACT_EMAIL_3);*/
-
-	set_con_value (PidLidHtml, E_CONTACT_HOMEPAGE_URL);
-	set_con_value (PidLidFreeBusyLocation, E_CONTACT_FREEBUSY_URL);
-
-	set_con_value (PidTagBusinessTelephoneNumber, E_CONTACT_PHONE_BUSINESS);
-	set_con_value (PidTagHomeTelephoneNumber, E_CONTACT_PHONE_HOME);
-	set_con_value (PidTagMobileTelephoneNumber, E_CONTACT_PHONE_MOBILE);
-	set_con_value (PidTagHomeFaxNumber, E_CONTACT_PHONE_HOME_FAX);
-	set_con_value (PidTagBusinessFaxNumber, E_CONTACT_PHONE_BUSINESS_FAX);
-	set_con_value (PidTagPagerTelephoneNumber, E_CONTACT_PHONE_PAGER);
-	set_con_value (PidTagAssistantTelephoneNumber, E_CONTACT_PHONE_ASSISTANT);
-	set_con_value (PidTagCompanyMainTelephoneNumber, E_CONTACT_PHONE_COMPANY);
-
-	set_con_value (PidTagManagerName, E_CONTACT_MANAGER);
-	set_con_value (PidTagAssistant, E_CONTACT_ASSISTANT);
-	set_con_value (PidTagCompanyName, E_CONTACT_ORG);
-	set_con_value (PidTagDepartmentName, E_CONTACT_ORG_UNIT);
-	set_con_value (PidTagProfession, E_CONTACT_ROLE);
-	set_con_value (PidTagTitle, E_CONTACT_TITLE);
-
-	set_con_value (PidTagOfficeLocation, E_CONTACT_OFFICE);
-	set_con_value (PidTagSpouseName, E_CONTACT_SPOUSE);
+	uid = e_contact_get_const (mcd->contact, E_CONTACT_UID);
+	if (uid)
+		old_contact = e_book_backend_sqlitedb_get_contact (mcd->db, EMA_EBB_CACHE_FOLDERID, uid, NULL, NULL, &error);
 
-	set_con_value (PidTagBody, E_CONTACT_NOTE);
-	set_con_value (PidTagNickname, E_CONTACT_NICKNAME);
-
-	/* BDAY AND ANNV */
-	if (e_contact_get (mcd->contact, E_CONTACT_BIRTH_DATE)) {
-		EContactDate *date = e_contact_get (mcd->contact, E_CONTACT_BIRTH_DATE);
-		struct tm tmtime = { 0 };
-		struct FILETIME t;
-
-		tmtime.tm_mday = date->day;
-		tmtime.tm_mon = date->month - 1;
-		tmtime.tm_year = date->year - 1900;
-
-		e_mapi_util_time_t_to_filetime (mktime (&tmtime) + (24 * 60 * 60), &t);
-
-		set_value (PidTagBirthday, &t);
-	}
-
-	if (e_contact_get (mcd->contact, E_CONTACT_ANNIVERSARY)) {
-		EContactDate *date = e_contact_get (mcd->contact, E_CONTACT_ANNIVERSARY);
-		struct tm tmtime = { 0 };
-		struct FILETIME t;
-
-		tmtime.tm_mday = date->day;
-		tmtime.tm_mon = date->month - 1;
-		tmtime.tm_year = date->year - 1900;
-
-		e_mapi_util_time_t_to_filetime (mktime (&tmtime) + (24 * 60 * 60), &t);
-
-		set_value (PidTagWeddingAnniversary, &t);
+	if (error) {
+		old_contact = NULL;
+		g_clear_error (&error);
 	}
 
-	/* Home and Office address */
-	if (e_contact_get (mcd->contact, E_CONTACT_ADDRESS_HOME)) {
-		EContactAddress *contact_addr = e_contact_get (mcd->contact, E_CONTACT_ADDRESS_HOME);
+	res = e_mapi_book_utils_contact_to_object (mcd->contact, old_contact, pobject, mem_ctx, cancellable, perror);
 
-		set_value (PidLidHomeAddress, contact_addr->street);
-		set_value (PidTagHomeAddressPostOfficeBox, contact_addr->ext);
-		set_value (PidTagHomeAddressCity, contact_addr->locality);
-		set_value (PidTagHomeAddressStateOrProvince, contact_addr->region);
-		set_value (PidTagHomeAddressPostalCode, contact_addr->code);
-		set_value (PidTagHomeAddressCountry, contact_addr->country);
-	}
-
-	if (e_contact_get (mcd->contact, E_CONTACT_ADDRESS_WORK)) {
-		EContactAddress *contact_addr = e_contact_get (mcd->contact, E_CONTACT_ADDRESS_WORK);
+	if (old_contact)
+		g_object_unref (old_contact);
 
-		set_value (PidLidWorkAddress, contact_addr->street);
-		set_value (PidTagPostOfficeBox, contact_addr->ext);
-		set_value (PidTagLocality, contact_addr->locality);
-		set_value (PidTagStateOrProvince, contact_addr->region);
-		set_value (PidTagPostalCode, contact_addr->code);
-		set_value (PidTagCountry, contact_addr->country);
-	}
-
-	if (e_contact_get (mcd->contact, E_CONTACT_IM_AIM)) {
-		GList *l = e_contact_get (mcd->contact, E_CONTACT_IM_AIM);
-		set_value (PidLidInstantMessagingAddress, l->data);
-	}
-
-	#undef set_value
-
-	return TRUE;
+	return res;
 }
 
-struct FetchContactItemData
+struct TransferContactData
 {
 	EBookBackendMAPI *ebma;
 	EContact *contact; /* out */
@@ -396,34 +115,15 @@ transfer_contact_cb (EMapiConnection *conn,
 		     GCancellable *cancellable,
 		     GError **perror)
 {
-	struct FetchContactItemData *fcid = user_data;
+	struct TransferContactData *tc = user_data;
 
-	g_return_val_if_fail (fcid != NULL, FALSE);
-	g_return_val_if_fail (fcid->ebma != NULL, FALSE);
+	g_return_val_if_fail (tc != NULL, FALSE);
+	g_return_val_if_fail (tc->ebma != NULL, FALSE);
 	g_return_val_if_fail (object != NULL, FALSE);
 
-	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) {
-		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);
-
-			if (!e_book_backend_mapi_notify_contact_update (fcid->ebma, NULL, fcid->contact, obj_index, obj_total, NULL)) {
-				g_free (suid);
-				return FALSE;
-			}
-
-			g_free (suid);
-		} else {
-			g_debug ("%s: No PidTagMid found", G_STRFUNC);
-		}
-	}
+	tc->contact = e_mapi_book_utils_contact_from_object (conn, object, e_book_backend_mapi_get_book_uri (tc->ebma));
+	if (tc->contact)
+		return e_book_backend_mapi_notify_contact_update (tc->ebma, NULL, tc->contact, obj_index, obj_total, NULL);
 
 	return TRUE;
 }
@@ -477,29 +177,17 @@ transfer_contacts_cb (EMapiConnection *conn,
 	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);
+	contact = e_mapi_book_utils_contact_from_object (conn, object, e_book_backend_mapi_get_book_uri (tcd->ebma));
 	if (contact) {
-		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);
-
-			if (tcd->cards)
-				*tcd->cards = g_slist_prepend (*tcd->cards, e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30));
-
-			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 (tcd->cards)
+			*tcd->cards = g_slist_prepend (*tcd->cards, e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30));
 
+		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);
-		} else {
-			g_debug ("%s: No PidTagMid found", G_STRFUNC);
+			return FALSE;
 		}
+
+		g_object_unref (contact);
 	} else {
 		g_debug ("%s: [%d/%d] Failed to transform to contact", G_STRFUNC, obj_index, obj_total);
 	}
@@ -525,7 +213,7 @@ gather_known_uids_cb (EMapiConnection *conn,
 
 	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));
+		g_hash_table_insert (lku->uid_to_rev, suid, e_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;
 	}
@@ -597,7 +285,8 @@ ebbmc_server_notification_cb (EMapiConnection *conn,
 	}
 
 	priv = ((EBookBackendMAPIContacts *) ebma)->priv;
-	if (priv->fid == update_folder1 || priv->fid == update_folder2)
+	if ((priv->fid == update_folder1 || priv->fid == update_folder2) &&
+	    e_book_backend_mapi_is_marked_for_offline (ebma))
 		e_book_backend_mapi_refresh_cache (ebma);
 }
 
@@ -947,7 +636,7 @@ ebbm_contacts_get_contact (EBookBackendMAPI *ebma, GCancellable *cancellable, co
 	EMapiConnection *conn;
 	mapi_id_t mid;
 	mapi_object_t obj_folder;
-	struct FetchContactItemData fcid = { 0 };
+	struct TransferContactData tc = { 0 };
 	gboolean status;
 	GError *mapi_error = NULL;
 
@@ -996,17 +685,17 @@ ebbm_contacts_get_contact (EBookBackendMAPI *ebma, GCancellable *cancellable, co
 	}
 
 	if (status) {
-		fcid.ebma = ebma;
-		fcid.contact = NULL;
+		tc.ebma = ebma;
+		tc.contact = NULL;
 
-		e_mapi_connection_transfer_object (conn, &obj_folder, mid, transfer_contact_cb, &fcid, cancellable, &mapi_error);
+		e_mapi_connection_transfer_object (conn, &obj_folder, mid, transfer_contact_cb, &tc, 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);
-		g_object_unref (fcid.contact);
+	if (tc.contact) {
+		*vcard =  e_vcard_to_string (E_VCARD (tc.contact), EVC_FORMAT_VCARD_30);
+		g_object_unref (tc.contact);
 	} else {
 		if (!mapi_error || mapi_error->code == MAPI_E_NOT_FOUND) {
 			g_propagate_error (error, EDB_ERROR (CONTACT_NOT_FOUND));
@@ -1076,7 +765,7 @@ ebbm_contacts_get_contact_list (EBookBackendMAPI *ebma, GCancellable *cancellabl
 
 	if (status) {
 		status = e_mapi_connection_list_objects (conn, &obj_folder,
-							 build_restriction_from_sexp_query, (gpointer) query,
+							 e_mapi_book_utils_build_sexp_restriction, (gpointer) query,
 							 gather_contact_mids_cb, &mids,
 							 cancellable, &mapi_error);
 
diff --git a/src/addressbook/e-book-backend-mapi-gal.c b/src/addressbook/e-book-backend-mapi-gal.c
index da4ac39..1b1990d 100644
--- a/src/addressbook/e-book-backend-mapi-gal.c
+++ b/src/addressbook/e-book-backend-mapi-gal.c
@@ -1,3 +1,26 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *    Bharath Acharya <abharath novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
@@ -21,6 +44,9 @@
 
 #include "e-book-backend-mapi-gal.h"
 
+/* default value for "partial-count", upper bound of objects to download during partial search */
+#define DEFAULT_PARTIAL_COUNT 50
+
 G_DEFINE_TYPE (EBookBackendMAPIGAL, e_book_backend_mapi_gal, E_TYPE_BOOK_BACKEND_MAPI)
 
 struct _EBookBackendMAPIGALPrivate
@@ -31,68 +57,37 @@ struct _EBookBackendMAPIGALPrivate
 	gint32 unused;
 };
 
-static gchar *
-get_uid_from_row (struct SRow *aRow, uint32_t row_index)
-{
-	gchar *suid = NULL;
-	const gchar *str;
-
-	g_return_val_if_fail (aRow != NULL, NULL);
-
-	str = e_mapi_util_find_row_propval (aRow, PR_EMAIL_ADDRESS_UNICODE);
-	if (str && *str)
-		suid = g_strdup (str);
-
-	if (!suid) {
-		const mapi_id_t *midptr;
-
-		midptr = e_mapi_util_find_row_propval (aRow, PR_MID);
-
-		suid = e_mapi_util_mapi_id_to_string (midptr ? *midptr : row_index);
-	}
-
-	return suid;
-}
-
-struct FetchGalData
+struct TransferGalData
 {
 	EBookBackendMAPI *ebma;
 	EDataBookView *book_view;
 	gpointer notify_contact_data;
-	mapi_id_t fid; /* folder ID of contacts, for named IDs */
 };
 
 static gboolean
-fetch_gal_cb (EMapiConnection *conn,
-	      uint32_t row_index,
-	      uint32_t n_rows,
-	      struct SRow *aRow,
-	      gpointer data,
-	      GCancellable *cancellable,
-	      GError **perror)
+transfer_gal_cb (EMapiConnection *conn,
+		 TALLOC_CTX *mem_ctx,
+		 /* const */ EMapiObject *object,
+		 guint32 obj_index,
+		 guint32 obj_total,
+		 gpointer user_data,
+		 GCancellable *cancellable,
+		 GError **perror)
 {
-	struct FetchGalData *fgd = data;
+	struct TransferGalData *tg = user_data;
 	EContact *contact;
 
 	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 (object != NULL, FALSE);
+	g_return_val_if_fail (tg != NULL, FALSE);
 
-	contact = mapi_book_utils_contact_from_props (conn, fgd->fid, e_book_backend_mapi_get_book_uri (fgd->ebma), NULL, aRow);
+	contact = e_mapi_book_utils_contact_from_object (conn, object, e_book_backend_mapi_get_book_uri (tg->ebma));
 	if (!contact) {
-		/* just ignore them */
+		/* this is GAL, just ignore them */
 		return TRUE;
 	}
 
-	if (!e_contact_get_const (contact, E_CONTACT_UID)) {
-		gchar *suid;
-
-		suid = get_uid_from_row (aRow, row_index);
-		e_contact_set (contact, E_CONTACT_UID, suid);
-		g_free (suid);
-	}
-
-	if (!e_book_backend_mapi_notify_contact_update (fgd->ebma, fgd->book_view, contact, row_index, n_rows, fgd->notify_contact_data)) {
+	if (!e_book_backend_mapi_notify_contact_update (tg->ebma, tg->book_view, contact, obj_index, obj_total, tg->notify_contact_data)) {
 		g_object_unref (contact);
 		return FALSE;
 	}
@@ -104,9 +99,10 @@ fetch_gal_cb (EMapiConnection *conn,
 
 static gboolean
 list_gal_uids_cb (EMapiConnection *conn,
-		  uint32_t row_index,
-		  uint32_t n_rows,
-		  struct SRow *aRow,
+		  TALLOC_CTX *mem_ctx,
+		  const ListObjectsData *object_data,
+		  guint32 obj_index,
+		  guint32 obj_total,
 		  gpointer user_data,
 		  GCancellable *cancellable,
 		  GError **perror)
@@ -115,24 +111,18 @@ list_gal_uids_cb (EMapiConnection *conn,
 	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 (object_data != NULL, FALSE);
 	g_return_val_if_fail (lku != NULL, FALSE);
 
-	uid = get_uid_from_row (aRow, row_index);
+	uid = e_mapi_util_mapi_id_to_string (object_data->mid);
 	if (uid) {
-		const struct FILETIME *ft;
-		time_t tt;
+		if (lku->latest_last_modify < object_data->last_modified)
+			lku->latest_last_modify = object_data->last_modified;
 
-		ft = e_mapi_util_find_row_propval (aRow, PidTagLastModificationTime);
-		tt = ft ? e_mapi_util_filetime_to_time_t (ft) : 1;
-
-		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));
+		g_hash_table_insert (lku->uid_to_rev, uid, e_mapi_book_utils_timet_to_string (object_data->last_modified));
 	}
 
-	return !g_cancellable_is_cancelled (cancellable);
+	return TRUE;
 }
 
 static void
@@ -180,10 +170,13 @@ ebbm_gal_transfer_contacts (EBookBackendMAPI *ebma,
 			    GError **error)
 {
 	GError *mapi_error = NULL;
-	struct FetchGalData fgd = { 0 };
+	struct TransferGalData tg = { 0 };
 	EMapiConnection *conn;
-	gchar *last_fetch;
-	gboolean fetch_successful;
+	ESource *source;
+	GSList *get_mids = NULL;
+	const GSList *iter;
+	gint partial_count = -1;
+	gboolean status;
 
 	e_book_backend_mapi_lock_connection (ebma);
 
@@ -194,50 +187,47 @@ ebbm_gal_transfer_contacts (EBookBackendMAPI *ebma,
 		return;
 	}
 
-	/* GAL doesn't use restrictions yet, thus just fetches all items always */
-	last_fetch = e_book_backend_mapi_cache_get (ebma, "gal-last-update");
-	if (last_fetch) {
-		GTimeVal last_tv = { 0 }, now = { 0 };
+	source = e_backend_get_source (E_BACKEND (ebma));
+	if (source && g_strcmp0 (e_source_get_property (source, "allow-partial"), "true") == 0) {
+		const gchar *partial_count_str = e_source_get_property (source, "partial-count");
 
-		g_get_current_time (&now);
+		partial_count = 0;
+		if (partial_count_str)
+			partial_count = atoi (partial_count_str);
 
-		/* refetch gal only once per week */
-		if (g_time_val_from_iso8601 (last_fetch, &last_tv) && now.tv_sec - last_tv.tv_sec <= 60 * 60 * 24 * 7) {
-			g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
-			g_free (last_fetch);
-			e_book_backend_mapi_unlock_connection (ebma);
-			return;
-		}
+		if (partial_count <= 0)
+			partial_count = DEFAULT_PARTIAL_COUNT;
+	}
 
-		g_free (last_fetch);
+	for (iter = uids; iter && (partial_count == -1 || partial_count > 0); iter = iter->next) {
+		mapi_id_t *pmid, mid;
+
+		if (e_mapi_util_mapi_id_from_string  (iter->data, &mid)) {
+			pmid = g_new0 (mapi_id_t, 1);
+			*pmid = mid;
+
+			get_mids = g_slist_prepend (get_mids, pmid);
+
+			if (partial_count > 0)
+				partial_count--;
+		}
 	}
 
-	fgd.ebma = ebma;
-	fgd.book_view = book_view;
-	fgd.notify_contact_data = notify_contact_data;
-	fgd.fid = e_mapi_connection_get_default_folder_id (conn, olFolderContacts, NULL, NULL);
+	tg.ebma = ebma;
+	tg.book_view = book_view;
+	tg.notify_contact_data = notify_contact_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);
+	status = e_mapi_connection_transfer_gal_objects	(conn, get_mids, transfer_gal_cb, &tg, 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"));
 		g_error_free (mapi_error);
-	} else if (!fetch_successful) {
+	} else if (!status) {
 		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
-	} else {
-		GTimeVal now = { 0 };
-
-		g_get_current_time (&now);
-
-		last_fetch = g_time_val_to_iso8601 (&now);
-		if (last_fetch && *last_fetch)
-			e_book_backend_mapi_cache_set (ebma, "gal-last-update", last_fetch);
-
-		g_free (last_fetch);
 	}
 
+	g_slist_free_full (get_mids, g_free);
+
 	e_book_backend_mapi_unlock_connection (ebma);
 }
 
@@ -247,11 +237,24 @@ ebbm_gal_get_contacts_count (EBookBackendMAPI *ebma,
 			     GCancellable *cancellable,
 			     GError **error)
 {
+	EMapiConnection *conn;
+
 	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;
+	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;
+	}
+
+	if (!e_mapi_connection_count_gal_objects (conn, obj_total, cancellable, error))
+		*obj_total = -1;
+
+	e_book_backend_mapi_unlock_connection (ebma);
 }
 
 static void
@@ -278,9 +281,7 @@ ebbm_gal_list_known_uids (EBookBackendMAPI *ebma,
 		return;
 	}
 
-	e_mapi_connection_fetch_gal (conn, NULL, NULL,
-		mapi_book_utils_get_prop_list, GET_UIDS_ONLY,
-		list_gal_uids_cb, lku, cancellable, &mapi_error);
+	e_mapi_connection_list_gal_objects (conn, build_rs_cb, build_rs_cb_data, 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"));
diff --git a/src/addressbook/e-book-backend-mapi.c b/src/addressbook/e-book-backend-mapi.c
index a63fbe8..08bdc50 100644
--- a/src/addressbook/e-book-backend-mapi.c
+++ b/src/addressbook/e-book-backend-mapi.c
@@ -34,7 +34,6 @@
 
 #include <libebook/e-contact.h>
 #include <libedataserver/e-data-server-util.h>
-#include <libedataserver/e-sexp.h>
 #include <camel/camel.h>
 
 #include <e-mapi-operation-queue.h>
@@ -61,73 +60,14 @@ struct _EBookBackendMAPIPrivate
 	time_t last_update_cache;
 
 	EBookBackendSqliteDB *db;
-	GHashTable *running_book_views;
 
 	guint32 last_server_contact_count;
 	time_t last_modify_time;
 	gboolean server_dirty;
-};
-
-#define ELEMENT_TYPE_MASK   0xF /* mask where the real type of the element is stored */
-
-#define ELEMENT_TYPE_SIMPLE 0x01
-#define ELEMENT_TYPE_COMPLEX 0x02
-
-#define ELEMENT_TYPE_NAMEDID 0x10
-
-static const struct field_element_mapping {
-		EContactField field_id;
-		uint32_t element_type;
-		uint32_t mapi_id;
-		gint contact_type;
-	} mappings [] = {
-
-	{ E_CONTACT_UID, PT_UNICODE, PR_EMAIL_ADDRESS_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_REV, PT_SYSTIME, PR_LAST_MODIFICATION_TIME, ELEMENT_TYPE_SIMPLE},
-
-	{ E_CONTACT_FILE_AS, PT_UNICODE, PidLidFileUnder, ELEMENT_TYPE_SIMPLE | ELEMENT_TYPE_NAMEDID},
-	{ E_CONTACT_FULL_NAME, PT_UNICODE, PR_DISPLAY_NAME_UNICODE, ELEMENT_TYPE_SIMPLE },
-	{ E_CONTACT_GIVEN_NAME, PT_UNICODE, PR_GIVEN_NAME_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_FAMILY_NAME, PT_UNICODE, PR_SURNAME_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_NICKNAME, PT_UNICODE, PR_NICKNAME_UNICODE, ELEMENT_TYPE_SIMPLE },
-
-	{ E_CONTACT_EMAIL_1, PT_UNICODE, PidLidEmail1OriginalDisplayName, ELEMENT_TYPE_SIMPLE | ELEMENT_TYPE_NAMEDID},
-	{ E_CONTACT_EMAIL_2, PT_UNICODE, PidLidEmail2EmailAddress, ELEMENT_TYPE_SIMPLE | ELEMENT_TYPE_NAMEDID},
-	{ E_CONTACT_EMAIL_3, PT_UNICODE, PidLidEmail3EmailAddress, ELEMENT_TYPE_SIMPLE | ELEMENT_TYPE_NAMEDID},
-	{ E_CONTACT_IM_AIM,  PT_UNICODE, PidLidInstantMessagingAddress, ELEMENT_TYPE_COMPLEX | ELEMENT_TYPE_NAMEDID},
 
-	{ E_CONTACT_PHONE_BUSINESS, PT_UNICODE, PR_OFFICE_TELEPHONE_NUMBER_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_PHONE_HOME, PT_UNICODE, PR_HOME_TELEPHONE_NUMBER_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_PHONE_MOBILE, PT_UNICODE, PR_MOBILE_TELEPHONE_NUMBER_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_PHONE_HOME_FAX, PT_UNICODE, PR_HOME_FAX_NUMBER_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_PHONE_BUSINESS_FAX, PT_UNICODE, PR_BUSINESS_FAX_NUMBER_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_PHONE_PAGER, PT_UNICODE, PR_PAGER_TELEPHONE_NUMBER_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_PHONE_ASSISTANT, PT_UNICODE, PR_ASSISTANT_TELEPHONE_NUMBER_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_PHONE_COMPANY, PT_UNICODE, PR_COMPANY_MAIN_PHONE_NUMBER_UNICODE, ELEMENT_TYPE_SIMPLE},
-
-	{ E_CONTACT_HOMEPAGE_URL, PT_UNICODE, PidLidHtml, ELEMENT_TYPE_SIMPLE | ELEMENT_TYPE_NAMEDID},
-	{ E_CONTACT_FREEBUSY_URL, PT_UNICODE, PidLidFreeBusyLocation, ELEMENT_TYPE_SIMPLE | ELEMENT_TYPE_NAMEDID},
-
-	{ E_CONTACT_ROLE, PT_UNICODE, PR_PROFESSION_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_TITLE, PT_UNICODE, PR_TITLE_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_ORG, PT_UNICODE, PR_COMPANY_NAME_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_ORG_UNIT, PT_UNICODE, PR_DEPARTMENT_NAME_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_MANAGER, PT_UNICODE, PR_MANAGER_NAME_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_ASSISTANT, PT_UNICODE, PR_ASSISTANT_UNICODE, ELEMENT_TYPE_SIMPLE},
-
-	{ E_CONTACT_OFFICE, PT_UNICODE, PR_OFFICE_LOCATION_UNICODE, ELEMENT_TYPE_SIMPLE},
-	{ E_CONTACT_SPOUSE, PT_UNICODE, PR_SPOUSE_NAME_UNICODE, ELEMENT_TYPE_SIMPLE},
-
-	{ E_CONTACT_BIRTH_DATE,  PT_SYSTIME, PR_BIRTHDAY, ELEMENT_TYPE_COMPLEX},
-	{ E_CONTACT_ANNIVERSARY, PT_SYSTIME, PR_WEDDING_ANNIVERSARY, ELEMENT_TYPE_COMPLEX},
-
-	{ E_CONTACT_NOTE, PT_UNICODE, PR_BODY_UNICODE, ELEMENT_TYPE_SIMPLE},
-
-	{ E_CONTACT_ADDRESS_HOME, PT_UNICODE, PidLidHomeAddress, ELEMENT_TYPE_COMPLEX | ELEMENT_TYPE_NAMEDID},
-	{ E_CONTACT_ADDRESS_WORK, PT_UNICODE, PidLidOtherAddress, ELEMENT_TYPE_COMPLEX | ELEMENT_TYPE_NAMEDID}
-	/* { E_CONTACT_BOOK_URI, ELEMENT_TYPE_SIMPLE, "book_uri"}, */
-	/* { E_CONTACT_CATEGORIES, } */
-	};
+	GHashTable *running_views; /* EDataBookView => GCancellable */
+	GMutex *running_views_lock;
+};
 
 static gboolean
 pick_view_cb (EDataBookView *view, gpointer user_data)
@@ -236,7 +176,6 @@ ebbm_update_cache_cb (gpointer data)
 	ebmac = E_BOOK_BACKEND_MAPI_GET_CLASS (ebma);
 	g_return_val_if_fail (ebmac != NULL, NULL);
 
-
 	cancellable = priv->update_cache;
 	g_cancellable_reset (cancellable);
 
@@ -414,7 +353,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 */) {
+		if (!g_cancellable_is_cancelled (cancellable) && priv->marked_for_offline) {
 			ebbm_maybe_invoke_cache_update (ebma);
 		}
 	}
@@ -535,18 +474,14 @@ ebbm_get_backend_property (EBookBackendMAPI *ebma, const gchar *prop_name, gchar
 	g_return_val_if_fail (prop_value != NULL, FALSE);
 
 	if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
-		*prop_value = g_strdup ("net,bulk-removes,do-initial-query,contact-lists");
+		if (e_book_backend_mapi_is_marked_for_offline (ebma))
+			*prop_value = g_strdup ("net,bulk-removes,contact-lists,do-initial-query");
+		else
+			*prop_value = g_strdup ("net,bulk-removes,contact-lists");
 	} else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
 		*prop_value = g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
 	} else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
-		gint ii;
-		GSList *fields = NULL;
-
-		for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) {
-			fields = g_slist_append (fields, (gpointer) e_contact_field_name (mappings[ii].field_id));
-		}
-
-		fields = g_slist_append (fields, (gpointer) e_contact_field_name (E_CONTACT_BOOK_URI));
+		GSList *fields = e_mapi_book_utils_get_supported_contact_fields ();
 
 		*prop_value = e_data_book_string_slist_to_comma_string (fields);
 
@@ -686,6 +621,7 @@ struct BookViewThreadData
 {
 	EBookBackendMAPI *ebma;
 	EDataBookView *book_view;
+	GCancellable *cancellable;
 };
 
 static gpointer
@@ -693,18 +629,20 @@ ebbm_book_view_thread (gpointer data)
 {
 	struct BookViewThreadData *bvtd = data;
 	EBookBackendMAPIPrivate *priv;
+	EBookBackendMAPIClass *ebmac;
 	GError *error = NULL;
 
 	g_return_val_if_fail (bvtd != NULL, NULL);
 	g_return_val_if_fail (bvtd->ebma != NULL, NULL);
 	g_return_val_if_fail (bvtd->book_view != NULL, NULL);
 
+	ebmac = E_BOOK_BACKEND_MAPI_GET_CLASS (bvtd->ebma);
+	g_return_val_if_fail (ebmac != NULL, NULL);
+
 	priv = bvtd->ebma->priv;
 
 	e_data_book_view_notify_progress (bvtd->book_view, -1, _("Searching"));
 
-	e_book_backend_mapi_update_view_by_cache (bvtd->ebma, bvtd->book_view, &error);
-
 	if (!error && priv && priv->conn && (!priv->update_cache_thread || g_cancellable_is_cancelled (priv->update_cache))
 	    && e_book_backend_mapi_book_view_is_running (bvtd->ebma, bvtd->book_view)) {
 		EBookBackendMAPIClass *ebmac;
@@ -713,7 +651,61 @@ ebbm_book_view_thread (gpointer data)
 		if (ebmac && ebmac->op_book_view_thread)
 			ebmac->op_book_view_thread (bvtd->ebma, bvtd->book_view, priv->update_cache, &error);
 
-		ebbm_maybe_invoke_cache_update (bvtd->ebma);
+		if (priv->marked_for_offline) {
+			e_book_backend_mapi_update_view_by_cache (bvtd->ebma, bvtd->book_view, &error);
+
+			ebbm_maybe_invoke_cache_update (bvtd->ebma);
+
+			e_book_backend_mapi_update_view_by_cache (bvtd->ebma, bvtd->book_view, &error);
+		} else if (ebmac->op_list_known_uids && ebmac->op_transfer_contacts) {
+			const gchar *sexp = e_data_book_view_get_card_query (bvtd->book_view);
+
+			/* search only if not searching for everything */
+			if (sexp && *sexp && g_ascii_strcasecmp (sexp, "(contains \"x-evolution-any-field\" \"\")") != 0) {
+				struct ListKnownUidsData lku = { 0 };
+				GHashTable *local_known_uids, *server_known_uids;
+
+				e_book_backend_mapi_update_view_by_cache (bvtd->ebma, bvtd->book_view, &error);
+
+				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);
+
+				lku.uid_to_rev = server_known_uids;
+				lku.latest_last_modify = 0;
+
+				ebmac->op_list_known_uids (bvtd->ebma, e_mapi_book_utils_build_sexp_restriction, (gpointer) sexp, &lku, bvtd->cancellable, &error);
+
+				if (!g_cancellable_is_cancelled (bvtd->cancellable)) {
+					GSList *uids = NULL;
+					GHashTableIter iter;
+					gpointer key, value;
+
+					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);
+						}
+
+						g_hash_table_remove (local_known_uids, uid);
+					}
+
+					if (uids) {
+						ebbm_transfer_contacts (bvtd->ebma, uids, NULL, bvtd->cancellable, &error);
+						e_book_backend_mapi_update_view_by_cache (bvtd->ebma, bvtd->book_view, &error);
+					}
+
+					/* has borrowed data from server_known_uids */
+					g_slist_free (uids);
+				}
+
+				g_hash_table_destroy (server_known_uids);
+				if (local_known_uids)
+					g_hash_table_destroy (local_known_uids);
+			}
+		}
 	}
 
 	if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
@@ -727,6 +719,8 @@ ebbm_book_view_thread (gpointer data)
 	if (error)
 		g_error_free (error);
 
+	if (bvtd->cancellable)
+		g_object_unref (bvtd->cancellable);
 	g_object_unref (bvtd->book_view);
 	/* May unref it out of the thread, in case it's the last reference to it */
 	g_idle_add (unref_backend_idle_cb, bvtd->ebma);
@@ -960,8 +954,16 @@ ebbm_operation_cb (OperationBase *op, gboolean cancelled, EBookBackend *backend)
 			GError *err = NULL;
 			struct BookViewThreadData *bvtd = g_new0 (struct BookViewThreadData, 1);
 
+			g_mutex_lock (ebma->priv->running_views_lock);
+
 			bvtd->ebma = g_object_ref (ebma);
 			bvtd->book_view = g_object_ref (opbv->book_view);
+			bvtd->cancellable = g_hash_table_lookup (ebma->priv->running_views, bvtd->book_view);
+
+			if (bvtd->cancellable)
+				g_object_ref (bvtd->cancellable);
+
+			g_mutex_unlock (ebma->priv->running_views_lock);
 
 			g_thread_create (ebbm_book_view_thread, bvtd, FALSE, &err);
 
@@ -1200,7 +1202,9 @@ ebbm_op_start_book_view (EBookBackend *backend, EDataBookView *book_view)
 	op->base.opid = 0;
 	op->book_view = g_object_ref (book_view);
 
-	g_hash_table_insert (priv->running_book_views, book_view, GINT_TO_POINTER(1));
+	g_mutex_lock (priv->running_views_lock);
+	g_hash_table_insert (priv->running_views, book_view, g_cancellable_new ());
+	g_mutex_unlock (priv->running_views_lock);
 
 	e_mapi_operation_queue_push (priv->op_queue, op);
 }
@@ -1211,6 +1215,7 @@ ebbm_op_stop_book_view (EBookBackend *backend, EDataBookView *book_view)
 	OperationBookView *op;
 	EBookBackendMAPI *ebbm;
 	EBookBackendMAPIPrivate *priv;
+	GCancellable *cancellable;
 
 	g_return_if_fail (backend != NULL);
 	g_return_if_fail (E_IS_BOOK_BACKEND_MAPI (backend));
@@ -1228,7 +1233,12 @@ ebbm_op_stop_book_view (EBookBackend *backend, EDataBookView *book_view)
 	op->base.opid = 0;
 	op->book_view = g_object_ref (book_view);
 
-	g_hash_table_remove (priv->running_book_views, book_view);
+	g_mutex_lock (priv->running_views_lock);
+	cancellable = g_hash_table_lookup (priv->running_views, book_view);
+	if (cancellable)
+		g_cancellable_cancel (cancellable);
+	g_hash_table_remove (priv->running_views, book_view);
+	g_mutex_unlock (priv->running_views_lock);
 
 	e_mapi_operation_queue_push (priv->op_queue, op);
 }
@@ -1268,7 +1278,8 @@ e_book_backend_mapi_init (EBookBackendMAPI *ebma)
 	ebma->priv = G_TYPE_INSTANCE_GET_PRIVATE (ebma, E_TYPE_BOOK_BACKEND_MAPI, EBookBackendMAPIPrivate);
 
 	ebma->priv->op_queue = e_mapi_operation_queue_new ((EMapiOperationQueueFunc) ebbm_operation_cb, ebma);
-	ebma->priv->running_book_views = g_hash_table_new (g_direct_hash, g_direct_equal);
+	ebma->priv->running_views = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
+	ebma->priv->running_views_lock = g_mutex_new ();
 	ebma->priv->conn_lock = g_mutex_new ();
 
 	ebma->priv->update_cache = g_cancellable_new ();
@@ -1309,7 +1320,8 @@ ebbm_dispose (GObject *object)
 		FREE (priv->profile);
 		FREE (priv->book_uri);
 
-		g_hash_table_destroy (priv->running_book_views);
+		g_hash_table_destroy (priv->running_views);
+		g_mutex_free (priv->running_views_lock);
 		g_mutex_free (priv->conn_lock);
 
 		#undef UNREF
@@ -1424,10 +1436,16 @@ e_book_backend_mapi_get_db (EBookBackendMAPI *ebma, EBookBackendSqliteDB **db)
 gboolean
 e_book_backend_mapi_book_view_is_running (EBookBackendMAPI *ebma, EDataBookView *book_view)
 {
+	gboolean res;
+
 	g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (ebma), FALSE);
 	g_return_val_if_fail (ebma->priv != NULL, FALSE);
 
-	return g_hash_table_lookup (ebma->priv->running_book_views, book_view) != NULL;
+	g_mutex_lock (ebma->priv->running_views_lock);
+	res = g_hash_table_lookup (ebma->priv->running_views, book_view) != NULL;
+	g_mutex_unlock (ebma->priv->running_views_lock);
+
+	return res;
 }
 
 gboolean
@@ -1615,344 +1633,6 @@ e_book_backend_mapi_refresh_cache (EBookBackendMAPI *ebma)
 
 /* utility functions/macros */
 
-/* 'data' is one of GET_ALL_KNOWN_IDS or GET_UIDS_ONLY */
-gboolean
-mapi_book_utils_get_prop_list (EMapiConnection *conn,
-			       mapi_id_t fid,
-			       TALLOC_CTX *mem_ctx,
-			       struct SPropTagArray *props,
-			       gpointer data,
-			       GCancellable *cancellable,
-			       GError **perror)
-{
-	/* this is a list of all known book MAPI tag IDs;
-	   if you add new add it here too, otherwise it may not be fetched */
-	static uint32_t known_book_mapi_ids[] = {
-		PR_ASSISTANT_TELEPHONE_NUMBER_UNICODE,
-		PR_ASSISTANT_UNICODE,
-		PR_BIRTHDAY,
-		PR_BODY,
-		PR_BODY_UNICODE,
-		PR_BUSINESS_FAX_NUMBER_UNICODE,
-		PR_COMPANY_MAIN_PHONE_NUMBER_UNICODE,
-		PR_COMPANY_NAME_UNICODE,
-		PR_COUNTRY_UNICODE,
-		PR_DEPARTMENT_NAME_UNICODE,
-		PR_DISPLAY_NAME_UNICODE,
-		PR_EMAIL_ADDRESS_UNICODE,
-		PR_SMTP_ADDRESS_UNICODE, /* used in GAL */
-		PR_FID,
-		PR_GIVEN_NAME_UNICODE,
-		PR_HASATTACH,
-		PR_HOME_ADDRESS_CITY_UNICODE,
-		PR_HOME_ADDRESS_COUNTRY_UNICODE,
-		PR_HOME_ADDRESS_POSTAL_CODE_UNICODE,
-		PR_HOME_ADDRESS_POST_OFFICE_BOX_UNICODE,
-		PR_HOME_ADDRESS_STATE_OR_PROVINCE_UNICODE,
-		PR_HOME_FAX_NUMBER_UNICODE,
-		PR_HOME_TELEPHONE_NUMBER_UNICODE,
-		PR_INSTANCE_NUM,
-		PR_INST_ID,
-		PR_LAST_MODIFICATION_TIME,
-		PR_LOCALITY_UNICODE,
-		PR_MANAGER_NAME_UNICODE,
-		PR_MESSAGE_CLASS,
-		PR_MID,
-		PR_MOBILE_TELEPHONE_NUMBER_UNICODE,
-		PR_NICKNAME_UNICODE,
-		PR_NORMALIZED_SUBJECT_UNICODE,
-		PR_OFFICE_LOCATION_UNICODE,
-		PR_OFFICE_TELEPHONE_NUMBER_UNICODE,
-		PR_PAGER_TELEPHONE_NUMBER_UNICODE,
-		PR_POSTAL_CODE_UNICODE,
-		PR_POST_OFFICE_BOX_UNICODE,
-		PR_PROFESSION_UNICODE,
-		PR_RULE_MSG_NAME,
-		PR_RULE_MSG_PROVIDER,
-		PR_SPOUSE_NAME_UNICODE,
-		PR_STATE_OR_PROVINCE_UNICODE,
-		PR_SUBJECT_UNICODE,
-		PR_SURNAME_UNICODE,
-		PR_TITLE_UNICODE,
-		PR_WEDDING_ANNIVERSARY,
-		PROP_TAG(PT_UNICODE, 0x801f)
-	};
-
-	static uint32_t uids_only_ids[] = {
-		PidTagFolderId,
-		PidTagMid,
-		PidTagLastModificationTime,
-		PidTagEmailAddress
-	};
-
-	/* do not make this array static, the function modifies it on run */
-	ResolveNamedIDsData nids[] = {
-		{ PidLidDistributionListName, 0 },
-		{ PidLidDistributionListOneOffMembers, 0 },
-		{ PidLidDistributionListMembers, 0 },
-		{ PidLidDistributionListChecksum, 0 },
-
-		{ PidLidFileUnder, 0 },
-
-		{ PidLidEmail1OriginalDisplayName, 0 },
-		{ PidLidEmail2OriginalDisplayName, 0 },
-		{ PidLidEmail3OriginalDisplayName, 0 },
-		{ PidLidInstantMessagingAddress, 0 },
-		{ PidLidHtml, 0 },
-		{ PidLidFreeBusyLocation, 0 }
-	};
-
-	g_return_val_if_fail (props != NULL, FALSE);
-
-	if (data == GET_UIDS_ONLY)
-		return e_mapi_utils_add_props_to_props_array (mem_ctx, props, uids_only_ids, G_N_ELEMENTS (uids_only_ids));
-
-	if (data == GET_ALL_KNOWN_IDS && !e_mapi_utils_add_props_to_props_array (mem_ctx, props, known_book_mapi_ids, G_N_ELEMENTS (known_book_mapi_ids)))
-		return FALSE;
-
-	/* called with fid = 0 from GAL */
-	if (!fid)
-		fid = e_mapi_connection_get_default_folder_id (conn, olFolderContacts, cancellable, NULL);
-
-	return e_mapi_utils_add_named_ids_to_props_array (conn, fid, mem_ctx, props, nids, G_N_ELEMENTS (nids), cancellable, perror);
-}
-
-static gchar *
-bin_to_string (const uint8_t *lpb, uint32_t cb)
-{
-	gchar *res, *p;
-	uint32_t i;
-
-	g_return_val_if_fail (lpb != NULL, NULL);
-	g_return_val_if_fail (cb > 0, NULL);
-
-	res = g_new0 (gchar, cb * 2 + 1);
-	for (i = 0, p = res; i < cb; i++, p += 2) {
-		sprintf (p, "%02x", lpb[i] & 0xFF);
-	}
-
-	return res;
-}
-
-static const gchar *
-not_null (gconstpointer ptr)
-{
-	return ptr ? (const gchar *) ptr : "";
-}
-
-/* This is not setting E_CONTACT_UID */
-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)
-{
-	EContact *contact = e_contact_new ();
-	gint i;
-
-	if (book_uri)
-		e_contact_set (contact, E_CONTACT_BOOK_URI, book_uri);
-
-	#define get_proptag(proptag) (aRow ? e_mapi_util_find_row_propval (aRow, proptag) : e_mapi_util_find_array_propval (mapi_properties, proptag))
-	#define get_str_proptag(proptag) not_null (get_proptag (proptag))
-	#define get_namedid(nid) (aRow ? e_mapi_util_find_row_namedid (aRow, conn, fid, nid) : e_mapi_util_find_array_namedid (mapi_properties, conn, fid, nid))
-	#define get_str_namedid(nid) not_null (get_namedid (nid))
-
-	if (g_str_equal (get_str_proptag (PR_MESSAGE_CLASS), IPM_DISTLIST)) {
-		const struct mapi_SBinaryArray *members, *members_dlist;
-		const struct FILETIME *last_modification;
-		GSList *attrs = NULL, *a;
-		gint i;
-
-		last_modification = get_proptag (PidTagLastModificationTime);
-		if (last_modification) {
-			gchar *buff = NULL;
-
-			buff = mapi_book_utils_timet_to_string (e_mapi_util_filetime_to_time_t (last_modification));
-			if (buff)
-				e_contact_set (contact, E_CONTACT_REV, buff);
-
-			g_free (buff);
-		}
-
-		/* it's a contact list/distribution list, fetch members and return it */
-		e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (TRUE));
-		/* we do not support this option, same as GroupWise */
-		e_contact_set (contact, E_CONTACT_LIST_SHOW_ADDRESSES, GINT_TO_POINTER (TRUE));
-
-		e_contact_set (contact, E_CONTACT_FILE_AS, get_str_namedid (PidLidDistributionListName));
-
-		members = get_namedid (PidLidDistributionListOneOffMembers);
-		members_dlist = get_namedid (PidLidDistributionListMembers);
-
-		g_return_val_if_fail (members != NULL, NULL);
-		g_return_val_if_fail (members_dlist != NULL, NULL);
-
-		/* these two lists should be in sync */
-		g_return_val_if_fail (members_dlist->cValues == members->cValues, NULL);
-
-		for (i = 0; i < members->cValues; i++) {
-			struct Binary_r br;
-			gchar *display_name = NULL, *email = NULL;
-			gchar *str;
-
-			br.lpb = members->bin[i].lpb;
-			br.cb = members->bin[i].cb;
-			if (e_mapi_util_recip_entryid_decode (conn, &br, &display_name, &email)) {
-				EVCardAttribute *attr;
-				gchar *value;
-				CamelInternetAddress *addr;
-
-				addr = camel_internet_address_new ();
-				attr = e_vcard_attribute_new (NULL, EVC_EMAIL);
-
-				camel_internet_address_add (addr, display_name, email);
-
-				value = camel_address_encode (CAMEL_ADDRESS (addr));
-
-				if (value)
-					e_vcard_attribute_add_value (attr, value);
-
-				g_free (value);
-				g_object_unref (addr);
-
-				str = g_strdup_printf ("%d", i + 1);
-				e_vcard_attribute_add_param_with_value (attr,
-						e_vcard_attribute_param_new (EMA_X_MEMBERID),
-						str);
-				g_free (str);
-
-				/* keep the value from ListMembers with the email, to not need to generate it on list changes;
-				   new values added in evolution-mapi will be always SMTP addresses anyway */
-				str = bin_to_string (members_dlist->bin[i].lpb, members_dlist->bin[i].cb);
-				if (str) {
-					e_vcard_attribute_add_param_with_value (attr,
-						e_vcard_attribute_param_new (EMA_X_MEMBERVALUE),
-						str);
-					g_free (str);
-				}
-
-				attrs = g_slist_prepend (attrs, attr);
-			}
-
-			g_free (display_name);
-			g_free (email);
-		}
-
-		for (a = attrs; a; a = a->next) {
-			e_vcard_add_attribute (E_VCARD (contact), a->data);
-		}
-
-		g_slist_free (attrs);
-
-		return contact;
-	}
-
-	for (i = 0; i < G_N_ELEMENTS (mappings); i++) {
-		gpointer value;
-		gint contact_type;
-
-		/* can cast value, no writing to the value; and it'll be freed not before the end of this function */
-		if (mappings[i].contact_type & ELEMENT_TYPE_NAMEDID)
-			value = (gpointer) get_namedid (mappings[i].mapi_id);
-		else
-			value = (gpointer) get_proptag (mappings[i].mapi_id);
-		contact_type = mappings[i].contact_type & ELEMENT_TYPE_MASK;
-		if (mappings[i].element_type == PT_UNICODE && contact_type == ELEMENT_TYPE_SIMPLE) {
-			const gchar *str = value;
-			if (str && *str)
-				e_contact_set (contact, mappings[i].field_id, str);
-		} else if (contact_type == ELEMENT_TYPE_SIMPLE) {
-			if (value && mappings[i].element_type == PT_SYSTIME) {
-				const struct FILETIME *t = value;
-				gchar *buff = NULL;
-
-				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);
-
-				g_free (buff);
-			}
-		} else if (contact_type == ELEMENT_TYPE_COMPLEX) {
-			if (mappings[i].field_id == E_CONTACT_IM_AIM) {
-				const gchar *str = value;
-				if (str && *str) {
-					GList *list = g_list_append (NULL, (gpointer) str);
-
-					e_contact_set (contact, mappings[i].field_id, list);
-
-					g_list_free (list);
-				}
-			} else if (mappings[i].field_id == E_CONTACT_BIRTH_DATE
-				   || mappings[i].field_id == E_CONTACT_ANNIVERSARY) {
-				const struct FILETIME *t = value;
-				time_t time;
-				struct tm * tmtime;
-				if (value) {
-					EContactDate date = {0};
-
-					time = e_mapi_util_filetime_to_time_t (t);
-					tmtime = gmtime (&time);
-
-					date.day = tmtime->tm_mday;
-					date.month = tmtime->tm_mon + 1;
-					date.year = tmtime->tm_year + 1900;
-					e_contact_set (contact, mappings[i].field_id, &date);
-				}
-
-			} else if (mappings[i].field_id == E_CONTACT_ADDRESS_WORK
-				   || mappings[i].field_id == E_CONTACT_ADDRESS_HOME) {
-				EContactAddress contact_addr = { 0 };
-
-				/* type-casting below to not allocate memory twice; e_contact_set will copy values itself. */
-				if (mappings[i].field_id == E_CONTACT_ADDRESS_HOME) {
-					contact_addr.address_format = NULL;
-					contact_addr.po = NULL;
-					contact_addr.street = (gchar *) value;
-					contact_addr.ext = (gchar *) get_str_proptag (PR_HOME_ADDRESS_POST_OFFICE_BOX_UNICODE);
-					contact_addr.locality = (gchar *) get_str_proptag (PR_HOME_ADDRESS_CITY_UNICODE);
-					contact_addr.region = (gchar *) get_str_proptag (PR_HOME_ADDRESS_STATE_OR_PROVINCE_UNICODE);
-					contact_addr.code = (gchar *) get_str_proptag (PR_HOME_ADDRESS_POSTAL_CODE_UNICODE);
-					contact_addr.country = (gchar *) get_str_proptag (PR_HOME_ADDRESS_COUNTRY_UNICODE);
-				} else {
-					contact_addr.address_format = NULL;
-					contact_addr.po = NULL;
-					contact_addr.street = (gchar *) value;
-					contact_addr.ext = (gchar *) get_str_proptag (PR_POST_OFFICE_BOX_UNICODE);
-					contact_addr.locality = (gchar *) get_str_proptag (PR_LOCALITY_UNICODE);
-					contact_addr.region = (gchar *) get_str_proptag (PR_STATE_OR_PROVINCE_UNICODE);
-					contact_addr.code = (gchar *) get_str_proptag (PR_POSTAL_CODE_UNICODE);
-					contact_addr.country = (gchar *) get_str_proptag (PR_COUNTRY_UNICODE);
-				}
-
-				#define is_set(x) ((x) && *(x))
-				if (is_set (contact_addr.address_format) ||
-				    is_set (contact_addr.po) ||
-				    is_set (contact_addr.street) ||
-				    is_set (contact_addr.ext) ||
-				    is_set (contact_addr.locality) ||
-				    is_set (contact_addr.region) ||
-				    is_set (contact_addr.code) ||
-				    is_set (contact_addr.country)) {
-					e_contact_set (contact, mappings[i].field_id, &contact_addr);
-				}
-				#undef is_set
-			}
-		}
-	}
-
-	if (!e_contact_get (contact, E_CONTACT_EMAIL_1)) {
-		gconstpointer value = get_proptag (PR_SMTP_ADDRESS_UNICODE);
-
-		if (value)
-			e_contact_set (contact, E_CONTACT_EMAIL_1, value);
-	}
-
-	#undef get_proptag
-	#undef get_str_proptag
-	#undef get_namedid
-	#undef get_str_namedid
-
-	return contact;
-}
-
 void
 mapi_error_to_edb_error (GError **perror, const GError *mapi_error, EDataBookStatus code, const gchar *context)
 {
@@ -1990,421 +1670,3 @@ 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);
-}
-
-struct EMapiSExpParserData
-{
-	TALLOC_CTX *mem_ctx;
-	/* parser results in ints, indexes to res_parts */
-	GPtrArray *res_parts;
-};
-
-static ESExpResult *
-term_eval_and (struct _ESExp *f,
-	       gint argc,
-	       struct _ESExpResult **argv,
-	       gpointer user_data)
-{
-	struct EMapiSExpParserData *esp = user_data;
-	ESExpResult *r;
-	gint ii, jj, valid = 0;
-
-	r = e_sexp_result_new (f, ESEXP_RES_INT);
-	r->value.number = -1;
-
-	for (ii = 0; ii < argc; ii++) {
-		if (argv[ii]->type == ESEXP_RES_INT &&
-		    argv[ii]->value.number >= 0 && 
-		    argv[ii]->value.number < esp->res_parts->len) {
-			jj = argv[ii]->value.number;
-			valid++;
-		}
-	}
-
-	if (valid == 1) {
-		r->value.number = jj;
-	} else if (valid > 0) {
-		struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
-		g_return_val_if_fail (res != NULL, NULL);
-
-		res->rt = RES_AND;
-		res->res.resAnd.cRes = valid;
-		res->res.resAnd.res = talloc_zero_array (esp->mem_ctx, struct mapi_SRestriction_and, res->res.resAnd.cRes + 1);
-
-		jj = 0;
-
-		for (ii = 0; ii < argc; ii++) {
-			if (argv[ii]->type == ESEXP_RES_INT &&
-			    argv[ii]->value.number >= 0 && 
-			    argv[ii]->value.number < esp->res_parts->len) {
-				struct mapi_SRestriction *subres = g_ptr_array_index (esp->res_parts, argv[ii]->value.number);
-
-				res->res.resAnd.res[jj].rt = subres->rt;
-				res->res.resAnd.res[jj].res = subres->res;
-
-				jj++;
-			}
-		}
-
-		g_ptr_array_add (esp->res_parts, res);
-		r->value.number = esp->res_parts->len - 1;
-	}
-
-	return r;
-}
-
-static ESExpResult *
-term_eval_or (struct _ESExp *f,
-	      gint argc,
-	      struct _ESExpResult **argv,
-	      gpointer user_data)
-{
-	struct EMapiSExpParserData *esp = user_data;
-	ESExpResult *r;
-	gint ii, jj = -1, valid = 0;
-
-	r = e_sexp_result_new (f, ESEXP_RES_INT);
-	r->value.number = -1;
-
-	for (ii = 0; ii < argc; ii++) {
-		if (argv[ii]->type == ESEXP_RES_INT &&
-		    argv[ii]->value.number >= 0 && 
-		    argv[ii]->value.number < esp->res_parts->len) {
-			jj = argv[ii]->value.number;
-			valid++;
-		    }
-	}
-
-	if (valid == 1) {
-		r->value.number = jj;
-	} else if (valid > 0) {
-		struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
-		g_return_val_if_fail (res != NULL, NULL);
-
-		res->rt = RES_OR;
-		res->res.resOr.cRes = valid;
-		res->res.resOr.res = talloc_zero_array (esp->mem_ctx, struct mapi_SRestriction_or, res->res.resOr.cRes + 1);
-
-		jj = 0;
-
-		for (ii = 0; ii < argc; ii++) {
-			if (argv[ii]->type == ESEXP_RES_INT &&
-			    argv[ii]->value.number >= 0 && 
-			    argv[ii]->value.number < esp->res_parts->len) {
-				struct mapi_SRestriction *subres = g_ptr_array_index (esp->res_parts, argv[ii]->value.number);
-
-				res->res.resOr.res[jj].rt = subres->rt;
-				res->res.resOr.res[jj].res = subres->res;
-
-				jj++;
-			}
-		}
-
-		g_ptr_array_add (esp->res_parts, res);
-		r->value.number = esp->res_parts->len - 1;
-	}
-
-	return r;
-}
-
-static ESExpResult *
-term_eval_not (struct _ESExp *f,
-	       gint argc,
-	       struct _ESExpResult **argv,
-	       gpointer user_data)
-{
-	ESExpResult *r;
-
-	r = e_sexp_result_new (f, ESEXP_RES_INT);
-	r->value.number = -1;
-
-	#ifdef HAVE_RES_NOT_SUPPORTED
-	if (argc == 1 && argv[0]->type == ESEXP_RES_INT) {
-		struct EMapiSExpParserData *esp = user_data;
-		gint idx = argv[0]->value.number;
-
-		if (esp && idx >= 0 && idx < esp->res_parts->len) {
-			struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
-			g_return_val_if_fail (res != NULL, NULL);
-
-			res->rt = RES_NOT;
-			res->res.resNot.res = g_ptr_array_index (esp->res_parts, idx);
-
-			g_ptr_array_add (esp->res_parts, res);
-			r->value.number = esp->res_parts->len - 1;
-		}
-	}
-	#endif
-
-	return r;
-}
-
-static uint32_t
-get_proptag_from_field_name (const gchar *field_name, gboolean is_contact_field)
-{
-	EContactField cfid;
-	gint ii;
-
-	if (is_contact_field)
-		cfid = e_contact_field_id (field_name);
-	else
-		cfid = e_contact_field_id_from_vcard (field_name);
-
-	for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) {
-		if (mappings[ii].field_id == cfid) {
-			return mappings[ii].mapi_id;
-		}
-	}
-
-	return MAPI_E_RESERVED;
-}
-
-static ESExpResult *
-func_eval_text_compare (struct _ESExp *f,
-			gint argc,
-			struct _ESExpResult **argv,
-			gpointer user_data,
-			uint32_t fuzzy)
-{
-	struct EMapiSExpParserData *esp = user_data;
-	ESExpResult *r;
-
-	r = e_sexp_result_new (f, ESEXP_RES_INT);
-	r->value.number = -1;
-
-	if (argc == 2
-	    && argv[0]->type == ESEXP_RES_STRING
-	    && argv[1]->type == ESEXP_RES_STRING) {
-		const gchar *propname = argv[0]->value.string;
-		const gchar *propvalue = argv[1]->value.string;
-
-		if (propname && propvalue && g_ascii_strcasecmp (propname, "x-evolution-any-field") != 0) {
-			uint32_t proptag = get_proptag_from_field_name (propname, TRUE);
-
-			if (proptag != MAPI_E_RESERVED && ((proptag & 0xFFFF) == PT_UNICODE || (proptag & 0xFFFF) == PT_STRING8)) {
-				struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
-				g_return_val_if_fail (res != NULL, NULL);
-
-				res->rt = RES_CONTENT;
-				res->res.resContent.fuzzy = fuzzy | FL_IGNORECASE;
-				res->res.resContent.ulPropTag = proptag;
-				res->res.resContent.lpProp.ulPropTag = proptag;
-				res->res.resContent.lpProp.value.lpszW = talloc_strdup (esp->mem_ctx, propvalue);
-
-				g_ptr_array_add (esp->res_parts, res);
-				r->value.number = esp->res_parts->len - 1;
-			} else if (g_ascii_strcasecmp (propname, "email") == 0) {
-				struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
-				g_return_val_if_fail (res != NULL, NULL);
-
-				res->rt = RES_OR;
-				res->res.resOr.cRes = 3;
-				res->res.resOr.res = talloc_zero_array (esp->mem_ctx, struct mapi_SRestriction_or, res->res.resOr.cRes + 1);
-
-				proptag = get_proptag_from_field_name ("email_1", TRUE);
-				res->res.resOr.res[0].rt = RES_CONTENT;
-				res->res.resOr.res[0].res.resContent.fuzzy = fuzzy | FL_IGNORECASE;
-				res->res.resOr.res[0].res.resContent.ulPropTag = proptag;
-				res->res.resOr.res[0].res.resContent.lpProp.ulPropTag = proptag;
-				res->res.resOr.res[0].res.resContent.lpProp.value.lpszW = talloc_strdup (esp->mem_ctx, propvalue);
-
-				proptag = get_proptag_from_field_name ("email_2", TRUE);
-				res->res.resOr.res[1].rt = RES_CONTENT;
-				res->res.resOr.res[1].res.resContent.fuzzy = fuzzy | FL_IGNORECASE;
-				res->res.resOr.res[1].res.resContent.ulPropTag = proptag;
-				res->res.resOr.res[1].res.resContent.lpProp.ulPropTag = proptag;
-				res->res.resOr.res[1].res.resContent.lpProp.value.lpszW = talloc_strdup (esp->mem_ctx, propvalue);
-
-				proptag = get_proptag_from_field_name ("email_3", TRUE);
-				res->res.resOr.res[2].rt = RES_CONTENT;
-				res->res.resOr.res[2].res.resContent.fuzzy = fuzzy | FL_IGNORECASE;
-				res->res.resOr.res[2].res.resContent.ulPropTag = proptag;
-				res->res.resOr.res[2].res.resContent.lpProp.ulPropTag = proptag;
-				res->res.resOr.res[2].res.resContent.lpProp.value.lpszW = talloc_strdup (esp->mem_ctx, propvalue);
-
-				g_ptr_array_add (esp->res_parts, res);
-				r->value.number = esp->res_parts->len - 1;
-			}
-		}
-	}
-
-	return r;
-}
-
-static ESExpResult *
-func_eval_contains (struct _ESExp *f,
-		    gint argc,
-		    struct _ESExpResult **argv,
-		    gpointer user_data)
-{
-	return func_eval_text_compare (f, argc, argv, user_data, FL_SUBSTRING);
-}
-
-static ESExpResult *
-func_eval_is (struct _ESExp *f,
-	      gint argc,
-	      struct _ESExpResult **argv,
-	      gpointer user_data)
-{
-	return func_eval_text_compare (f, argc, argv, user_data, FL_FULLSTRING);
-}
-static ESExpResult *
-func_eval_beginswith (struct _ESExp *f,
-		      gint argc,
-		      struct _ESExpResult **argv,
-		      gpointer user_data)
-{
-	return func_eval_text_compare (f, argc, argv, user_data, FL_PREFIX);
-}
-static ESExpResult *
-func_eval_endswith (struct _ESExp *f,
-		    gint argc,
-		    struct _ESExpResult **argv,
-		    gpointer user_data)
-{
-	/* no suffix, thus at least substring is used */
-	return func_eval_text_compare (f, argc, argv, user_data, FL_SUBSTRING);
-}
-
-static ESExpResult *
-func_eval_field_exists (struct _ESExp *f,
-			gint argc,
-			struct _ESExpResult **argv,
-			gpointer user_data,
-			gboolean is_contact_field)
-{
-	struct EMapiSExpParserData *esp = user_data;
-	ESExpResult *r;
-
-	r = e_sexp_result_new (f, ESEXP_RES_INT);
-	r->value.number = -1;
-
-	if (argc == 1 && argv[0]->type == ESEXP_RES_STRING) {
-		const gchar *propname = argv[0]->value.string;
-		uint32_t proptag = get_proptag_from_field_name (propname, is_contact_field);
-
-		if (proptag != MAPI_E_RESERVED) {
-			struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
-			g_return_val_if_fail (res != NULL, NULL);
-
-			res->rt = RES_EXIST;
-			res->res.resExist.ulPropTag = proptag;
-
-			g_ptr_array_add (esp->res_parts, res);
-			r->value.number = esp->res_parts->len - 1;
-		} else if (g_ascii_strcasecmp (propname, "email") == 0) {
-			struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
-			g_return_val_if_fail (res != NULL, NULL);
-
-			res->rt = RES_OR;
-			res->res.resOr.cRes = 3;
-			res->res.resOr.res = talloc_zero_array (esp->mem_ctx, struct mapi_SRestriction_or, res->res.resOr.cRes + 1);
-
-			res->res.resOr.res[0].rt = RES_EXIST;
-			res->res.resOr.res[0].res.resExist.ulPropTag = get_proptag_from_field_name ("email_1", TRUE);
-
-			res->res.resOr.res[1].rt = RES_EXIST;
-			res->res.resOr.res[1].res.resExist.ulPropTag = get_proptag_from_field_name ("email_2", TRUE);
-
-			res->res.resOr.res[2].rt = RES_EXIST;
-			res->res.resOr.res[2].res.resExist.ulPropTag = get_proptag_from_field_name ("email_3", TRUE);
-
-			g_ptr_array_add (esp->res_parts, res);
-			r->value.number = esp->res_parts->len - 1;
-		}
-	}
-
-	return r;
-}
-
-static ESExpResult *
-func_eval_exists (struct _ESExp *f,
-		  gint argc,
-		  struct _ESExpResult **argv,
-		  gpointer user_data)
-{
-	return func_eval_field_exists (f, argc, argv, user_data, TRUE);
-}
-
-static ESExpResult *
-func_eval_exists_vcard (struct _ESExp *f,
-			gint argc,
-			struct _ESExpResult **argv,
-			gpointer user_data)
-{
-	return func_eval_field_exists (f, argc, argv, user_data, FALSE);
-}
-
-struct mapi_SRestriction *
-mapi_book_utils_sexp_to_restriction (TALLOC_CTX *mem_ctx, const gchar *sexp_query)
-{
-	/* 'builtin' functions */
-	static const struct {
-		const gchar *name;
-		ESExpFunc *func;
-		gint type;		/* set to 1 if a function can perform shortcut evaluation, or
-					   doesn't execute everything, 0 otherwise */
-	} check_symbols[] = {
-		{ "and", 		term_eval_and,		0 },
-		{ "or", 		term_eval_or,		0 },
-		{ "not", 		term_eval_not,		0 },
-
-		{ "contains",		func_eval_contains,	0 },
-		{ "is",			func_eval_is,		0 },
-		{ "beginswith",		func_eval_beginswith,	0 },
-		{ "endswith",		func_eval_endswith,	0 },
-		{ "exists",		func_eval_exists,	0 },
-		{ "exists_vcard",	func_eval_exists_vcard,	0 }
-	};
-
-	gint i;
-	ESExp *sexp;
-	ESExpResult *r;
-	struct EMapiSExpParserData esp;
-	struct mapi_SRestriction *restriction;
-
-	g_return_val_if_fail (sexp_query != NULL, NULL);
-
-	esp.mem_ctx = mem_ctx;
-	sexp = e_sexp_new ();
-
-	for (i = 0; i < G_N_ELEMENTS (check_symbols); i++) {
-		if (check_symbols[i].type == 1) {
-			e_sexp_add_ifunction (sexp, 0, check_symbols[i].name,
-					      (ESExpIFunc *) check_symbols[i].func, &esp);
-		} else {
-			e_sexp_add_function (sexp, 0, check_symbols[i].name,
-					     check_symbols[i].func, &esp);
-		}
-	}
-
-	e_sexp_input_text (sexp, sexp_query, strlen (sexp_query));
-	if (e_sexp_parse (sexp) == -1) {
-		e_sexp_unref (sexp);
-		return NULL;
-	}
-
-	esp.res_parts = g_ptr_array_new ();
-	r = e_sexp_eval (sexp);
-
-	restriction = NULL;
-	if (r && r->type == ESEXP_RES_INT && r->value.number >= 0 && r->value.number < esp.res_parts->len)
-		restriction = g_ptr_array_index (esp.res_parts, r->value.number);
-
-	e_sexp_result_free (sexp, r);
-
-	e_sexp_unref (sexp);
-	g_ptr_array_free (esp.res_parts, TRUE);
-
-	return restriction;
-}
diff --git a/src/addressbook/e-book-backend-mapi.h b/src/addressbook/e-book-backend-mapi.h
index 504312e..ce3e2ba 100644
--- a/src/addressbook/e-book-backend-mapi.h
+++ b/src/addressbook/e-book-backend-mapi.h
@@ -32,6 +32,7 @@
 #include "e-mapi-connection.h"
 #include "e-mapi-defs.h"
 #include "e-mapi-utils.h"
+#include "e-mapi-book-utils.h"
 
 G_BEGIN_DECLS
 
@@ -127,33 +128,6 @@ void mapi_error_to_edb_error (GError **perror, const GError *mapi_error, EDataBo
 #define EMA_EBB_CACHE_PROFILEID	"EMA_PROFILE"
 #define EMA_EBB_CACHE_FOLDERID	"EMA_FOLDER"
 
-/* vCard parameter name in contact list */
-#define EMA_X_MEMBERID "X-EMA-MEMBER-ID"
-#define EMA_X_MEMBERVALUE "X-EMA-MEMBER-VALUE"
-
-#define GET_ALL_KNOWN_IDS (GINT_TO_POINTER(1))
-#define GET_UIDS_ONLY     (GINT_TO_POINTER(2))
-
-/* data is one of GET_ALL_KNOWN_IDS or GET_UIDS_ONLY */
-gboolean mapi_book_utils_get_prop_list (EMapiConnection *conn,
-					mapi_id_t fid,
-					TALLOC_CTX *mem_ctx,
-					struct SPropTagArray *props,
-					gpointer data,
-					GCancellable *cancellable,
-					GError **perror);
-
-/* 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);
-
-/* converts sexp_query into mapi_SRestriction, which is completely
-   allocated on the given mem_ctx */
-struct mapi_SRestriction *mapi_book_utils_sexp_to_restriction (TALLOC_CTX *mem_ctx, const gchar *sexp_query);
-
 G_END_DECLS
 
 #endif /* __E_BOOK_BACKEND_MAPI_H__ */
diff --git a/src/libexchangemapi/Makefile.am b/src/libexchangemapi/Makefile.am
index 78e8011..ddeaa82 100644
--- a/src/libexchangemapi/Makefile.am
+++ b/src/libexchangemapi/Makefile.am
@@ -5,6 +5,7 @@ AM_CPPFLAGS =						\
 	-I$(top_srcdir)					\
 	$(EVOLUTION_DATA_SERVER_CFLAGS)			\
 	$(LIBEDATASERVER_CFLAGS)			\
+	$(LIBEBOOK_CFLAGS)				\
 	$(LIBECAL_CFLAGS)				\
 	$(LIBMAPI_CFLAGS)				\
 	$(CAMEL_CFLAGS)
@@ -32,6 +33,8 @@ libexchangemapi_1_0_la_SOURCES =	\
 	e-mapi-debug.h			\
 	e-mapi-utils.c			\
 	e-mapi-utils.h			\
+	e-mapi-book-utils.c		\
+	e-mapi-book-utils.h		\
 	e-mapi-cal-utils.c		\
 	e-mapi-cal-utils.h		\
 	e-mapi-cal-tz-utils.c		\
@@ -47,6 +50,7 @@ libexchangemapi_1_0_la_SOURCES =	\
 libexchangemapi_1_0_la_LIBADD =			\
 	$(EVOLUTION_DATA_SERVER_LIBS)		\
 	$(LIBEDATASERVER_LIBS)			\
+	$(LIBEBOOK_LIBS)			\
 	$(LIBECAL_LIBS)				\
 	$(LIBMAPI_LIBS)				\
 	$(CAMEL_LIBS)
@@ -62,6 +66,7 @@ libexchangemapiinclude_HEADERS = 	\
 	e-mapi-debug.h			\
 	e-mapi-fast-transfer.h		\
 	e-mapi-utils.h			\
+	e-mapi-book-utils.h		\
 	e-mapi-cal-utils.h		\
 	e-mapi-cal-tz-utils.h		\
 	e-mapi-cal-recur-utils.h	\
diff --git a/src/libexchangemapi/e-mapi-book-utils.c b/src/libexchangemapi/e-mapi-book-utils.c
new file mode 100644
index 0000000..911b4e1
--- /dev/null
+++ b/src/libexchangemapi/e-mapi-book-utils.c
@@ -0,0 +1,1134 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libedataserver/e-sexp.h>
+
+#include "e-mapi-book-utils.h"
+
+#define ELEMENT_TYPE_MASK   0xF /* mask where the real type of the element is stored */
+
+#define ELEMENT_TYPE_SKIP_SET	0x00
+#define ELEMENT_TYPE_SIMPLE	0x01
+#define ELEMENT_TYPE_COMPLEX	0x02
+
+static const struct field_element_mapping {
+	EContactField field_id;
+	uint32_t mapi_id;
+	gint element_type;
+} mappings [] = {
+	{ E_CONTACT_UID,		PidTagMid,			ELEMENT_TYPE_SKIP_SET },
+	{ E_CONTACT_REV,		PidTagLastModificationTime,	ELEMENT_TYPE_SIMPLE },
+
+	{ E_CONTACT_FILE_AS,		PidLidFileUnder,		ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_FULL_NAME,		PidTagDisplayName,		ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_GIVEN_NAME,		PidTagGivenName,		ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_FAMILY_NAME,	PidTagSurname,			ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_NICKNAME,		PidTagNickname,			ELEMENT_TYPE_SIMPLE },
+
+	{ E_CONTACT_EMAIL_1,		PidLidEmail1OriginalDisplayName,ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_EMAIL_2,		PidLidEmail2EmailAddress,	ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_EMAIL_3,		PidLidEmail3EmailAddress,	ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_IM_AIM,		PidLidInstantMessagingAddress,	ELEMENT_TYPE_COMPLEX },
+
+	{ E_CONTACT_PHONE_BUSINESS,	PidTagBusinessTelephoneNumber,	ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_PHONE_HOME,		PidTagHomeTelephoneNumber,	ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_PHONE_MOBILE,	PidTagMobileTelephoneNumber,	ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_PHONE_HOME_FAX,	PidTagHomeFaxNumber,		ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_PHONE_BUSINESS_FAX,	PidTagBusinessFaxNumber,	ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_PHONE_PAGER,	PidTagPagerTelephoneNumber,	ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_PHONE_ASSISTANT,	PidTagAssistantTelephoneNumber,	ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_PHONE_COMPANY,	PidTagCompanyMainTelephoneNumber,ELEMENT_TYPE_SIMPLE },
+
+	{ E_CONTACT_HOMEPAGE_URL,	PidLidHtml,			ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_FREEBUSY_URL,	PidLidFreeBusyLocation,		ELEMENT_TYPE_SIMPLE },
+
+	{ E_CONTACT_ROLE,		PidTagProfession,		ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_TITLE,		PidTagTitle,			ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_ORG,		PidTagCompanyName,		ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_ORG_UNIT,		PidTagDepartmentName,		ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_MANAGER,		PidTagManagerName,		ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_ASSISTANT,		PidTagAssistant,		ELEMENT_TYPE_SIMPLE },
+
+	{ E_CONTACT_OFFICE,		PidTagOfficeLocation,		ELEMENT_TYPE_SIMPLE },
+	{ E_CONTACT_SPOUSE,		PidTagSpouseName,		ELEMENT_TYPE_SIMPLE },
+
+	{ E_CONTACT_BIRTH_DATE,		PidTagBirthday,			ELEMENT_TYPE_COMPLEX },
+	{ E_CONTACT_ANNIVERSARY,	PidTagWeddingAnniversary,	ELEMENT_TYPE_COMPLEX },
+
+	{ E_CONTACT_NOTE,		PidTagBody,			ELEMENT_TYPE_SIMPLE },
+
+	{ E_CONTACT_ADDRESS_HOME,	PidLidHomeAddress,		ELEMENT_TYPE_COMPLEX },
+	{ E_CONTACT_ADDRESS_WORK,	PidLidOtherAddress,		ELEMENT_TYPE_COMPLEX }
+};
+
+/* extra properties used in ELEMENT_TYPE_COMPLEX types and some other etra properties */
+static const uint32_t extra_proptags[] = {
+	PidTagHomeAddressPostOfficeBox,
+	PidTagHomeAddressCity,
+	PidTagHomeAddressStateOrProvince,
+	PidTagHomeAddressPostalCode,
+	PidTagHomeAddressCountry,
+	PidTagPostOfficeBox,
+	PidTagLocality,
+	PidTagStateOrProvince,
+	PidTagPostalCode,
+	PidTagCountry,
+	PidTagPrimarySmtpAddress,
+	PidTagFolderId
+};
+
+static gchar *
+bin_to_string (const uint8_t *lpb, uint32_t cb)
+{
+	gchar *res, *p;
+	uint32_t i;
+
+	g_return_val_if_fail (lpb != NULL, NULL);
+	g_return_val_if_fail (cb > 0, NULL);
+
+	res = g_new0 (gchar, cb * 2 + 1);
+	for (i = 0, p = res; i < cb; i++, p += 2) {
+		sprintf (p, "%02x", lpb[i] & 0xFF);
+	}
+
+	return res;
+}
+
+static const gchar *
+not_null (gconstpointer ptr)
+{
+	return ptr ? (const gchar *) ptr : "";
+}
+
+EContact *
+e_mapi_book_utils_contact_from_object (EMapiConnection *conn,
+				       EMapiObject *object,
+				       const gchar *book_uri)
+{
+	EContact *contact;
+	const mapi_id_t *pmid;
+	gint i;
+
+	g_return_val_if_fail (object != NULL, NULL);
+
+	contact = e_contact_new ();
+	if (book_uri)
+		e_contact_set (contact, E_CONTACT_BOOK_URI, book_uri);
+
+	#define get_proptag(proptag) e_mapi_util_find_array_propval (&object->properties, proptag)
+	#define get_str_proptag(proptag) not_null (get_proptag (proptag))
+
+	pmid = get_proptag (PidTagMid);
+	if (pmid) {
+		gchar *suid = e_mapi_util_mapi_id_to_string (*pmid);
+
+		e_contact_set (contact, E_CONTACT_UID, suid);
+
+		g_free (suid);
+	}
+
+	if (g_str_equal (get_str_proptag (PidTagMessageClass), IPM_DISTLIST)) {
+		const struct mapi_SBinaryArray *members, *members_dlist;
+		const struct FILETIME *last_modification;
+		GSList *attrs = NULL, *a;
+		gint i;
+
+		last_modification = get_proptag (PidTagLastModificationTime);
+		if (last_modification) {
+			gchar *buff = NULL;
+
+			buff = e_mapi_book_utils_timet_to_string (e_mapi_util_filetime_to_time_t (last_modification));
+			if (buff)
+				e_contact_set (contact, E_CONTACT_REV, buff);
+
+			g_free (buff);
+		}
+
+		/* it's a contact list/distribution list, fetch members and return it */
+		e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (TRUE));
+		/* we do not support this option, same as GroupWise */
+		e_contact_set (contact, E_CONTACT_LIST_SHOW_ADDRESSES, GINT_TO_POINTER (TRUE));
+
+		e_contact_set (contact, E_CONTACT_FILE_AS, get_str_proptag (PidLidDistributionListName));
+
+		members = get_proptag (PidLidDistributionListOneOffMembers);
+		members_dlist = get_proptag (PidLidDistributionListMembers);
+
+		g_return_val_if_fail (members != NULL, NULL);
+		g_return_val_if_fail (members_dlist != NULL, NULL);
+
+		/* these two lists should be in sync */
+		g_return_val_if_fail (members_dlist->cValues == members->cValues, NULL);
+
+		for (i = 0; i < members->cValues; i++) {
+			struct Binary_r br;
+			gchar *display_name = NULL, *email = NULL;
+			gchar *str;
+
+			br.lpb = members->bin[i].lpb;
+			br.cb = members->bin[i].cb;
+			if (e_mapi_util_recip_entryid_decode (conn, &br, &display_name, &email)) {
+				EVCardAttribute *attr;
+				gchar *value;
+				CamelInternetAddress *addr;
+
+				addr = camel_internet_address_new ();
+				attr = e_vcard_attribute_new (NULL, EVC_EMAIL);
+
+				camel_internet_address_add (addr, display_name, email);
+
+				value = camel_address_encode (CAMEL_ADDRESS (addr));
+
+				if (value)
+					e_vcard_attribute_add_value (attr, value);
+
+				g_free (value);
+				g_object_unref (addr);
+
+				str = g_strdup_printf ("%d", i + 1);
+				e_vcard_attribute_add_param_with_value (attr,
+						e_vcard_attribute_param_new (EMA_X_MEMBERID),
+						str);
+				g_free (str);
+
+				/* keep the value from ListMembers with the email, to not need to generate it on list changes;
+				   new values added in evolution-mapi will be always SMTP addresses anyway */
+				str = bin_to_string (members_dlist->bin[i].lpb, members_dlist->bin[i].cb);
+				if (str) {
+					e_vcard_attribute_add_param_with_value (attr,
+						e_vcard_attribute_param_new (EMA_X_MEMBERVALUE),
+						str);
+					g_free (str);
+				}
+
+				attrs = g_slist_prepend (attrs, attr);
+			}
+
+			g_free (display_name);
+			g_free (email);
+		}
+
+		for (a = attrs; a; a = a->next) {
+			e_vcard_add_attribute (E_VCARD (contact), a->data);
+		}
+
+		g_slist_free (attrs);
+
+		return contact;
+	}
+
+	for (i = 0; i < G_N_ELEMENTS (mappings); i++) {
+		gpointer value;
+		gint element_type;
+
+		/* can cast value, no writing to the value; and it'll be freed not before the end of this function */
+		value = (gpointer) get_proptag (mappings[i].mapi_id);
+		element_type = mappings[i].element_type & ELEMENT_TYPE_MASK;
+		if (element_type == ELEMENT_TYPE_SKIP_SET) {
+			/* skip, when asked for */
+		} else if (element_type == ELEMENT_TYPE_SIMPLE) {
+			switch (mappings[i].mapi_id & 0xFFFF) {
+				case PT_UNICODE: {
+					const gchar *str = value;
+					if (str && *str)
+						e_contact_set (contact, mappings[i].field_id, str);
+				} break;
+				case PT_SYSTIME: {
+					const struct FILETIME *t = value;
+					gchar *buff = NULL;
+
+					buff = e_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);
+
+					g_free (buff);
+				} break;
+				default:
+					/* ignore everything else */
+					break;
+			}
+		} else if (element_type == ELEMENT_TYPE_COMPLEX) {
+			if (mappings[i].field_id == E_CONTACT_IM_AIM) {
+				const gchar *str = value;
+				if (str && *str) {
+					GList *list = g_list_append (NULL, (gpointer) str);
+
+					e_contact_set (contact, mappings[i].field_id, list);
+
+					g_list_free (list);
+				}
+			} else if (mappings[i].field_id == E_CONTACT_BIRTH_DATE
+				   || mappings[i].field_id == E_CONTACT_ANNIVERSARY) {
+				const struct FILETIME *t = value;
+				time_t time;
+				struct tm * tmtime;
+				if (value) {
+					EContactDate date = {0};
+
+					time = e_mapi_util_filetime_to_time_t (t);
+					tmtime = gmtime (&time);
+
+					date.day = tmtime->tm_mday;
+					date.month = tmtime->tm_mon + 1;
+					date.year = tmtime->tm_year + 1900;
+					e_contact_set (contact, mappings[i].field_id, &date);
+				}
+
+			} else if (mappings[i].field_id == E_CONTACT_ADDRESS_WORK
+				   || mappings[i].field_id == E_CONTACT_ADDRESS_HOME) {
+				EContactAddress contact_addr = { 0 };
+
+				/* type-casting below to not allocate memory twice; e_contact_set will copy values itself. */
+				if (mappings[i].field_id == E_CONTACT_ADDRESS_HOME) {
+					contact_addr.address_format = NULL;
+					contact_addr.po = NULL;
+					contact_addr.street = (gchar *) value;
+					contact_addr.ext = (gchar *) get_str_proptag (PidTagHomeAddressPostOfficeBox);
+					contact_addr.locality = (gchar *) get_str_proptag (PidTagHomeAddressCity);
+					contact_addr.region = (gchar *) get_str_proptag (PidTagHomeAddressStateOrProvince);
+					contact_addr.code = (gchar *) get_str_proptag (PidTagHomeAddressPostalCode);
+					contact_addr.country = (gchar *) get_str_proptag (PidTagHomeAddressCountry);
+				} else {
+					contact_addr.address_format = NULL;
+					contact_addr.po = NULL;
+					contact_addr.street = (gchar *) value;
+					contact_addr.ext = (gchar *) get_str_proptag (PidTagPostOfficeBox);
+					contact_addr.locality = (gchar *) get_str_proptag (PidTagLocality);
+					contact_addr.region = (gchar *) get_str_proptag (PidTagStateOrProvince);
+					contact_addr.code = (gchar *) get_str_proptag (PidTagPostalCode);
+					contact_addr.country = (gchar *) get_str_proptag (PidTagCountry);
+				}
+
+				#define is_set(x) ((x) && *(x))
+				if (is_set (contact_addr.address_format) ||
+				    is_set (contact_addr.po) ||
+				    is_set (contact_addr.street) ||
+				    is_set (contact_addr.ext) ||
+				    is_set (contact_addr.locality) ||
+				    is_set (contact_addr.region) ||
+				    is_set (contact_addr.code) ||
+				    is_set (contact_addr.country)) {
+					e_contact_set (contact, mappings[i].field_id, &contact_addr);
+				}
+				#undef is_set
+			}
+		}
+	}
+
+	if (!e_contact_get (contact, E_CONTACT_EMAIL_1)) {
+		gconstpointer value = get_proptag (PidTagPrimarySmtpAddress);
+
+		if (value)
+			e_contact_set (contact, E_CONTACT_EMAIL_1, value);
+	}
+
+	#undef get_proptag
+	#undef get_str_proptag
+
+	return contact;
+}
+
+static uint32_t
+string_to_bin (TALLOC_CTX *mem_ctx, const gchar *str, uint8_t **lpb)
+{
+	uint32_t len, i;
+
+	g_return_val_if_fail (str != NULL, 0);
+	g_return_val_if_fail (lpb != NULL, 0);
+
+	len = strlen (str);
+	g_return_val_if_fail ((len & 1) == 0, 0);
+
+	len = len / 2;
+	*lpb = talloc_zero_array (mem_ctx, uint8_t, len);
+
+	i = 0;
+	while (*str && i < len) {
+		gchar c1 = str[0], c2 = str[1];
+		str += 2;
+
+		g_return_val_if_fail ((c1 >= '0' && c1 <= '9') || (c1 >= 'a' && c1 <= 'f') || (c1 >= 'A' && c1 <= 'F'), 0);
+		g_return_val_if_fail ((c2 >= '0' && c2 <= '9') || (c2 >= 'a' && c2 <= 'f') || (c2 >= 'A' && c2 <= 'F'), 0);
+
+		#define deHex(x) (((x) >= '0' && (x) <= '9') ? ((x) - '0') : (((x) >= 'a' && (x) <= 'f') ? (x) - 'a' + 10 : (x) - 'A' + 10))
+		(*lpb)[i] = (deHex (c1) << 4) | (deHex (c2));
+		#undef deHex
+		i++;
+	}
+
+	return len;
+}
+
+static gint
+cmp_member_id (gconstpointer a, gconstpointer b, gpointer ht)
+{
+	gchar *va, *vb;
+	gint res;
+
+	if (!a)
+		return b ? -1 : 0;
+	if (!b)
+		return 1;
+
+	va = e_vcard_attribute_get_value ((EVCardAttribute *) a);
+	vb = e_vcard_attribute_get_value ((EVCardAttribute *) b);
+
+	res = GPOINTER_TO_INT (g_hash_table_lookup (ht, va)) - GPOINTER_TO_INT (g_hash_table_lookup (ht, vb));
+
+	g_free (va);
+	g_free (vb);
+
+	return res;
+}
+
+gboolean
+e_mapi_book_utils_contact_to_object (EContact *contact,
+				     EContact *old_contact, /* can be NULL */
+				     EMapiObject **pobject,
+				     TALLOC_CTX *mem_ctx,
+				     GCancellable *cancellable,
+				     GError **perror)
+{
+	EMapiObject *object;
+
+	#define set_value(hex, val) G_STMT_START { \
+		if (!e_mapi_utils_add_property (&object->properties, hex, val, object)) \
+			return FALSE;	\
+		} G_STMT_END
+
+	#define set_con_value(hex, field_id) G_STMT_START { \
+		if (e_contact_get (contact, field_id)) { \
+			set_value (hex, e_contact_get (contact, field_id)); \
+		} } G_STMT_END
+
+	g_return_val_if_fail (contact != NULL, FALSE);
+	g_return_val_if_fail (mem_ctx != NULL, FALSE);
+	g_return_val_if_fail (pobject != NULL, FALSE);
+
+	object = e_mapi_object_new (mem_ctx);
+	*pobject = object;
+
+	if (GPOINTER_TO_INT (e_contact_get (contact, E_CONTACT_IS_LIST))) {
+		GList *local, *l;
+		struct BinaryArray_r *members, *oneoff_members;
+		uint32_t u32, crc32 = 0;
+		GHashTable *member_values = NULL, *member_ids = NULL;
+		GError *error = NULL;
+
+		if (!error && old_contact) {
+			member_values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+			member_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+			local = e_contact_get_attributes (old_contact, E_CONTACT_EMAIL);
+			for (l = local; l; l = l->next) {
+				EVCardAttribute *attr = l->data;
+				GList *param;
+
+				if (!attr)
+					continue;
+
+				param = e_vcard_attribute_get_param (attr, EMA_X_MEMBERVALUE);
+				if (param && param->data && !param->next) {
+					g_hash_table_insert (member_values, e_vcard_attribute_get_value (attr), g_strdup (param->data));
+				}
+
+				param = e_vcard_attribute_get_param (attr, EMA_X_MEMBERID);
+				if (param && param->data && !param->next) {
+					g_hash_table_insert (member_ids, e_vcard_attribute_get_value (attr), GINT_TO_POINTER (atoi (param->data)));
+				}
+			}
+
+			g_list_free_full (local, (GDestroyNotify) e_vcard_attribute_free);
+		}
+
+		if (error)
+			g_error_free (error);
+
+		set_value (PidTagMessageClass, IPM_DISTLIST);
+		u32 = 0xFFFFFFFF;
+		set_value (PidLidFileUnderId, &u32);
+		set_con_value (PidLidFileUnder, E_CONTACT_FILE_AS);
+		set_con_value (PidLidDistributionListName, E_CONTACT_FILE_AS);
+		set_con_value (PidTagDisplayName, E_CONTACT_FILE_AS);
+		set_con_value (PidTagNormalizedSubject, E_CONTACT_FILE_AS);
+
+		local = e_contact_get_attributes (contact, E_CONTACT_EMAIL);
+		if (member_ids)
+			local = g_list_sort_with_data (local, cmp_member_id, member_ids);
+
+		members = talloc_zero (mem_ctx, struct BinaryArray_r);
+		members->cValues = 0;
+		members->lpbin = talloc_zero_array (mem_ctx, struct Binary_r, g_list_length (local));
+
+		oneoff_members = talloc_zero (mem_ctx, struct BinaryArray_r);
+		oneoff_members->cValues = 0;
+		oneoff_members->lpbin = talloc_zero_array (mem_ctx, struct Binary_r, g_list_length (local));
+
+		for (l = local; l; l = l->next) {
+			EVCardAttribute *attr = (EVCardAttribute *) l->data;
+			gchar *raw;
+			CamelInternetAddress *addr;
+
+			if (!attr)
+				continue;
+
+			raw = e_vcard_attribute_get_value (attr);
+			if (!raw)
+				continue;
+
+			addr = camel_internet_address_new ();
+			if (camel_address_decode (CAMEL_ADDRESS (addr), raw) > 0) {
+				const gchar *nm = NULL, *eml = NULL;
+
+				camel_internet_address_get (addr, 0, &nm, &eml);
+				if (eml) {
+					/* keep both lists in sync */
+					if (member_values && g_hash_table_lookup (member_values, raw)) {
+						/* stored ListMembers values when contact's value didn't change */
+						members->lpbin[members->cValues].cb = string_to_bin (mem_ctx, g_hash_table_lookup (member_values, raw), &members->lpbin[members->cValues].lpb);
+						members->cValues++;
+					} else {
+						e_mapi_util_recip_entryid_generate_smtp (mem_ctx, &members->lpbin[members->cValues], nm ? nm : "", eml);
+						members->cValues++;
+					}
+
+					e_mapi_util_recip_entryid_generate_smtp (mem_ctx, &oneoff_members->lpbin[oneoff_members->cValues], nm ? nm : "", eml);
+					oneoff_members->cValues++;
+
+					crc32 = e_mapi_utils_push_crc32 (crc32, members->lpbin[members->cValues - 1].lpb, members->lpbin[members->cValues - 1].cb);
+				}
+			}
+
+			g_object_unref (addr);
+			g_free (raw);
+		}
+
+		if (member_values)
+			g_hash_table_destroy (member_values);
+		if (member_ids)
+			g_hash_table_destroy (member_ids);
+		g_list_foreach (local, (GFunc)e_vcard_attribute_free, NULL);
+		g_list_free (local);
+
+		set_value (PidLidDistributionListOneOffMembers, oneoff_members);
+		set_value (PidLidDistributionListMembers, members);
+		set_value (PidLidDistributionListChecksum, &crc32);
+
+		return TRUE;
+	}
+
+	set_value (PidTagMessageClass, IPM_CONTACT);
+	set_con_value (PidLidFileUnder, E_CONTACT_FILE_AS);
+
+	set_con_value (PidTagDisplayName, E_CONTACT_FULL_NAME);
+	set_con_value (PidTagNormalizedSubject, E_CONTACT_FILE_AS);
+	set_con_value (PidLidEmail1OriginalDisplayName, E_CONTACT_EMAIL_1);
+	/*set_con_value (PidLidEmail1EmailAddress, E_CONTACT_EMAIL_1);*/
+
+	/*set_con_value (0x8083001e, E_CONTACT_EMAIL_1);*/
+	set_con_value (PidLidEmail2EmailAddress, E_CONTACT_EMAIL_2);
+
+	set_con_value (PidLidEmail3EmailAddress, E_CONTACT_EMAIL_3);
+	/*set_con_value (PidLidEmail3OriginalDisplayName, E_CONTACT_EMAIL_3);*/
+
+	set_con_value (PidLidHtml, E_CONTACT_HOMEPAGE_URL);
+	set_con_value (PidLidFreeBusyLocation, E_CONTACT_FREEBUSY_URL);
+
+	set_con_value (PidTagBusinessTelephoneNumber, E_CONTACT_PHONE_BUSINESS);
+	set_con_value (PidTagHomeTelephoneNumber, E_CONTACT_PHONE_HOME);
+	set_con_value (PidTagMobileTelephoneNumber, E_CONTACT_PHONE_MOBILE);
+	set_con_value (PidTagHomeFaxNumber, E_CONTACT_PHONE_HOME_FAX);
+	set_con_value (PidTagBusinessFaxNumber, E_CONTACT_PHONE_BUSINESS_FAX);
+	set_con_value (PidTagPagerTelephoneNumber, E_CONTACT_PHONE_PAGER);
+	set_con_value (PidTagAssistantTelephoneNumber, E_CONTACT_PHONE_ASSISTANT);
+	set_con_value (PidTagCompanyMainTelephoneNumber, E_CONTACT_PHONE_COMPANY);
+
+	set_con_value (PidTagManagerName, E_CONTACT_MANAGER);
+	set_con_value (PidTagAssistant, E_CONTACT_ASSISTANT);
+	set_con_value (PidTagCompanyName, E_CONTACT_ORG);
+	set_con_value (PidTagDepartmentName, E_CONTACT_ORG_UNIT);
+	set_con_value (PidTagProfession, E_CONTACT_ROLE);
+	set_con_value (PidTagTitle, E_CONTACT_TITLE);
+
+	set_con_value (PidTagOfficeLocation, E_CONTACT_OFFICE);
+	set_con_value (PidTagSpouseName, E_CONTACT_SPOUSE);
+
+	set_con_value (PidTagBody, E_CONTACT_NOTE);
+	set_con_value (PidTagNickname, E_CONTACT_NICKNAME);
+
+	/* BDAY AND ANNV */
+	if (e_contact_get (contact, E_CONTACT_BIRTH_DATE)) {
+		EContactDate *date = e_contact_get (contact, E_CONTACT_BIRTH_DATE);
+		struct tm tmtime = { 0 };
+		struct FILETIME t;
+
+		tmtime.tm_mday = date->day;
+		tmtime.tm_mon = date->month - 1;
+		tmtime.tm_year = date->year - 1900;
+
+		e_mapi_util_time_t_to_filetime (mktime (&tmtime) + (24 * 60 * 60), &t);
+
+		set_value (PidTagBirthday, &t);
+	}
+
+	if (e_contact_get (contact, E_CONTACT_ANNIVERSARY)) {
+		EContactDate *date = e_contact_get (contact, E_CONTACT_ANNIVERSARY);
+		struct tm tmtime = { 0 };
+		struct FILETIME t;
+
+		tmtime.tm_mday = date->day;
+		tmtime.tm_mon = date->month - 1;
+		tmtime.tm_year = date->year - 1900;
+
+		e_mapi_util_time_t_to_filetime (mktime (&tmtime) + (24 * 60 * 60), &t);
+
+		set_value (PidTagWeddingAnniversary, &t);
+	}
+
+	/* Home and Office address */
+	if (e_contact_get (contact, E_CONTACT_ADDRESS_HOME)) {
+		EContactAddress *contact_addr = e_contact_get (contact, E_CONTACT_ADDRESS_HOME);
+
+		set_value (PidLidHomeAddress, contact_addr->street);
+		set_value (PidTagHomeAddressPostOfficeBox, contact_addr->ext);
+		set_value (PidTagHomeAddressCity, contact_addr->locality);
+		set_value (PidTagHomeAddressStateOrProvince, contact_addr->region);
+		set_value (PidTagHomeAddressPostalCode, contact_addr->code);
+		set_value (PidTagHomeAddressCountry, contact_addr->country);
+	}
+
+	if (e_contact_get (contact, E_CONTACT_ADDRESS_WORK)) {
+		EContactAddress *contact_addr = e_contact_get (contact, E_CONTACT_ADDRESS_WORK);
+
+		set_value (PidLidWorkAddress, contact_addr->street);
+		set_value (PidTagPostOfficeBox, contact_addr->ext);
+		set_value (PidTagLocality, contact_addr->locality);
+		set_value (PidTagStateOrProvince, contact_addr->region);
+		set_value (PidTagPostalCode, contact_addr->code);
+		set_value (PidTagCountry, contact_addr->country);
+	}
+
+	if (e_contact_get (contact, E_CONTACT_IM_AIM)) {
+		GList *l = e_contact_get (contact, E_CONTACT_IM_AIM);
+		set_value (PidLidInstantMessagingAddress, l->data);
+	}
+
+	#undef set_value
+
+	return TRUE;
+}
+
+gchar *
+e_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);
+}
+
+struct EMapiSExpParserData
+{
+	TALLOC_CTX *mem_ctx;
+	/* parser results in ints, indexes to res_parts */
+	GPtrArray *res_parts;
+};
+
+static ESExpResult *
+term_eval_and (struct _ESExp *f,
+	       gint argc,
+	       struct _ESExpResult **argv,
+	       gpointer user_data)
+{
+	struct EMapiSExpParserData *esp = user_data;
+	ESExpResult *r;
+	gint ii, jj, valid = 0;
+
+	r = e_sexp_result_new (f, ESEXP_RES_INT);
+	r->value.number = -1;
+
+	for (ii = 0; ii < argc; ii++) {
+		if (argv[ii]->type == ESEXP_RES_INT &&
+		    argv[ii]->value.number >= 0 && 
+		    argv[ii]->value.number < esp->res_parts->len) {
+			jj = argv[ii]->value.number;
+			valid++;
+		}
+	}
+
+	if (valid == 1) {
+		r->value.number = jj;
+	} else if (valid > 0) {
+		struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
+		g_return_val_if_fail (res != NULL, NULL);
+
+		res->rt = RES_AND;
+		res->res.resAnd.cRes = valid;
+		res->res.resAnd.res = talloc_zero_array (esp->mem_ctx, struct mapi_SRestriction_and, res->res.resAnd.cRes + 1);
+
+		jj = 0;
+
+		for (ii = 0; ii < argc; ii++) {
+			if (argv[ii]->type == ESEXP_RES_INT &&
+			    argv[ii]->value.number >= 0 && 
+			    argv[ii]->value.number < esp->res_parts->len) {
+				struct mapi_SRestriction *subres = g_ptr_array_index (esp->res_parts, argv[ii]->value.number);
+
+				res->res.resAnd.res[jj].rt = subres->rt;
+				res->res.resAnd.res[jj].res = subres->res;
+
+				jj++;
+			}
+		}
+
+		g_ptr_array_add (esp->res_parts, res);
+		r->value.number = esp->res_parts->len - 1;
+	}
+
+	return r;
+}
+
+static ESExpResult *
+term_eval_or (struct _ESExp *f,
+	      gint argc,
+	      struct _ESExpResult **argv,
+	      gpointer user_data)
+{
+	struct EMapiSExpParserData *esp = user_data;
+	ESExpResult *r;
+	gint ii, jj = -1, valid = 0;
+
+	r = e_sexp_result_new (f, ESEXP_RES_INT);
+	r->value.number = -1;
+
+	for (ii = 0; ii < argc; ii++) {
+		if (argv[ii]->type == ESEXP_RES_INT &&
+		    argv[ii]->value.number >= 0 && 
+		    argv[ii]->value.number < esp->res_parts->len) {
+			jj = argv[ii]->value.number;
+			valid++;
+		    }
+	}
+
+	if (valid == 1) {
+		r->value.number = jj;
+	} else if (valid > 0) {
+		struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
+		g_return_val_if_fail (res != NULL, NULL);
+
+		res->rt = RES_OR;
+		res->res.resOr.cRes = valid;
+		res->res.resOr.res = talloc_zero_array (esp->mem_ctx, struct mapi_SRestriction_or, res->res.resOr.cRes + 1);
+
+		jj = 0;
+
+		for (ii = 0; ii < argc; ii++) {
+			if (argv[ii]->type == ESEXP_RES_INT &&
+			    argv[ii]->value.number >= 0 && 
+			    argv[ii]->value.number < esp->res_parts->len) {
+				struct mapi_SRestriction *subres = g_ptr_array_index (esp->res_parts, argv[ii]->value.number);
+
+				res->res.resOr.res[jj].rt = subres->rt;
+				res->res.resOr.res[jj].res = subres->res;
+
+				jj++;
+			}
+		}
+
+		g_ptr_array_add (esp->res_parts, res);
+		r->value.number = esp->res_parts->len - 1;
+	}
+
+	return r;
+}
+
+static ESExpResult *
+term_eval_not (struct _ESExp *f,
+	       gint argc,
+	       struct _ESExpResult **argv,
+	       gpointer user_data)
+{
+	ESExpResult *r;
+
+	r = e_sexp_result_new (f, ESEXP_RES_INT);
+	r->value.number = -1;
+
+	#ifdef HAVE_RES_NOT_SUPPORTED
+	if (argc == 1 && argv[0]->type == ESEXP_RES_INT) {
+		struct EMapiSExpParserData *esp = user_data;
+		gint idx = argv[0]->value.number;
+
+		if (esp && idx >= 0 && idx < esp->res_parts->len) {
+			struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
+			g_return_val_if_fail (res != NULL, NULL);
+
+			res->rt = RES_NOT;
+			res->res.resNot.res = g_ptr_array_index (esp->res_parts, idx);
+
+			g_ptr_array_add (esp->res_parts, res);
+			r->value.number = esp->res_parts->len - 1;
+		}
+	}
+	#endif
+
+	return r;
+}
+
+static uint32_t
+get_proptag_from_field_name (const gchar *field_name, gboolean is_contact_field)
+{
+	EContactField cfid;
+	gint ii;
+
+	if (is_contact_field)
+		cfid = e_contact_field_id (field_name);
+	else
+		cfid = e_contact_field_id_from_vcard (field_name);
+
+	for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) {
+		if (mappings[ii].field_id == cfid) {
+			return mappings[ii].mapi_id;
+		}
+	}
+
+	return MAPI_E_RESERVED;
+}
+
+static ESExpResult *
+func_eval_text_compare (struct _ESExp *f,
+			gint argc,
+			struct _ESExpResult **argv,
+			gpointer user_data,
+			uint32_t fuzzy)
+{
+	struct EMapiSExpParserData *esp = user_data;
+	ESExpResult *r;
+
+	r = e_sexp_result_new (f, ESEXP_RES_INT);
+	r->value.number = -1;
+
+	if (argc == 2
+	    && argv[0]->type == ESEXP_RES_STRING
+	    && argv[1]->type == ESEXP_RES_STRING) {
+		const gchar *propname = argv[0]->value.string;
+		const gchar *propvalue = argv[1]->value.string;
+
+		if (propname && propvalue && g_ascii_strcasecmp (propname, "x-evolution-any-field") != 0) {
+			uint32_t proptag = get_proptag_from_field_name (propname, TRUE);
+
+			if (proptag != MAPI_E_RESERVED && ((proptag & 0xFFFF) == PT_UNICODE || (proptag & 0xFFFF) == PT_STRING8)) {
+				struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
+				g_return_val_if_fail (res != NULL, NULL);
+
+				res->rt = RES_CONTENT;
+				res->res.resContent.fuzzy = fuzzy | FL_IGNORECASE;
+				res->res.resContent.ulPropTag = proptag;
+				res->res.resContent.lpProp.ulPropTag = proptag;
+				res->res.resContent.lpProp.value.lpszW = talloc_strdup (esp->mem_ctx, propvalue);
+
+				g_ptr_array_add (esp->res_parts, res);
+				r->value.number = esp->res_parts->len - 1;
+			} else if (g_ascii_strcasecmp (propname, "email") == 0) {
+				uint32_t ii, jj;
+				const gchar *emails[] = {"email_1", "email_2", "email_3", NULL};
+				struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
+				g_return_val_if_fail (res != NULL, NULL);
+
+				res->rt = RES_OR;
+				res->res.resOr.cRes = 4;
+				res->res.resOr.res = talloc_zero_array (esp->mem_ctx, struct mapi_SRestriction_or, res->res.resOr.cRes + 1);
+
+				proptag = PidTagPrimarySmtpAddress;
+				res->res.resOr.res[0].rt = RES_CONTENT;
+				res->res.resOr.res[0].res.resContent.fuzzy = fuzzy | FL_IGNORECASE;
+				res->res.resOr.res[0].res.resContent.ulPropTag = proptag;
+				res->res.resOr.res[0].res.resContent.lpProp.ulPropTag = proptag;
+				res->res.resOr.res[0].res.resContent.lpProp.value.lpszW = talloc_strdup (esp->mem_ctx, propvalue);
+
+				for (ii = 1, jj = 0; emails[jj]; jj++) {
+					proptag = get_proptag_from_field_name (emails[jj], TRUE);
+					if (proptag == MAPI_E_RESERVED)
+						continue;
+
+					res->res.resOr.res[ii].rt = RES_CONTENT;
+					res->res.resOr.res[ii].res.resContent.fuzzy = fuzzy | FL_IGNORECASE;
+					res->res.resOr.res[ii].res.resContent.ulPropTag = proptag;
+					res->res.resOr.res[ii].res.resContent.lpProp.ulPropTag = proptag;
+					res->res.resOr.res[ii].res.resContent.lpProp.value.lpszW = talloc_strdup (esp->mem_ctx, propvalue);
+
+					ii++;
+				}
+
+				res->res.resOr.cRes = ii;
+
+				g_ptr_array_add (esp->res_parts, res);
+				r->value.number = esp->res_parts->len - 1;
+			}
+		}
+	}
+
+	return r;
+}
+
+static ESExpResult *
+func_eval_contains (struct _ESExp *f,
+		    gint argc,
+		    struct _ESExpResult **argv,
+		    gpointer user_data)
+{
+	return func_eval_text_compare (f, argc, argv, user_data, FL_SUBSTRING);
+}
+
+static ESExpResult *
+func_eval_is (struct _ESExp *f,
+	      gint argc,
+	      struct _ESExpResult **argv,
+	      gpointer user_data)
+{
+	return func_eval_text_compare (f, argc, argv, user_data, FL_FULLSTRING);
+}
+
+static ESExpResult *
+func_eval_beginswith (struct _ESExp *f,
+		      gint argc,
+		      struct _ESExpResult **argv,
+		      gpointer user_data)
+{
+	return func_eval_text_compare (f, argc, argv, user_data, FL_PREFIX);
+}
+
+static ESExpResult *
+func_eval_endswith (struct _ESExp *f,
+		    gint argc,
+		    struct _ESExpResult **argv,
+		    gpointer user_data)
+{
+	/* no suffix, thus at least substring is used */
+	return func_eval_text_compare (f, argc, argv, user_data, FL_SUBSTRING);
+}
+
+static ESExpResult *
+func_eval_field_exists (struct _ESExp *f,
+			gint argc,
+			struct _ESExpResult **argv,
+			gpointer user_data,
+			gboolean is_contact_field)
+{
+	struct EMapiSExpParserData *esp = user_data;
+	ESExpResult *r;
+
+	r = e_sexp_result_new (f, ESEXP_RES_INT);
+	r->value.number = -1;
+
+	if (argc == 1 && argv[0]->type == ESEXP_RES_STRING) {
+		const gchar *propname = argv[0]->value.string;
+		uint32_t proptag = get_proptag_from_field_name (propname, is_contact_field);
+
+		if (proptag != MAPI_E_RESERVED) {
+			struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
+			g_return_val_if_fail (res != NULL, NULL);
+
+			res->rt = RES_EXIST;
+			res->res.resExist.ulPropTag = proptag;
+
+			g_ptr_array_add (esp->res_parts, res);
+			r->value.number = esp->res_parts->len - 1;
+		} else if (g_ascii_strcasecmp (propname, "email") == 0) {
+			uint32_t ii, jj;
+			const gchar *emails[] = { "email_1", "email_2", "email_3", NULL };
+			struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
+			g_return_val_if_fail (res != NULL, NULL);
+
+			res->rt = RES_OR;
+			res->res.resOr.cRes = 4;
+			res->res.resOr.res = talloc_zero_array (esp->mem_ctx, struct mapi_SRestriction_or, res->res.resOr.cRes + 1);
+
+			res->res.resOr.res[0].rt = RES_EXIST;
+			res->res.resOr.res[0].res.resExist.ulPropTag = PidTagPrimarySmtpAddress;
+
+			for (ii = 1, jj = 0; emails[jj]; jj++) {
+				proptag = get_proptag_from_field_name ("email_1", TRUE);
+
+				if (proptag == MAPI_E_RESERVED)
+					continue;
+
+				res->res.resOr.res[ii].rt = RES_EXIST;
+				res->res.resOr.res[ii].res.resExist.ulPropTag = proptag;
+
+				ii++;
+			}
+
+			res->res.resOr.cRes = ii;
+
+			g_ptr_array_add (esp->res_parts, res);
+			r->value.number = esp->res_parts->len - 1;
+		}
+	}
+
+	return r;
+}
+
+static ESExpResult *
+func_eval_exists (struct _ESExp *f,
+		  gint argc,
+		  struct _ESExpResult **argv,
+		  gpointer user_data)
+{
+	return func_eval_field_exists (f, argc, argv, user_data, TRUE);
+}
+
+static ESExpResult *
+func_eval_exists_vcard (struct _ESExp *f,
+			gint argc,
+			struct _ESExpResult **argv,
+			gpointer user_data)
+{
+	return func_eval_field_exists (f, argc, argv, user_data, FALSE);
+}
+
+static struct mapi_SRestriction *
+mapi_book_utils_sexp_to_restriction (TALLOC_CTX *mem_ctx, const gchar *sexp_query)
+{
+	/* 'builtin' functions */
+	static const struct {
+		const gchar *name;
+		ESExpFunc *func;
+		gint type;		/* set to 1 if a function can perform shortcut evaluation, or
+					   doesn't execute everything, 0 otherwise */
+	} check_symbols[] = {
+		{ "and", 		term_eval_and,		0 },
+		{ "or", 		term_eval_or,		0 },
+		{ "not", 		term_eval_not,		0 },
+
+		{ "contains",		func_eval_contains,	0 },
+		{ "is",			func_eval_is,		0 },
+		{ "beginswith",		func_eval_beginswith,	0 },
+		{ "endswith",		func_eval_endswith,	0 },
+		{ "exists",		func_eval_exists,	0 },
+		{ "exists_vcard",	func_eval_exists_vcard,	0 }
+	};
+
+	gint i;
+	ESExp *sexp;
+	ESExpResult *r;
+	struct EMapiSExpParserData esp;
+	struct mapi_SRestriction *restriction;
+
+	g_return_val_if_fail (sexp_query != NULL, NULL);
+
+	esp.mem_ctx = mem_ctx;
+	sexp = e_sexp_new ();
+
+	for (i = 0; i < G_N_ELEMENTS (check_symbols); i++) {
+		if (check_symbols[i].type == 1) {
+			e_sexp_add_ifunction (sexp, 0, check_symbols[i].name,
+					      (ESExpIFunc *) check_symbols[i].func, &esp);
+		} else {
+			e_sexp_add_function (sexp, 0, check_symbols[i].name,
+					     check_symbols[i].func, &esp);
+		}
+	}
+
+	e_sexp_input_text (sexp, sexp_query, strlen (sexp_query));
+	if (e_sexp_parse (sexp) == -1) {
+		e_sexp_unref (sexp);
+		return NULL;
+	}
+
+	esp.res_parts = g_ptr_array_new ();
+	r = e_sexp_eval (sexp);
+
+	restriction = NULL;
+	if (r && r->type == ESEXP_RES_INT && r->value.number >= 0 && r->value.number < esp.res_parts->len)
+		restriction = g_ptr_array_index (esp.res_parts, r->value.number);
+
+	e_sexp_result_free (sexp, r);
+
+	e_sexp_unref (sexp);
+	g_ptr_array_free (esp.res_parts, TRUE);
+
+	return restriction;
+}
+
+gboolean
+e_mapi_book_utils_build_sexp_restriction (EMapiConnection *conn,
+					  TALLOC_CTX *mem_ctx,
+					  struct mapi_SRestriction **restrictions,
+					  gpointer user_data, /* const gchar *sexp */
+					  GCancellable *cancellable,
+					  GError **perror)
+{
+	const gchar *sexp = user_data;
+
+	g_return_val_if_fail (conn != NULL, FALSE);
+	g_return_val_if_fail (mem_ctx != NULL, FALSE);
+	g_return_val_if_fail (restrictions != NULL, FALSE);
+
+	if (!sexp || !*sexp)
+		*restrictions = NULL;
+	else
+		*restrictions = mapi_book_utils_sexp_to_restriction (mem_ctx, sexp);
+
+	return TRUE;
+}
+
+/* return with g_slist_free(), 'data' pointers (strings) are not newly allocated */
+GSList *
+e_mapi_book_utils_get_supported_contact_fields (void)
+{
+	gint ii;
+	GSList *fields = NULL;
+
+	for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) {
+		fields = g_slist_append (fields, (gpointer) e_contact_field_name (mappings[ii].field_id));
+	}
+
+	fields = g_slist_append (fields, (gpointer) e_contact_field_name (E_CONTACT_BOOK_URI));
+
+	return fields;
+}
+
+gboolean
+e_mapi_book_utils_get_supported_mapi_proptags (TALLOC_CTX *mem_ctx,
+					       struct SPropTagArray **propTagArray)
+{
+	gint ii;
+
+	g_return_val_if_fail (mem_ctx != NULL, FALSE);
+	g_return_val_if_fail (propTagArray != NULL, FALSE);
+
+	*propTagArray = set_SPropTagArray (mem_ctx, 1, PidTagObjectType);
+
+	for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) {
+		SPropTagArray_add (mem_ctx, *propTagArray, mappings[ii].mapi_id);
+	}
+
+	for (ii = 0; ii < G_N_ELEMENTS (extra_proptags); ii++) {
+		SPropTagArray_add (mem_ctx, *propTagArray, extra_proptags[ii]);
+	}
+
+	return TRUE;
+}
diff --git a/src/libexchangemapi/e-mapi-book-utils.h b/src/libexchangemapi/e-mapi-book-utils.h
new file mode 100644
index 0000000..6ec2d9c
--- /dev/null
+++ b/src/libexchangemapi/e-mapi-book-utils.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAPI_BOOK_UTILS_H
+#define E_MAPI_BOOK_UTILS_H
+
+#include <libebook/e-contact.h>
+
+#include <e-mapi-connection.h>
+#include <e-mapi-defs.h>
+#include <e-mapi-utils.h>
+
+/* vCard parameter name in contact list */
+#define EMA_X_MEMBERID "X-EMA-MEMBER-ID"
+#define EMA_X_MEMBERVALUE "X-EMA-MEMBER-VALUE"
+
+G_BEGIN_DECLS
+
+EContact *	e_mapi_book_utils_contact_from_object		(EMapiConnection *conn,
+								 EMapiObject *object,
+								 const gchar *book_uri);
+
+gboolean	e_mapi_book_utils_contact_to_object		(EContact *contact,
+								 EContact *old_contact, /* can be NULL */
+								 EMapiObject **pobject,
+								 TALLOC_CTX *mem_ctx,
+								 GCancellable *cancellable,
+								 GError **perror);
+
+/* converts time_t to string, suitable for E_CONTACT_REV field value;
+   free returned pointer with g_free() */
+gchar *		e_mapi_book_utils_timet_to_string		(time_t tt);
+
+/* converts sexp_query into mapi_SRestriction, which is completely
+   allocated on the given mem_ctx */
+gboolean	e_mapi_book_utils_build_sexp_restriction	(EMapiConnection *conn,
+								 TALLOC_CTX *mem_ctx,
+								 struct mapi_SRestriction **restrictions,
+								 gpointer user_data, /* const gchar *sexp */
+								 GCancellable *cancellable,
+								 GError **perror);
+
+GSList *	e_mapi_book_utils_get_supported_contact_fields	(void);
+
+gboolean	e_mapi_book_utils_get_supported_mapi_proptags	(TALLOC_CTX *mem_ctx,
+								 struct SPropTagArray **propTagArray);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libexchangemapi/e-mapi-connection.c b/src/libexchangemapi/e-mapi-connection.c
index b1d2f54..f35cf9a 100644
--- a/src/libexchangemapi/e-mapi-connection.c
+++ b/src/libexchangemapi/e-mapi-connection.c
@@ -38,15 +38,18 @@
 #include "e-mapi-connection.h"
 #include "e-mapi-folder.h"
 #include "e-mapi-utils.h"
+#include "e-mapi-book-utils.h"
 #include "e-mapi-mail-utils.h"
 #include "e-mapi-fast-transfer.h"
 #include "e-mapi-openchange.h"
 
-//#include <param.h>
 /* how many bytes can be written within one property with SetProps() call;
    if its size exceeds this limit, it's converted into an EMapiStreamedProp */
 #define MAX_PROPERTY_WRITE_SIZE	2048
 
+/* how may contacts in one chunk can GAL ask to fetch */
+#define MAX_GAL_CHUNK 50
+
 static void register_connection (EMapiConnection *conn);
 static void unregister_connection (EMapiConnection *conn);
 static gboolean mapi_profile_create (struct mapi_context *mapi_ctx, const EMapiProfileData *empd, mapi_profile_callback_t callback, gconstpointer data, GCancellable *cancellable, GError **perror, gboolean use_locking);
@@ -531,6 +534,7 @@ e_mapi_connection_new (const gchar *profile, const gchar *password, GCancellable
 
 	priv->profile = g_strdup (profile);
 	priv->has_public_store = FALSE;
+
 	UNLOCK ();
 
 	e_mapi_debug_print ("%s: %s: Connected ", G_STRLOC, G_STRFUNC);
@@ -649,84 +653,6 @@ may_skip_property (uint32_t proptag)
 }
 
 gboolean
-e_mapi_connection_fetch_gal (EMapiConnection *conn,
-			     BuildRestrictionsCB build_rs_cb,
-			     gpointer build_rs_cb_data,
-			     BuildReadPropsCB build_props,
-			     gpointer brp_data,
-			     FetchGALCallback cb,
-			     gpointer data,
-			     GCancellable *cancellable,
-			     GError **perror)
-{
-	struct SPropTagArray	*propsTagArray;
-	struct SRowSet		*aRowSet;
-	enum MAPISTATUS		ms;
-	uint32_t		i, count, n_rows = 0;
-	uint8_t			ulFlags;
-	TALLOC_CTX *mem_ctx;
-
-	CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
-	e_return_val_mapi_error_if_fail (build_props != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
-	e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
-
-	mem_ctx = talloc_new (priv->session);
-
-	LOCK ();
-
-	ms = GetGALTableCount (priv->session, &n_rows);
-	if (ms != MAPI_E_SUCCESS) {
-		make_mapi_error (perror, "GetGALTableCount", ms);
-		n_rows = 0;
-	}
-
-	propsTagArray = set_SPropTagArray (mem_ctx, 0x1, PR_MESSAGE_CLASS);
-	if (!build_props (conn, 0, mem_ctx, propsTagArray, brp_data, cancellable, perror)) {
-		make_mapi_error (perror, "build_props", MAPI_E_CALL_FAILED);
-		UNLOCK();
-		talloc_free (mem_ctx);
-		return FALSE;
-	}
-
-	ms = MAPI_E_SUCCESS;
-	count = 0;
-	ulFlags = TABLE_START;
-	while (ms == MAPI_E_SUCCESS) {
-		aRowSet = NULL;
-		/* fetch per 100 items */
-		ms = GetGALTable (priv->session, propsTagArray, &aRowSet, 100, ulFlags);
-		if ((!aRowSet) || (!(aRowSet->aRow)) || ms != MAPI_E_SUCCESS) {
-			break;
-		}
-		if (aRowSet->cRows) {
-			global_unlock ();
-			for (i = 0; i < aRowSet->cRows; i++, count++) {
-				if (!cb (conn, count, n_rows, &aRowSet->aRow[i], data, cancellable, perror)) {
-					ms = MAPI_E_RESERVED;
-					break;
-				}
-			}
-			global_lock ();
-		} else {
-			talloc_free (aRowSet);
-			break;
-		}
-
-		ulFlags = TABLE_CUR;
-		talloc_free (aRowSet);
-	}
-
-	talloc_free (mem_ctx);
-
-	UNLOCK ();
-
-	if (ms != MAPI_E_SUCCESS && ms != MAPI_E_RESERVED)
-		make_mapi_error (perror, "GetGALTable", ms);
-
-	return ms == MAPI_E_SUCCESS;
-}
-
-gboolean
 e_mapi_connection_get_public_folder (EMapiConnection *conn,
 				     mapi_object_t *obj_store,
 				     GCancellable *cancellable,
@@ -929,7 +855,7 @@ e_mapi_connection_get_folder_properties (EMapiConnection *conn,
 
 	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)) {
+		if (!brp_cb (conn, mem_ctx, spropTagArray, brp_cb_user_data, cancellable, perror)) {
 			goto cleanup;
 		}
 	} else {
@@ -941,7 +867,7 @@ e_mapi_connection_get_folder_properties (EMapiConnection *conn,
 	if (spropTagArray && spropTagArray->cValues) {
 		struct SPropValue *lpProps;
 		uint32_t prop_count = 0, k, ll;
-		ResolveNamedIDsData *named_ids_list = NULL;
+		EResolveNamedIDsData *named_ids_list = NULL;
 		guint named_ids_len = 0;
 
 		lpProps = talloc_zero (mem_ctx, struct SPropValue);
@@ -957,7 +883,7 @@ e_mapi_connection_get_folder_properties (EMapiConnection *conn,
 				g_debug ("%s: Cannot fetch property 0x%08x %s", G_STRFUNC, proptag, name);
 			} else if (((proptag >> 16) & 0xFFFF) >= 0x8000) {
 				if (!named_ids_list)
-					named_ids_list = g_new0 (ResolveNamedIDsData, spropTagArray->cValues - k + 1);
+					named_ids_list = g_new0 (EResolveNamedIDsData, spropTagArray->cValues - k + 1);
 				named_ids_list[named_ids_len].pidlid_propid = proptag;
 				named_ids_list[named_ids_len].propid = MAPI_E_RESERVED;
 				named_ids_len++;
@@ -1130,10 +1056,10 @@ list_objects_internal_cb (EMapiConnection *conn,
 			  GError **perror)
 {
 	struct ListObjectsInternalData *loi_data = user_data;
-	ListObjectsData lod;
+	ListObjectsData lod = { 0 };
 	const mapi_id_t	*pmid;
 	const gchar *msg_class;
-	const uint32_t *pmsg_flags;
+	const uint32_t *pmsg_flags, *pobj_type;
 	const struct FILETIME *last_modified;
 
 	CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
@@ -1141,12 +1067,14 @@ 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, PidTagMid);
-	msg_class = get_SPropValue_SRow_data (srow, PidTagMessageClass);
-	pmsg_flags = get_SPropValue_SRow_data (srow, PidTagMessageFlags);
-	last_modified = get_SPropValue_SRow_data (srow, PidTagLastModificationTime);
+	pmid = e_mapi_util_find_row_propval (srow, PidTagMid);
+	pobj_type = e_mapi_util_find_row_propval (srow, PidTagObjectType);
+	msg_class = e_mapi_util_find_row_propval (srow, PidTagMessageClass);
+	pmsg_flags = e_mapi_util_find_row_propval (srow, PidTagMessageFlags);
+	last_modified = e_mapi_util_find_row_propval (srow, PidTagLastModificationTime);
 
 	lod.mid = pmid ? *pmid : 0;
+	lod.obj_type = pobj_type ? *pobj_type : 0;
 	lod.msg_class = msg_class;
 	lod.msg_flags = pmsg_flags ? *pmsg_flags : 0;
 	lod.last_modified = last_modified ? e_mapi_util_filetime_to_time_t (last_modified) : 0;
@@ -1156,7 +1084,7 @@ list_objects_internal_cb (EMapiConnection *conn,
 
 static void
 maybe_add_named_id_tag (uint32_t proptag,
-			ResolveNamedIDsData **named_ids_list,
+			EResolveNamedIDsData **named_ids_list,
 			guint *named_ids_len)
 {
 	g_return_if_fail (named_ids_list != NULL);
@@ -1164,10 +1092,10 @@ maybe_add_named_id_tag (uint32_t proptag,
 
 	if (((proptag >> 16) & 0xFFFF) >= 0x8000) {
 		if (!*named_ids_list) {
-			*named_ids_list = g_new0 (ResolveNamedIDsData, 1);
+			*named_ids_list = g_new0 (EResolveNamedIDsData, 1);
 			*named_ids_len = 0;
 		} else {
-			*named_ids_list = g_renew (ResolveNamedIDsData, *named_ids_list, *named_ids_len + 1);
+			*named_ids_list = g_renew (EResolveNamedIDsData, *named_ids_list, *named_ids_len + 1);
 		}
 
 		(*named_ids_list)[*named_ids_len].pidlid_propid = proptag;
@@ -1178,7 +1106,7 @@ maybe_add_named_id_tag (uint32_t proptag,
 
 static void
 gather_mapi_SRestriction_named_ids (struct mapi_SRestriction *restriction,
-				    ResolveNamedIDsData **named_ids_list,
+				    EResolveNamedIDsData **named_ids_list,
 				    guint *named_ids_len)
 {
 	guint i;
@@ -1227,60 +1155,78 @@ gather_mapi_SRestriction_named_ids (struct mapi_SRestriction *restriction,
 	}
 }
 
+/* free returned pointer with g_hash_table_destroy */
+static GHashTable *
+prepare_maybe_replace_hash (const EResolveNamedIDsData *named_ids_list,
+			    guint named_ids_len)
+{
+	GHashTable *res;
+	gint ii;
+
+	if (!named_ids_list || !named_ids_len)
+		return NULL;
+
+	res = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+	for (ii = 0; ii < named_ids_len; ii++) {
+		uint32_t search_tag = named_ids_list[ii].pidlid_propid;
+		uint32_t replace_with = named_ids_list[ii].propid;
+
+		g_hash_table_insert (res, GUINT_TO_POINTER (search_tag), GUINT_TO_POINTER (replace_with));
+
+		search_tag = (search_tag & ~0xFFFF) | PT_ERROR;
+		replace_with = (replace_with & ~0xFFFF) | PT_ERROR;
+
+		g_hash_table_insert (res, GUINT_TO_POINTER (search_tag), GUINT_TO_POINTER (replace_with));
+	}
+
+	return res;
+}
+
 static void
 maybe_replace_named_id_tag (uint32_t *pproptag,
-			    const ResolveNamedIDsData *named_ids_list,
-			    guint named_ids_len)
+			    GHashTable *replace_hash)
 {
-	gint i;
+	gpointer key, value;
 
 	g_return_if_fail (pproptag != NULL);
-	g_return_if_fail (named_ids_list != NULL);
 
-	if ((((*pproptag) >> 16) & 0xFFFF) < 0x8000)
+	if (!replace_hash)
 		return;
 
-	for (i = 0; i < named_ids_len; i++) {
-		if ((*pproptag) == named_ids_list[i].pidlid_propid ||
-		    ((((*pproptag) & 0xFFFF) == PT_ERROR) &&
-			((*pproptag) & ~0xFFFF) == (named_ids_list[i].pidlid_propid & ~0xFFFF))) {
-			(*pproptag) = ((*pproptag) & 0xFFFF) | (named_ids_list[i].propid & ~0xFFFF);
-			break;
-		}
-	}
+	if (g_hash_table_lookup_extended (replace_hash, GUINT_TO_POINTER (*pproptag), &key, &value))
+		*pproptag = GPOINTER_TO_UINT (value);
 }
 
 static void
 replace_mapi_SRestriction_named_ids (struct mapi_SRestriction *restriction,
-				     const ResolveNamedIDsData *named_ids_list,
-				     guint named_ids_len)
+				     GHashTable *replace_hash)
 {
 	guint i;
 	uint32_t proptag;
 
 	g_return_if_fail (restriction != NULL);
-	g_return_if_fail (named_ids_list != NULL);
 
-	#define check_proptag(x) {								\
-			proptag = x;								\
-			maybe_replace_named_id_tag (&proptag, named_ids_list, named_ids_len);	\
-			x = proptag;								\
+	#define check_proptag(x) {						\
+			proptag = x;						\
+			maybe_replace_named_id_tag (&proptag, replace_hash);	\
+			x = proptag;						\
 		}
 
 	switch (restriction->rt) {
 	case RES_AND:
 		for (i = 0; i < restriction->res.resAnd.cRes; i++) {
-			replace_mapi_SRestriction_named_ids ((struct mapi_SRestriction *) &(restriction->res.resAnd.res[i]), named_ids_list, named_ids_len);
+			replace_mapi_SRestriction_named_ids ((struct mapi_SRestriction *) &(restriction->res.resAnd.res[i]), replace_hash);
 		}
 		break;
 	case RES_OR:
 		for (i = 0; i < restriction->res.resOr.cRes; i++) {
-			replace_mapi_SRestriction_named_ids ((struct mapi_SRestriction *) &(restriction->res.resOr.res[i]), named_ids_list, named_ids_len);
+			replace_mapi_SRestriction_named_ids ((struct mapi_SRestriction *) &(restriction->res.resOr.res[i]), replace_hash);
 		}
 		break;
 	#ifdef HAVE_RES_NOT_SUPPORTED
 	case RES_NOT:
-		replace_mapi_SRestriction_named_ids (restriction->res.resNot.res, named_ids_list, named_ids_len);
+		replace_mapi_SRestriction_named_ids (restriction->res.resNot.res, replace_hash);
 		break;
 	#endif
 	case RES_CONTENT:
@@ -1316,7 +1262,7 @@ change_mapi_SRestriction_named_ids (EMapiConnection *conn,
 				    GCancellable *cancellable,
 				    GError **perror)
 {
-	ResolveNamedIDsData *named_ids_list = NULL;
+	EResolveNamedIDsData *named_ids_list = NULL;
 	guint named_ids_len = 0;
 	gboolean res = FALSE;
 
@@ -1332,8 +1278,14 @@ change_mapi_SRestriction_named_ids (EMapiConnection *conn,
 
 	res = e_mapi_connection_resolve_named_props (conn, mapi_object_get_id (obj_folder), named_ids_list, named_ids_len, cancellable, perror);
 
-	if (res)
-		replace_mapi_SRestriction_named_ids (restrictions, named_ids_list, named_ids_len);
+	if (res) {
+		GHashTable *replace_hash = prepare_maybe_replace_hash (named_ids_list, named_ids_len);
+
+		if (replace_hash) {
+			replace_mapi_SRestriction_named_ids (restrictions, replace_hash);
+			g_hash_table_destroy (replace_hash);
+		}
+	}
 
 	g_free (named_ids_list);
 
@@ -1378,8 +1330,9 @@ e_mapi_connection_list_objects (EMapiConnection *conn,
 		goto cleanup;
 	}
 
-	propTagArray = set_SPropTagArray (mem_ctx, 0x4,
+	propTagArray = set_SPropTagArray (mem_ctx, 0x5,
 					  PidTagMid,
+					  PidTagObjectType,
 					  PidTagMessageClass,
 					  PidTagMessageFlags,
 					  PidTagLastModificationTime);
@@ -2474,7 +2427,7 @@ convert_mapi_props_to_props (EMapiConnection *conn,
 			     GError **perror)
 {
 	uint16_t ii;
-	ResolveNamedIDsData *named_ids_list = NULL;
+	EResolveNamedIDsData *named_ids_list = NULL;
 	guint named_ids_len = 0;
 	gboolean res = TRUE;
 
@@ -2576,23 +2529,31 @@ convert_mapi_props_to_props (EMapiConnection *conn,
 	}
 
 	if (named_ids_list) {
+		GHashTable *replace_hash = NULL;
+
 		res = e_mapi_connection_resolve_named_props (conn, mapi_object_get_id (obj_folder), named_ids_list, named_ids_len, cancellable, perror);
 
-		if (res && *props) {
+		if (res)
+			replace_hash = prepare_maybe_replace_hash (named_ids_list, named_ids_len);
+
+		if (replace_hash && *props) {
 			for (ii = 0; ii < *propslen; ii++) {
 				uint32_t proptag = (*props)[ii].ulPropTag;
 
-				maybe_replace_named_id_tag (&proptag, named_ids_list, named_ids_len);
+				maybe_replace_named_id_tag (&proptag, replace_hash);
 
 				(*props)[ii].ulPropTag = proptag;
 			}
 		}
 
-		if (res && streams) {
+		if (replace_hash && streams) {
 			for (ii = 0; ii < *streamslen; ii++) {
-				maybe_replace_named_id_tag (&((*streams)[ii].proptag), named_ids_list, named_ids_len);
+				maybe_replace_named_id_tag (&((*streams)[ii].proptag), replace_hash);
 			}
 		}
+
+		if (replace_hash)
+			g_hash_table_destroy (replace_hash);
 	}
 
 	g_free (named_ids_list);
@@ -2834,7 +2795,7 @@ add_object_recipients (EMapiConnection *conn,
 	struct SPropTagArray *tags;
 	struct SRowSet *rows = NULL;
 	struct PropertyTagArray_r *flagList = NULL;
-	ResolveNamedIDsData *named_ids_list = NULL;
+	EResolveNamedIDsData *named_ids_list = NULL;
 	guint named_ids_len = 0;
 	const gchar **users = NULL;
 	EMapiRecipient *recipient;
@@ -2902,19 +2863,26 @@ add_object_recipients (EMapiConnection *conn,
 	}
 
 	if (named_ids_list) {
+		GHashTable *replace_hash;
+
 		if (!e_mapi_connection_resolve_named_props (conn, mapi_object_get_id (obj_folder), named_ids_list, named_ids_len, cancellable, perror)) {
 			ms = MAPI_E_CALL_FAILED;
 			make_mapi_error (perror, "e_mapi_connection_resolve_named_props", ms);
 			goto cleanup;
 		}
 
-		for (ii = 0; ii < tags->cValues; ii++) {
+		replace_hash = prepare_maybe_replace_hash (named_ids_list, named_ids_len);
+
+		for (ii = 0; ii < tags->cValues && replace_hash; ii++) {
 			uint32_t proptag = tags->aulPropTag[ii];
 
-			maybe_replace_named_id_tag (&proptag, named_ids_list, named_ids_len);
+			maybe_replace_named_id_tag (&proptag, replace_hash);
 
 			tags->aulPropTag[ii] = proptag;
 		}
+
+		if (replace_hash)
+			g_hash_table_destroy (replace_hash);
 	}
 
 	ms = ResolveNames (priv->session, users, tags, &rows, &flagList, MAPI_UNICODE);
@@ -3387,6 +3355,615 @@ e_mapi_connection_modify_object (EMapiConnection *conn,
 	return ms == MAPI_E_SUCCESS;
 }
 
+static void
+convert_mapi_SRestriction_to_Restriction_r (struct mapi_SRestriction *restriction,
+					    struct Restriction_r *rr,
+					    TALLOC_CTX *mem_ctx,
+					    GHashTable *replace_hash)
+{
+	guint i;
+	uint32_t proptag;
+
+	g_return_if_fail (restriction != NULL);
+	g_return_if_fail (rr != NULL);
+	g_return_if_fail (mem_ctx != NULL);
+
+	#define copy(x, y) rr->res.x = restriction->res.y
+	#define copy_prop(pprop, mprop)	{							\
+			rr->res.pprop = talloc_zero (mem_ctx, struct SPropValue);		\
+			g_return_if_fail (rr->res.pprop != NULL);				\
+			rr->res.pprop->ulPropTag = restriction->res.mprop.ulPropTag;		\
+			rr->res.pprop->dwAlignPad = 0;						\
+			cast_SPropValue (mem_ctx, &(restriction->res.mprop), rr->res.pprop);	\
+		}
+	#define check_proptag(x) {								\
+			proptag = x;								\
+			maybe_replace_named_id_tag (&proptag, replace_hash);			\
+			/* workaround for unresolved properties */				\
+			if (proptag == MAPI_E_RESERVED)						\
+				proptag = PidTagDisplayName;					\
+			x = proptag;								\
+		}
+
+	rr->rt = restriction->rt;
+
+	switch (restriction->rt) {
+	case RES_AND:
+		rr->res.resAnd.lpRes = talloc_zero_array (mem_ctx, struct Restriction_r, restriction->res.resAnd.cRes);
+		g_return_if_fail (rr->res.resAnd.lpRes != NULL);
+
+		copy (resAnd.cRes, resAnd.cRes);
+		for (i = 0; i < restriction->res.resAnd.cRes; i++) {
+			convert_mapi_SRestriction_to_Restriction_r (
+				(struct mapi_SRestriction *) &(restriction->res.resAnd.res[i]),
+				&(rr->res.resAnd.lpRes[i]),
+				mem_ctx, replace_hash);
+		}
+		break;
+	case RES_OR:
+		rr->res.resOr.lpRes = talloc_zero_array (mem_ctx, struct Restriction_r, restriction->res.resOr.cRes);
+		g_return_if_fail (rr->res.resOr.lpRes != NULL);
+
+		copy (resOr.cRes, resOr.cRes);
+		for (i = 0; i < restriction->res.resOr.cRes; i++) {
+			convert_mapi_SRestriction_to_Restriction_r (
+				(struct mapi_SRestriction *) &(restriction->res.resOr.res[i]),
+				&(rr->res.resOr.lpRes[i]),
+				mem_ctx, replace_hash);
+		}
+		break;
+	#ifdef HAVE_RES_NOT_SUPPORTED
+	case RES_NOT:
+		rr->res.resNot.lpRes = talloc_zero (mem_ctx, struct Restriction_r);
+		g_return_if_fail (r->res.resNot.lpRes != NULL);
+
+		convert_mapi_SRestriction_to_Restriction_r (
+			restriction->res.resNot.res,
+			rr->res.resNot.lpRes,
+			mem_ctx, replace_hash);
+		break;
+	#endif
+	case RES_CONTENT:
+		copy (resContent.ulFuzzyLevel, resContent.fuzzy);
+		copy (resContent.ulPropTag, resContent.ulPropTag);
+		copy_prop (resContent.lpProp, resContent.lpProp);
+
+		check_proptag (rr->res.resContent.ulPropTag);
+		check_proptag (rr->res.resContent.lpProp->ulPropTag);
+		break;
+	case RES_PROPERTY:
+		copy (resProperty.relop, resProperty.relop);
+		copy (resProperty.ulPropTag, resProperty.ulPropTag);
+		copy_prop (resProperty.lpProp, resProperty.lpProp);
+
+
+		check_proptag (rr->res.resProperty.ulPropTag);
+		check_proptag (rr->res.resProperty.lpProp->ulPropTag);
+		break;
+	case RES_COMPAREPROPS:
+		copy (resCompareProps.relop, resCompareProps.relop);
+		copy (resCompareProps.ulPropTag1, resCompareProps.ulPropTag1);
+		copy (resCompareProps.ulPropTag2, resCompareProps.ulPropTag2);
+
+		check_proptag (rr->res.resCompareProps.ulPropTag1);
+		check_proptag (rr->res.resCompareProps.ulPropTag2);
+		break;
+	case RES_BITMASK:
+		copy (resBitMask.relMBR, resBitmask.relMBR);
+		copy (resBitMask.ulPropTag, resBitmask.ulPropTag);
+		copy (resBitMask.ulMask, resBitmask.ulMask);
+
+		check_proptag (rr->res.resBitMask.ulPropTag);
+		break;
+	case RES_SIZE:
+		copy (resSize.relop, resSize.relop);
+		copy (resSize.ulPropTag, resSize.ulPropTag);
+		copy (resSize.cb, resSize.size);
+
+		check_proptag (rr->res.resSize.ulPropTag);
+		break;
+	case RES_EXIST:
+		rr->res.resExist.ulReserved1 = 0;
+		rr->res.resExist.ulReserved2 = 0;
+		copy (resExist.ulPropTag, resExist.ulPropTag);
+
+		check_proptag (rr->res.resExist.ulPropTag);
+		break;
+	}
+
+	#undef check_proptag
+	#undef copy_prop
+	#undef copy
+}
+
+static enum MAPISTATUS
+process_gal_rows_chunk (EMapiConnection *conn,
+			TALLOC_CTX *mem_ctx,
+			uint32_t rows_offset,
+			uint32_t rows_total,
+			struct SRowSet *rows,
+			struct PropertyTagArray_r *mids,
+			ForeachTableRowCB cb,
+			gpointer user_data,
+			GCancellable *cancellable,
+			GError **perror)
+{
+	enum MAPISTATUS ms = MAPI_E_SUCCESS;
+	uint32_t ii;
+
+	e_return_val_mapi_error_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
+	e_return_val_mapi_error_if_fail (rows != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
+	e_return_val_mapi_error_if_fail (mids != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
+	e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
+	e_return_val_mapi_error_if_fail (rows->cRows <= mids->cValues, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
+
+	for (ii = 0; ii < rows->cRows; ii++) {
+		struct SRow *row = &rows->aRow[ii];
+		int64_t mid = mids->aulPropTag[ii];
+
+		/* add the temporary mid as a PidTagMid */
+		if (!e_mapi_utils_add_spropvalue (mem_ctx, &row->lpProps, &row->cValues, PidTagMid, &mid)) {
+			ms = MAPI_E_CALL_FAILED;
+			make_mapi_error (perror, "e_mapi_utils_add_spropvalue", ms);
+			break;
+		}
+
+		if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
+			ms = MAPI_E_USER_CANCEL;
+			break;
+		}
+
+		if (!cb (conn, mem_ctx, row, rows_offset + ii + 1, rows_total, user_data, cancellable, perror)) {
+			ms = MAPI_E_USER_CANCEL;
+			break;
+		}
+	}
+
+	return ms;
+}
+
+static enum MAPISTATUS
+foreach_gal_tablerow (EMapiConnection *conn,
+		      TALLOC_CTX *mem_ctx,
+		      struct SRowSet *first_rows,
+		      struct PropertyTagArray_r *all_mids,
+		      struct SPropTagArray *propTagArray,
+		      ForeachTableRowCB cb,
+		      gpointer user_data,
+		      GCancellable *cancellable,
+		      GError **perror)
+{
+	enum MAPISTATUS ms;
+	struct SRowSet *rows = NULL;
+	struct PropertyTagArray_r *to_query = NULL;
+	uint32_t  midspos;
+
+	CHECK_CORRECT_CONN_AND_GET_PRIV (conn, MAPI_E_INVALID_PARAMETER);
+	e_return_val_mapi_error_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
+	e_return_val_mapi_error_if_fail (first_rows != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
+	e_return_val_mapi_error_if_fail (all_mids != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
+	e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
+	e_return_val_mapi_error_if_fail (first_rows->cRows <= all_mids->cValues, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
+
+	midspos = 0;
+	ms = process_gal_rows_chunk (conn, mem_ctx, midspos, all_mids->cValues, first_rows, all_mids, cb, user_data, cancellable, perror);
+	if (ms != MAPI_E_SUCCESS) {
+		make_mapi_error (perror, "process_gal_rows_chunk", ms);
+		goto cleanup;
+	}
+
+	midspos = first_rows->cRows;
+	to_query = talloc_zero (mem_ctx, struct PropertyTagArray_r);
+	to_query->aulPropTag = talloc_zero_array (mem_ctx, uint32_t, MAX_GAL_CHUNK);
+
+	while (midspos < all_mids->cValues) {
+		uint32_t ii;
+
+		to_query->cValues = 0;
+		for (ii = midspos; to_query->cValues < MAX_GAL_CHUNK && ii < all_mids->cValues; to_query->cValues++, ii++) {
+			to_query->aulPropTag[to_query->cValues] = all_mids->aulPropTag[ii];
+		}
+
+		if (!to_query->cValues)
+			break;
+
+		if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
+			ms = MAPI_E_USER_CANCEL;
+			break;
+		}
+
+		ms = nspi_QueryRows (priv->session->nspi->ctx, mem_ctx, propTagArray, to_query, to_query->cValues, &rows);
+		if (ms != MAPI_E_SUCCESS) {
+			make_mapi_error (perror, "nspi_QueryRows", ms);
+			goto cleanup;
+		}
+
+		if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
+			ms = MAPI_E_USER_CANCEL;
+			break;
+		}
+
+		if (!rows || rows->cRows <= 0) {
+			/* success or finished, probably */
+			break;
+		}
+
+		ms = process_gal_rows_chunk (conn, mem_ctx, midspos, all_mids->cValues, rows, to_query, cb, user_data, cancellable, perror);
+		if (ms != MAPI_E_SUCCESS) {
+			make_mapi_error (perror, "process_gal_rows_chunk", ms);
+			goto cleanup;
+		}
+
+		midspos += rows->cRows;
+		talloc_free (rows);
+		rows = NULL;
+	}
+
+ cleanup:
+	talloc_free (to_query);
+	talloc_free (rows);
+
+	return ms;
+}
+
+gboolean
+e_mapi_connection_count_gal_objects (EMapiConnection *conn,
+				     guint32 *obj_total,
+				     GCancellable *cancellable,
+				     GError **perror)
+{
+	enum MAPISTATUS ms;
+	uint32_t count = 0;
+
+	CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
+	e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
+	e_return_val_mapi_error_if_fail (priv->session->nspi != NULL, MAPI_E_UNCONFIGURED, FALSE);
+	e_return_val_mapi_error_if_fail (priv->session->nspi->ctx != NULL, MAPI_E_UNCONFIGURED, FALSE);
+	e_return_val_mapi_error_if_fail (obj_total != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
+
+	*obj_total = 0;
+
+	LOCK ();
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
+		ms = MAPI_E_USER_CANCEL;
+	} else {
+		ms = GetGALTableCount (priv->session, &count);
+		if (ms != MAPI_E_SUCCESS) {
+			make_mapi_error (perror, "GetGALTableCount", ms);
+		} else {
+			*obj_total = count;
+		}
+	}
+
+	UNLOCK ();
+
+	return ms == MAPI_E_SUCCESS;
+}
+
+gboolean
+e_mapi_connection_list_gal_objects (EMapiConnection *conn,
+				    BuildRestrictionsCB build_rs_cb,
+				    gpointer build_rs_cb_data,
+				    ListObjectsCB cb,
+				    gpointer user_data,
+				    GCancellable *cancellable,
+				    GError **perror)
+{
+	enum MAPISTATUS ms;
+	TALLOC_CTX *mem_ctx;
+	struct SPropTagArray *propTagArray = NULL;
+	struct Restriction_r *use_restriction = NULL;
+	struct SRowSet *rows = NULL;
+	struct PropertyTagArray_r *pMIds = NULL;
+	struct ListObjectsInternalData loi_data;
+
+	CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
+	e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
+	e_return_val_mapi_error_if_fail (priv->session->nspi != NULL, MAPI_E_UNCONFIGURED, FALSE);
+	e_return_val_mapi_error_if_fail (priv->session->nspi->ctx != NULL, MAPI_E_UNCONFIGURED, FALSE);
+	e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
+
+	LOCK ();
+	mem_ctx = talloc_new (priv->session);
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
+		ms = MAPI_E_USER_CANCEL;
+		goto cleanup;
+	}
+
+	propTagArray = set_SPropTagArray (mem_ctx, 4,
+					  PidTagObjectType,
+					  PidTagMessageClass,
+					  PidTagMessageFlags,
+					  PidTagLastModificationTime);
+
+	if (build_rs_cb) {
+		struct mapi_SRestriction *restrictions = NULL;
+
+		if (!build_rs_cb (conn, mem_ctx, &restrictions, build_rs_cb_data, cancellable, perror)) {
+			ms = MAPI_E_CALL_FAILED;
+			make_mapi_error (perror, "build_restrictions", ms);
+			goto cleanup;
+		}
+
+		if (restrictions) {
+			EResolveNamedIDsData *named_ids_list = NULL;
+			guint named_ids_len = 0;
+			gboolean res = FALSE;
+
+			gather_mapi_SRestriction_named_ids (restrictions, &named_ids_list, &named_ids_len);
+
+			if (named_ids_list) {
+				/* use 0 for GAL as a folder ID parameter */
+				res = e_mapi_connection_resolve_named_props (conn, 0, named_ids_list, named_ids_len, cancellable, perror);
+
+				if (res) {
+					GHashTable *replace_hash = prepare_maybe_replace_hash (named_ids_list, named_ids_len);
+
+					use_restriction = talloc_zero (mem_ctx, struct Restriction_r);
+					convert_mapi_SRestriction_to_Restriction_r (restrictions, use_restriction, mem_ctx, replace_hash);
+
+					if (replace_hash)
+						g_hash_table_destroy (replace_hash);
+				} else {
+					ms = MAPI_E_CALL_FAILED;
+					goto cleanup;
+				}
+
+				g_free (named_ids_list);
+			} else {
+				use_restriction = talloc_zero (mem_ctx, struct Restriction_r);
+				convert_mapi_SRestriction_to_Restriction_r (restrictions, use_restriction, mem_ctx, NULL);
+			}
+
+			if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
+				ms = MAPI_E_USER_CANCEL;
+				goto cleanup;
+			}
+		}
+	}
+
+	ms = nspi_GetMatches (priv->session->nspi->ctx, mem_ctx, propTagArray, use_restriction, &rows, &pMIds);
+	if (ms != MAPI_E_SUCCESS || !rows) {
+		if (ms == MAPI_E_NOT_FOUND || (!rows && ms == MAPI_E_SUCCESS))
+			ms = MAPI_E_SUCCESS;
+		else if (ms != MAPI_E_SUCCESS)
+			make_mapi_error (perror, "nspi_GetMatches", ms);
+		goto cleanup;
+	}
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
+		ms = MAPI_E_USER_CANCEL;
+		goto cleanup;
+	}
+
+	loi_data.cb = cb;
+	loi_data.user_data = user_data;
+
+	ms = foreach_gal_tablerow (conn, mem_ctx, rows, pMIds, propTagArray, list_objects_internal_cb, &loi_data, cancellable, perror);
+	if (ms != MAPI_E_SUCCESS) {
+		make_mapi_error (perror, "foreach_gal_tablerow", ms);
+		goto cleanup;
+	}
+
+ cleanup:
+	talloc_free (pMIds);
+	talloc_free (rows);
+	talloc_free (propTagArray);
+	talloc_free (mem_ctx);
+	UNLOCK ();
+
+	return ms == MAPI_E_SUCCESS;
+}
+
+struct TransferGALObjectData
+{
+	GHashTable *reverse_replace_hash;
+	TransferObjectCB cb;
+	gpointer cb_user_data;
+};
+
+static gboolean
+e_mapi_transfer_gal_objects_cb (EMapiConnection *conn,
+				TALLOC_CTX *mem_ctx,
+				struct SRow *srow,
+				guint32 row_index,
+				guint32 rows_total,
+				gpointer user_data,
+				GCancellable *cancellable,
+				GError **perror)
+{
+	struct TransferGALObjectData *tgo = user_data;
+	EMapiObject *object;
+	uint32_t ii;
+	gboolean res;
+
+	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 (tgo != NULL, FALSE);
+	g_return_val_if_fail (tgo->cb != NULL, FALSE);
+
+	object = e_mapi_object_new (mem_ctx);
+
+	res = TRUE;
+
+	for (ii = 0; ii < srow->cValues; ii++) {
+		uint32_t proptag = srow->lpProps[ii].ulPropTag;
+		gconstpointer propdata = get_SPropValue_data (&srow->lpProps[ii]);
+
+		if (!propdata || may_skip_property (srow->lpProps[ii].ulPropTag))
+			continue;
+
+		/* reverse_replace_hash has them stored in opposite,
+		   the key is the name-id-proptag as stored on the server,
+		   the value is a pidlid/pidname proptag */
+		maybe_replace_named_id_tag (&proptag, tgo->reverse_replace_hash);
+
+		if (!e_mapi_utils_add_property (&object->properties, proptag, propdata, object)) {
+			res = FALSE;
+			make_mapi_error (perror, "e_mapi_utils_add_property", MAPI_E_CALL_FAILED);
+			break;
+		}
+	}
+
+	if (res)
+		res = tgo->cb (conn, mem_ctx, object, row_index, rows_total, tgo->cb_user_data, cancellable, perror);
+
+	e_mapi_object_free (object);
+
+	return res;
+}
+
+static void
+fill_reverse_replace_hash (gpointer key,
+			   gpointer value,
+			   gpointer user_data)
+{
+	GHashTable *reverse_replace_hash = user_data;
+
+	g_return_if_fail (reverse_replace_hash != NULL);
+
+	g_hash_table_insert (reverse_replace_hash, value, key);
+}
+
+gboolean
+e_mapi_connection_transfer_gal_objects (EMapiConnection *conn,
+					const GSList *mids,
+					TransferObjectCB cb,
+					gpointer cb_user_data,
+					GCancellable *cancellable,
+					GError **perror)
+{
+	enum MAPISTATUS ms;
+	TALLOC_CTX *mem_ctx;
+	struct PropertyTagArray_r *ids = NULL;
+	struct SPropTagArray *propTagArray = NULL;
+	struct SRowSet rows;
+	struct TransferGALObjectData tgo;
+	GHashTable *reverse_replace_hash = NULL;
+	EResolveNamedIDsData *named_ids_list = NULL;
+	guint named_ids_len = 0, ii;
+	const GSList *iter;
+
+	CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
+	e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
+	e_return_val_mapi_error_if_fail (priv->session->nspi != NULL, MAPI_E_UNCONFIGURED, FALSE);
+	e_return_val_mapi_error_if_fail (priv->session->nspi->ctx != NULL, MAPI_E_UNCONFIGURED, FALSE);
+	e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
+
+	LOCK ();
+	mem_ctx = talloc_new (priv->session);
+
+	for (iter = mids; iter; iter = iter->next) {
+		mapi_id_t *pmid = iter->data;
+
+		if (pmid) {
+			if (!ids) {
+				ids = talloc_zero (mem_ctx, struct PropertyTagArray_r);
+			}
+			ids->cValues++;
+			ids->aulPropTag = talloc_realloc (mem_ctx,
+				ids->aulPropTag,
+				uint32_t,
+				ids->cValues + 1);
+			ids->aulPropTag[ids->cValues - 1] = (uint32_t) (*pmid);
+			ids->aulPropTag[ids->cValues] = 0;
+		}
+	}
+
+	if (!ids) {
+		ms = MAPI_E_INVALID_PARAMETER;
+		make_mapi_error (perror, "gather valid mids", ms);
+		goto cleanup;
+	}
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
+		ms = MAPI_E_USER_CANCEL;
+		goto cleanup;
+	}
+
+	if (!e_mapi_book_utils_get_supported_mapi_proptags (mem_ctx, &propTagArray) || !propTagArray) {
+		ms = MAPI_E_CALL_FAILED;
+		make_mapi_error (perror, "e_mapi_book_utils_get_supported_mapi_proptags", ms);
+		goto cleanup;
+	}
+
+	for (ii = 0; ii < propTagArray->cValues; ii++) {
+		maybe_add_named_id_tag (propTagArray->aulPropTag[ii], &named_ids_list, &named_ids_len);
+	}
+
+	if (named_ids_list) {
+		GHashTable *replace_hash;
+
+		if (!e_mapi_connection_resolve_named_props (conn, 0, named_ids_list, named_ids_len, cancellable, perror)) {
+			ms = MAPI_E_CALL_FAILED;
+			make_mapi_error (perror, "e_mapi_connection_resolve_named_props", ms);
+			goto cleanup;
+		}
+
+		replace_hash = prepare_maybe_replace_hash (named_ids_list, named_ids_len);
+
+		if (replace_hash) {
+			for (ii = 0; ii < propTagArray->cValues; ii++) {
+				uint32_t proptag = propTagArray->aulPropTag[ii];
+
+				maybe_replace_named_id_tag (&proptag, replace_hash);
+
+				propTagArray->aulPropTag[ii] = proptag;
+			}
+
+			reverse_replace_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+			g_hash_table_foreach (replace_hash, fill_reverse_replace_hash, reverse_replace_hash);
+			g_hash_table_destroy (replace_hash);
+		}
+	}
+
+	/* fake rows, to start reading from the first mid */
+	rows.cRows = 0;
+	rows.aRow = NULL;
+
+	tgo.cb = cb;
+	tgo.cb_user_data = cb_user_data;
+	tgo.reverse_replace_hash = reverse_replace_hash;
+
+	ms = foreach_gal_tablerow (conn, mem_ctx, &rows, ids, propTagArray, e_mapi_transfer_gal_objects_cb, &tgo, cancellable, perror);
+	if (ms != MAPI_E_SUCCESS) {
+		make_mapi_error (perror, "foreach_gal_tablerow", ms);
+		goto cleanup;
+	}
+
+ cleanup:
+	if (reverse_replace_hash)
+		g_hash_table_destroy (reverse_replace_hash);
+	talloc_free (propTagArray);
+	talloc_free (ids);
+	talloc_free (mem_ctx);
+	UNLOCK ();
+
+	return ms == MAPI_E_SUCCESS;
+}
+
+gboolean
+e_mapi_connection_transfer_gal_object (EMapiConnection *conn,
+				       mapi_id_t message_id,
+				       TransferObjectCB cb,
+				       gpointer cb_user_data,
+				       GCancellable *cancellable,
+				       GError **perror)
+{
+	GSList *mids;
+	gboolean res;
+
+	mids = g_slist_append (NULL, &message_id);
+	res = e_mapi_connection_transfer_gal_objects (conn, mids, cb, cb_user_data, cancellable, perror);
+	g_slist_free (mids);
+
+	return res;
+}
+
 mapi_id_t
 e_mapi_connection_create_folder (EMapiConnection *conn,
 				 uint32_t olFolder,
@@ -3775,11 +4352,12 @@ e_mapi_connection_move_folder  (EMapiConnection *conn,
 	return result;
 }
 
-/* named_ids_list contains pointers to ResolveNamedIDsData structure */
+/* named_ids_list contains pointers to EResolveNamedIDsData structure;
+   fid 0 is reserved for lookup in GAL */
 gboolean
 e_mapi_connection_resolve_named_props  (EMapiConnection *conn,
 					mapi_id_t fid,
-					ResolveNamedIDsData *named_ids_list,
+					EResolveNamedIDsData *named_ids_list,
 					guint named_ids_n_elems,
 					GCancellable *cancellable,
 					GError **perror)
@@ -3807,7 +4385,7 @@ e_mapi_connection_resolve_named_props  (EMapiConnection *conn,
 
 		if (ids) {
 			for (i = 0; i < named_ids_n_elems; i++) {
-				ResolveNamedIDsData *data = &named_ids_list[i];
+				EResolveNamedIDsData *data = &named_ids_list[i];
 				uint32_t propid;
 
 				propid = GPOINTER_TO_UINT (g_hash_table_lookup (ids, GUINT_TO_POINTER (data->pidlid_propid)));
@@ -3834,10 +4412,17 @@ e_mapi_connection_resolve_named_props  (EMapiConnection *conn,
 	nameid = mapi_nameid_new (mem_ctx);
 	SPropTagArray = talloc_zero (mem_ctx, struct SPropTagArray);
 
-	/* Attempt to open the folder */
-	ms = open_folder (conn, 0, &fid, 0, &obj_folder, perror);
-	if (ms != MAPI_E_SUCCESS) {
-		goto cleanup;
+	if (fid) {
+		/* Attempt to open the folder */
+		ms = open_folder (conn, 0, &fid, 0, &obj_folder, perror);
+		if (ms != MAPI_E_SUCCESS) {
+			goto cleanup;
+		}
+	} else {
+		if (!priv->session->nspi || !priv->session->nspi->ctx) {
+			ms = MAPI_E_UNCONFIGURED;
+			goto cleanup;
+		}
 	}
 
 	if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
@@ -3853,7 +4438,7 @@ e_mapi_connection_resolve_named_props  (EMapiConnection *conn,
 	}
 
 	for (i = 0; i < todo->len; i++) {
-		ResolveNamedIDsData *data = todo->pdata[i];
+		EResolveNamedIDsData *data = todo->pdata[i];
 
 		if (mapi_nameid_canonical_add (nameid, data->pidlid_propid) != MAPI_E_SUCCESS)
 			data->propid = MAPI_E_RESERVED;
@@ -3866,7 +4451,88 @@ e_mapi_connection_resolve_named_props  (EMapiConnection *conn,
 		goto cleanup;
 	}
 
-	ms = mapi_nameid_GetIDsFromNames (nameid, &obj_folder, SPropTagArray);
+	if (fid) {
+		ms = mapi_nameid_GetIDsFromNames (nameid, &obj_folder, SPropTagArray);
+	} else {
+		/* lookup in GAL */
+		struct SPropTagArray *gal_tags;
+		uint32_t prop_count = nameid->count;
+		struct PropertyName_r *names = talloc_zero_array (mem_ctx, struct PropertyName_r, prop_count + 1);
+
+		g_assert (names != NULL);
+
+		SPropTagArray = talloc_zero (mem_ctx, struct SPropTagArray);
+		g_assert (SPropTagArray != NULL);
+
+		SPropTagArray->cValues = nameid->count;
+		SPropTagArray->aulPropTag = talloc_zero_array (mem_ctx, enum MAPITAGS, SPropTagArray->cValues + 1);
+		g_assert (SPropTagArray->aulPropTag != NULL);
+
+		j = 0;
+		for (i = 0; i < nameid->count; i++) {
+			guint ab[16];
+
+			SPropTagArray->aulPropTag[i] = nameid->entries[i].proptag;
+
+			if (nameid->entries[i].ulKind == MNID_ID &&
+			    16 == sscanf (nameid->entries[i].OLEGUID,
+					"%02x%02x%02x%02x-"
+					"%02x%02x-"
+					"%02x%02x-"
+					"%02x%02x-"
+					"%02x%02x%02x%02x%02x%02x",
+					&ab[0], &ab[1], &ab[2], &ab[3],
+					&ab[4], &ab[5],
+					&ab[6], &ab[7],
+					&ab[8], &ab[9],
+					&ab[10], &ab[11], &ab[12], &ab[13], &ab[14], &ab[15])) {
+				gint k;
+
+				names[j].ulReserved = 0;
+				names[j].lID = nameid->entries[i].lid;
+				names[j].lpguid = talloc_zero (mem_ctx, struct FlatUID_r);
+
+				for (k = 0; k < 16; k++) {
+					names[j].lpguid->ab[k] = (ab[k] & 0xFF);
+				}
+
+				j++;
+			} else {
+				SPropTagArray->aulPropTag[i] = (SPropTagArray->aulPropTag[i] & (~0xFFFF)) | PT_ERROR;
+				prop_count--;
+			}
+		}
+
+		if (prop_count > 0) {
+			ms = nspi_GetIDsFromNames (priv->session->nspi->ctx, mem_ctx, false, prop_count, names, &gal_tags);
+			if (ms == MAPI_E_SUCCESS && gal_tags) {
+				if (gal_tags->cValues != prop_count)
+					g_warning ("%s: Requested (%d) and returned (%d) property names don't match", G_STRFUNC, prop_count, gal_tags->cValues);
+
+				j = 0;
+				for (i = 0; i < gal_tags->cValues; i++) {
+					while (j < SPropTagArray->cValues && (SPropTagArray->aulPropTag[j] & 0xFFFF) == PT_ERROR) {
+						j++;
+					}
+
+					if (j >= SPropTagArray->cValues)
+						break;
+
+					SPropTagArray->aulPropTag[j] = gal_tags->aulPropTag[i];
+				}
+
+				while (j < SPropTagArray->cValues) {
+					SPropTagArray->aulPropTag[j] = (SPropTagArray->aulPropTag[j] & (~0xFFFF)) | PT_ERROR;
+					j++;
+				}
+			}
+		} else {
+			ms = MAPI_E_NOT_FOUND;
+		}
+
+		talloc_free (names);
+	}
+
 	if (ms != MAPI_E_SUCCESS) {
 		make_mapi_error (perror, "mapi_nameid_GetIDsFromNames", ms);
 		goto cleanup;
@@ -3879,7 +4545,7 @@ e_mapi_connection_resolve_named_props  (EMapiConnection *conn,
 
 	for (i = 0, j = 0; i < SPropTagArray->cValues && j < todo->len; i++) {
 		while (j < todo->len) {
-			ResolveNamedIDsData *data = todo->pdata[j];
+			EResolveNamedIDsData *data = todo->pdata[j];
 			if (data && data->propid == 0) {
 				if ((SPropTagArray->aulPropTag[i] & 0xFFFF) == PT_ERROR)
 					data->propid = MAPI_E_RESERVED;
@@ -3906,7 +4572,7 @@ e_mapi_connection_resolve_named_props  (EMapiConnection *conn,
 		}
 
 		for (i = 0; i < todo->len; i++) {
-			ResolveNamedIDsData *data = todo->pdata[i];
+			EResolveNamedIDsData *data = todo->pdata[i];
 
 			g_hash_table_insert (ids, GUINT_TO_POINTER (data->pidlid_propid), GUINT_TO_POINTER (data->propid));
 		}
diff --git a/src/libexchangemapi/e-mapi-connection.h b/src/libexchangemapi/e-mapi-connection.h
index 2c9b6ea..348c0a3 100644
--- a/src/libexchangemapi/e-mapi-connection.h
+++ b/src/libexchangemapi/e-mapi-connection.h
@@ -23,7 +23,7 @@
  */
 
 #ifndef E_MAPI_CONNECTION_H
-#define E_MAPI_CONNECTION_H 
+#define E_MAPI_CONNECTION_H
 
 #include <glib.h>
 #include <glib-object.h>
@@ -64,13 +64,14 @@ typedef enum {
 typedef struct {
 	uint32_t pidlid_propid; /* PidLid or PidName legacy property named ID to resolve */
 	uint32_t propid;	/* resolved prop ID; equals to MAPI_E_RESERVED when not found or other error */
-} ResolveNamedIDsData;
+} EResolveNamedIDsData;
 
 typedef struct {
-	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 */
+	mapi_id_t mid;		/* message ID, from PidTagMid */
+	uint32_t obj_type;	/* PidTagObjectType */
+	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;
@@ -123,15 +124,7 @@ typedef enum {
 } EMapiCreateFlags;
 
 /* callbacks return whether to continue in transfer of the next object */
-typedef gboolean (*FetchGALCallback)		(EMapiConnection *conn,
-						 uint32_t row_index,
-						 uint32_t n_rows,
-						 struct SRow *aRow,
-						 gpointer data,
-						 GCancellable *cancellable,
-						 GError **perror);
 typedef gboolean (*BuildReadPropsCB)		(EMapiConnection *conn,
-						 mapi_id_t fid,
 						 TALLOC_CTX *mem_ctx,
 						 struct SPropTagArray *props,
 						 gpointer data,
@@ -285,13 +278,30 @@ gboolean		e_mapi_connection_modify_object		(EMapiConnection *conn,
 								 GCancellable *cancellable,
 								 GError **perror);
 
-gboolean		e_mapi_connection_fetch_gal		(EMapiConnection *conn,
+gboolean		e_mapi_connection_count_gal_objects	(EMapiConnection *conn,
+								 guint32 *obj_total,
+								 GCancellable *cancellable,
+								 GError **perror);
+
+gboolean		e_mapi_connection_list_gal_objects	(EMapiConnection *conn,
 								 BuildRestrictionsCB build_rs_cb,
 								 gpointer build_rs_cb_data,
-								 BuildReadPropsCB build_props,
-								 gpointer brp_data,
-								 FetchGALCallback cb,
-								 gpointer data,
+								 ListObjectsCB cb,
+								 gpointer user_data,
+								 GCancellable *cancellable,
+								 GError **perror);
+
+gboolean		e_mapi_connection_transfer_gal_objects	(EMapiConnection *conn,
+								 const GSList *mids,
+								 TransferObjectCB cb,
+								 gpointer cb_user_data,
+								 GCancellable *cancellable,
+								 GError **perror);
+
+gboolean		e_mapi_connection_transfer_gal_object	(EMapiConnection *conn,
+								 mapi_id_t message_id,
+								 TransferObjectCB cb,
+								 gpointer cb_user_data,
 								 GCancellable *cancellable,
 								 GError **perror);
 
@@ -384,7 +394,7 @@ GSList *		e_mapi_connection_peek_folders_list	(EMapiConnection *conn);
 
 gboolean		e_mapi_connection_resolve_named_props	(EMapiConnection *conn,
 								 mapi_id_t fid,
-								 ResolveNamedIDsData *named_ids_list,
+								 EResolveNamedIDsData *named_ids_list,
 								 guint named_ids_n_elems,
 								 GCancellable *cancellable,
 								 GError **perror);
diff --git a/src/libexchangemapi/e-mapi-debug.c b/src/libexchangemapi/e-mapi-debug.c
index a715895..b9cb8ad 100644
--- a/src/libexchangemapi/e-mapi-debug.c
+++ b/src/libexchangemapi/e-mapi-debug.c
@@ -110,603 +110,618 @@ dump_bin (const uint8_t *bin, uint32_t bin_sz, gint indent)
 }
 
 static const gchar *
-get_namedid_name (EMapiConnection *conn, mapi_id_t fid, uint32_t proptag)
+get_namedid_name (uint32_t proptag)
 {
-	if (conn)
-		proptag = e_mapi_connection_unresolve_proptag_to_nameid (conn, fid, proptag);
+	static GHashTable *names = NULL;
 
 	if (proptag == MAPI_E_RESERVED)
 		return NULL;
 
-	switch (proptag) {
-	#define cs(x) case x: return #x;
-	cs (PidLidAddressBookProviderArrayType)
-	cs (PidLidAddressBookProviderEmailList)
-	cs (PidLidAddressCountryCode)
-	cs (PidLidAnniversaryEventEntryId)
-	cs (PidLidAutoLog)
-	cs (PidLidBirthdayEventEntryId)
-	cs (PidLidBirthdayLocal)
-	cs (PidLidBusinessCardCardPicture)
-	cs (PidLidBusinessCardDisplayDefinition)
-	cs (PidLidContactCharacterSet)
-	cs (PidLidContactItemData)
-	cs (PidLidContactUserField1)
-	cs (PidLidContactUserField2)
-	cs (PidLidContactUserField3)
-	cs (PidLidContactUserField4)
-	cs (PidLidDepartment)
-	cs (PidLidDistributionListChecksum)
-	cs (PidLidDistributionListMembers)
-	cs (PidLidDistributionListName)
-	cs (PidLidDistributionListOneOffMembers)
-	cs (PidLidDistributionListStream)
-	cs (PidLidEmail1AddressType)
-	cs (PidLidEmail1DisplayName)
-	cs (PidLidEmail1EmailAddress)
-	cs (PidLidEmail1OriginalDisplayName)
-	cs (PidLidEmail1OriginalEntryId)
-	cs (PidLidEmail1RichTextFormat)
-	cs (PidLidEmail2AddressType)
-	cs (PidLidEmail2DisplayName)
-	cs (PidLidEmail2EmailAddress)
-	cs (PidLidEmail2OriginalDisplayName)
-	cs (PidLidEmail2OriginalEntryId)
-	cs (PidLidEmail2RichTextFormat)
-	cs (PidLidEmail3AddressType)
-	cs (PidLidEmail3DisplayName)
-	cs (PidLidEmail3EmailAddress)
-	cs (PidLidEmail3OriginalDisplayName)
-	cs (PidLidEmail3OriginalEntryId)
-	cs (PidLidEmail3RichTextFormat)
-	cs (PidLidEmailList)
-	cs (PidLidFax1AddressType)
-	cs (PidLidFax1EmailAddress)
-	cs (PidLidFax1OriginalDisplayName)
-	cs (PidLidFax1OriginalEntryId)
-	cs (PidLidFax2AddressType)
-	cs (PidLidFax2EmailAddress)
-	cs (PidLidFax2OriginalDisplayName)
-	cs (PidLidFax2OriginalEntryId)
-	cs (PidLidFax3AddressType)
-	cs (PidLidFax3EmailAddress)
-	cs (PidLidFax3OriginalDisplayName)
-	cs (PidLidFax3OriginalEntryId)
-	cs (PidLidFileUnder)
-	cs (PidLidFileUnderId)
-	cs (PidLidFileUnderList)
-	cs (PidLidFreeBusyLocation)
-	cs (PidLidHasPicture)
-	cs (PidLidHomeAddress)
-	cs (PidLidHomeAddressCountryCode)
-	cs (PidLidHtml)
-	cs (PidLidInstantMessagingAddress)
-	cs (PidLidOtherAddress)
-	cs (PidLidOtherAddressCountryCode)
-	cs (PidLidPostalAddressId)
-	cs (PidLidReferredBy)
-	cs (PidLidWeddingAnniversaryLocal)
-	cs (PidLidWorkAddress)
-	cs (PidLidWorkAddressCity)
-	cs (PidLidWorkAddressCountry)
-	cs (PidLidWorkAddressCountryCode)
-	cs (PidLidWorkAddressPostalCode)
-	cs (PidLidWorkAddressPostOfficeBox)
-	cs (PidLidWorkAddressState)
-	cs (PidLidYomiCompanyName)
-	cs (PidLidYomiFirstName)
-	cs (PidLidYomiLastName)
-	cs (PidLidAllAttendeesString)
-	cs (PidLidAllowExternalCheck)
-	cs (PidLidAppointmentAuxiliaryFlags)
-	cs (PidLidAppointmentColor)
-	cs (PidLidAppointmentCounterProposal)
-	cs (PidLidAppointmentDuration)
-	cs (PidLidAppointmentEndDate)
-	cs (PidLidAppointmentEndTime)
-	cs (PidLidAppointmentEndWhole)
-	cs (PidLidAppointmentLastSequence)
-	cs (PidLidAppointmentNotAllowPropose)
-	cs (PidLidAppointmentProposalNumber)
-	cs (PidLidAppointmentProposedDuration)
-	cs (PidLidAppointmentProposedEndWhole)
-	cs (PidLidAppointmentProposedStartWhole)
-	cs (PidLidAppointmentRecur)
-	cs (PidLidAppointmentReplyName)
-	cs (PidLidAppointmentReplyTime)
-	cs (PidLidAppointmentSequence)
-	cs (PidLidAppointmentSequenceTime)
-	cs (PidLidAppointmentStartDate)
-	cs (PidLidAppointmentStartTime)
-	cs (PidLidAppointmentStartWhole)
-	cs (PidLidAppointmentStateFlags)
-	cs (PidLidAppointmentSubType)
-	cs (PidLidAppointmentTimeZoneDefinitionEndDisplay)
-	cs (PidLidAppointmentTimeZoneDefinitionRecur)
-	cs (PidLidAppointmentTimeZoneDefinitionStartDisplay)
-	cs (PidLidAppointmentUnsendableRecipients)
-	cs (PidLidAppointmentUpdateTime)
-	cs (PidLidAutoFillLocation)
-	cs (PidLidAutoStartCheck)
-	cs (PidLidBusyStatus)
-	cs (PidLidCcAttendeesString)
-	cs (PidLidChangeHighlight)
-	cs (PidLidClipEnd)
-	cs (PidLidClipStart)
-	cs (PidLidCollaborateDoc)
-	cs (PidLidConferencingCheck)
-	cs (PidLidConferencingType)
-	cs (PidLidDirectory)
-	cs (PidLidExceptionReplaceTime)
-	cs (PidLidFExceptionalAttendees)
-	cs (PidLidFExceptionalBody)
-	cs (PidLidFInvited)
-	cs (PidLidForwardInstance)
-	cs (PidLidForwardNotificationRecipients)
-	cs (PidLidFOthersAppointment)
-	cs (PidLidInboundICalStream)
-	cs (PidLidIntendedBusyStatus)
-	cs (PidLidLinkedTaskItems)
-	cs (PidLidLocation)
-	cs (PidLidMeetingWorkspaceUrl)
-	cs (PidLidNetShowUrl)
-	cs (PidLidOnlinePassword)
-	cs (PidLidOrganizerAlias)
-	cs (PidLidOriginalStoreEntryId)
-	cs (PidLidOwnerName)
-	cs (PidLidRecurrencePattern)
-	cs (PidLidRecurrenceType)
-	cs (PidLidRecurring)
-	cs (PidLidResponseStatus)
-	cs (PidLidSingleBodyICal)
-	cs (PidLidTimeZoneDescription)
-	cs (PidLidTimeZoneStruct)
-	cs (PidLidToAttendeesString)
-	cs (PidLidClientIntent)
-	cs (PidLidServerProcessed)
-	cs (PidLidServerProcessingActions)
-	cs (PidLidAgingDontAgeMe)
-	cs (PidLidAutoProcessState)
-	cs (PidLidBilling)
-	cs (PidLidClassification)
-	cs (PidLidClassificationDescription)
-	cs (PidLidClassificationGuid)
-	cs (PidLidClassificationKeep)
-	cs (PidLidClassified)
-	cs (PidLidCommonEnd)
-	cs (PidLidCommonStart)
-	cs (PidLidCompanies)
-	cs (PidLidContactLinkEntry)
-	cs (PidLidContactLinkName)
-	cs (PidLidContactLinkSearchKey)
-	cs (PidLidContacts)
-	cs (PidLidConversationActionLastAppliedTime)
-	cs (PidLidConversationActionMaxDeliveryTime)
-	cs (PidLidConversationActionMoveFolderEid)
-	cs (PidLidConversationActionMoveStoreEid)
-	cs (PidLidConversationActionVersion)
-	cs (PidLidConversationProcessed)
-	cs (PidLidCurrentVersion)
-	cs (PidLidCurrentVersionName)
-	cs (PidLidDayOfMonth)
-	cs (PidLidFlagRequest)
-	cs (PidLidFlagString)
-	cs (PidLidICalendarDayOfWeekMask)
-	cs (PidLidInfoPathFormName)
-	cs (PidLidInternetAccountName)
-	cs (PidLidInternetAccountStamp)
-	cs (PidLidMonthOfYear)
-	cs (PidLidNoEndDateFlag)
-	cs (PidLidNonSendableBcc)
-	cs (PidLidNonSendableCc)
-	cs (PidLidNonSendableTo)
-	cs (PidLidNonSendBccTrackStatus)
-	cs (PidLidNonSendCcTrackStatus)
-	cs (PidLidNonSendToTrackStatus)
-	cs (PidLidOccurrences)
-	cs (PidLidPrivate)
-	cs (PidLidPromptSendUpdate)
-	cs (PidLidRecurrenceDuration)
-	cs (PidLidReferenceEntryId)
-	cs (PidLidReminderDelta)
-	cs (PidLidReminderFileParameter)
-	cs (PidLidReminderOverride)
-	cs (PidLidReminderPlaySound)
-	cs (PidLidReminderSet)
-	cs (PidLidReminderSignalTime)
-	cs (PidLidReminderTime)
-	cs (PidLidReminderTimeDate)
-	cs (PidLidReminderTimeTime)
-	cs (PidLidReminderType)
-	cs (PidLidRemoteStatus)
-	cs (PidLidSideEffects)
-	cs (PidLidSmartNoAttach)
-	cs (PidLidSpamOriginalFolder)
-	cs (PidLidTaskGlobalId)
-	cs (PidLidTaskMode)
-	cs (PidLidToDoOrdinalDate)
-	cs (PidLidToDoSubOrdinal)
-	cs (PidLidToDoTitle)
-	cs (PidLidUseTnef)
-	cs (PidLidValidFlagStringProof)
-	cs (PidLidVerbResponse)
-	cs (PidLidVerbStream)
-	cs (PidLidLogDocumentPosted)
-	cs (PidLidLogDocumentPrinted)
-	cs (PidLidLogDocumentRouted)
-	cs (PidLidLogDocumentSaved)
-	cs (PidLidLogDuration)
-	cs (PidLidLogEnd)
-	cs (PidLidLogFlags)
-	cs (PidLidLogStart)
-	cs (PidLidLogType)
-	cs (PidLidLogTypeDesc)
-	cs (PidLidAppointmentMessageClass)
-	cs (PidLidAttendeeCriticalChange)
-	cs (PidLidCalendarType)
-	cs (PidLidCleanGlobalObjectId)
-	cs (PidLidDayInterval)
-	cs (PidLidDelegateMail)
-	cs (PidLidEndRecurrenceDate)
-	cs (PidLidEndRecurrenceTime)
-	cs (PidLidGlobalObjectId)
-	cs (PidLidIsException)
-	cs (PidLidIsRecurring)
-	cs (PidLidIsSilent)
-	cs (PidLidMeetingType)
-	cs (PidLidMonthInterval)
-	cs (PidLidMonthOfYearMask)
-	cs (PidLidOldLocation)
-	cs (PidLidOldRecurrenceType)
-	cs (PidLidOldWhenEndWhole)
-	cs (PidLidOldWhenStartWhole)
-	cs (PidLidOptionalAttendees)
-	cs (PidLidOwnerCriticalChange)
-	cs (PidLidRequiredAttendees)
-	cs (PidLidResourceAttendees)
-	cs (PidLidStartRecurrenceDate)
-	cs (PidLidStartRecurrenceTime)
-	cs (PidLidTimeZone)
-	cs (PidLidWeekInterval)
-	cs (PidLidWhere)
-	cs (PidLidYearInterval)
-	cs (PidLidNoteColor)
-	cs (PidLidNoteHeight)
-	cs (PidLidNoteWidth)
-	cs (PidLidNoteX)
-	cs (PidLidNoteY)
-	cs (PidLidPostRssChannel)
-	cs (PidLidPostRssChannelLink)
-	cs (PidLidPostRssItemGuid)
-	cs (PidLidPostRssItemHash)
-	cs (PidLidPostRssItemLink)
-	cs (PidLidPostRssItemXml)
-	cs (PidLidPostRssSubscription)
-	cs (PidLidSharingAnonymity)
-	cs (PidLidSharingBindingEntryId)
-	cs (PidLidSharingBrowseUrl)
-	cs (PidLidSharingCapabilities)
-	cs (PidLidSharingConfigurationUrl)
-	cs (PidLidSharingDataRangeEnd)
-	cs (PidLidSharingDataRangeStart)
-	cs (PidLidSharingDetail)
-	cs (PidLidSharingExtensionXml)
-	cs (PidLidSharingFilter)
-	cs (PidLidSharingFlags)
-	cs (PidLidSharingFlavor)
-	cs (PidLidSharingFolderEntryId)
-	cs (PidLidSharingIndexEntryId)
-	cs (PidLidSharingInitiatorEntryId)
-	cs (PidLidSharingInitiatorName)
-	cs (PidLidSharingInitiatorSmtp)
-	cs (PidLidSharingInstanceGuid)
-	cs (PidLidSharingLastAutoSyncTime)
-	cs (PidLidSharingLastSyncTime)
-	cs (PidLidSharingLocalComment)
-	cs (PidLidSharingLocalLastModificationTime)
-	cs (PidLidSharingLocalName)
-	cs (PidLidSharingLocalPath)
-	cs (PidLidSharingLocalStoreUid)
-	cs (PidLidSharingLocalType)
-	cs (PidLidSharingLocalUid)
-	cs (PidLidSharingOriginalMessageEntryId)
-	cs (PidLidSharingParentBindingEntryId)
-	cs (PidLidSharingParticipants)
-	cs (PidLidSharingPermissions)
-	cs (PidLidSharingProviderExtension)
-	cs (PidLidSharingProviderGuid)
-	cs (PidLidSharingProviderName)
-	cs (PidLidSharingProviderUrl)
-	cs (PidLidSharingRangeEnd)
-	cs (PidLidSharingRangeStart)
-	cs (PidLidSharingReciprocation)
-	cs (PidLidSharingRemoteByteSize)
-	cs (PidLidSharingRemoteComment)
-	cs (PidLidSharingRemoteCrc)
-	cs (PidLidSharingRemoteLastModificationTime)
-	cs (PidLidSharingRemoteMessageCount)
-	cs (PidLidSharingRemoteName)
-	cs (PidLidSharingRemotePass)
-	cs (PidLidSharingRemotePath)
-	cs (PidLidSharingRemoteStoreUid)
-	cs (PidLidSharingRemoteType)
-	cs (PidLidSharingRemoteUid)
-	cs (PidLidSharingRemoteUser)
-	cs (PidLidSharingRemoteVersion)
-	cs (PidLidSharingResponseTime)
-	cs (PidLidSharingResponseType)
-	cs (PidLidSharingRoamLog)
-	cs (PidLidSharingStart)
-	cs (PidLidSharingStatus)
-	cs (PidLidSharingStop)
-	cs (PidLidSharingSyncFlags)
-	cs (PidLidSharingSyncInterval)
-	cs (PidLidSharingTimeToLive)
-	cs (PidLidSharingTimeToLiveAuto)
-	cs (PidLidSharingWorkingHoursDays)
-	cs (PidLidSharingWorkingHoursEnd)
-	cs (PidLidSharingWorkingHoursStart)
-	cs (PidLidSharingWorkingHoursTimeZone)
-	cs (PidLidPercentComplete)
-	cs (PidLidTaskAcceptanceState)
-	cs (PidLidTaskAccepted)
-	cs (PidLidTaskActualEffort)
-	cs (PidLidTaskAssigner)
-	cs (PidLidTaskAssigners)
-	cs (PidLidTaskComplete)
-	cs (PidLidTaskCustomFlags)
-	cs (PidLidTaskDateCompleted)
-	cs (PidLidTaskDeadOccurrence)
-	cs (PidLidTaskDueDate)
-	cs (PidLidTaskEstimatedEffort)
-	cs (PidLidTaskFCreator)
-	cs (PidLidTaskFFixOffline)
-	cs (PidLidTaskFRecurring)
-	cs (PidLidTaskHistory)
-	cs (PidLidTaskLastDelegate)
-	cs (PidLidTaskLastUpdate)
-	cs (PidLidTaskLastUser)
-	cs (PidLidTaskMultipleRecipients)
-	cs (PidLidTaskNoCompute)
-	cs (PidLidTaskOrdinal)
-	cs (PidLidTaskOwner)
-	cs (PidLidTaskOwnership)
-	cs (PidLidTaskRecurrence)
-	cs (PidLidTaskResetReminder)
-	cs (PidLidTaskRole)
-	cs (PidLidTaskStartDate)
-	cs (PidLidTaskState)
-	cs (PidLidTaskStatus)
-	cs (PidLidTaskStatusOnComplete)
-	cs (PidLidTaskUpdates)
-	cs (PidLidTaskVersion)
-	cs (PidLidTeamTask)
-	cs (PidLidTrustRecipientHighlights)
-	cs (PidLidCategories)
-	cs (PidNameInstantMessagingAddress2)
-	cs (PidNameInstantMessagingAddress3)
-	cs (PidNameAttachmentMacContentType)
-	cs (PidNameAttachmentMacInfo)
-	cs (PidNameOriginalSpamConfidenceLevel)
-	cs (PidNameAudioNotes)
-	cs (PidNameAutomaticSpeechRecognitionData)
-	cs (PidNameOutlookProtectionRuleTimestamp)
-	cs (PidNameXUnifiedMessagingPartnerAssignedId)
-	cs (PidNameXUnifiedMessagingPartnerContent)
-	cs (PidNameXUnifiedMessagingPartnerContext)
-	cs (PidNameXUnifiedMessagingPartnerStatus)
-	cs (PidNameAcceptLanguage)
-	cs (PidNameApprovalAllowedDecisionMakers)
-	cs (PidNameApprovalRequestor)
-	cs (PidNameApproved)
-	cs (PidNameAuthenticatedAs)
-	cs (PidNameAuthenticatedDomain)
-	cs (PidNameAuthenticatedMechanism)
-	cs (PidNameAuthenticatedSource)
-	cs (PidNameBcc)
-	cs (PidNameCc)
-	cs (PidNameContentBase)
-	cs (PidNameContentClass)
-	cs (PidNameContentDisposition)
-	cs (PidNameContentID)
-	cs (PidNameContentLanguage)
-	cs (PidNameContentLocation)
-	cs (PidNameContentTransferEncoding)
-	cs (PidNameContentType)
-	cs (PidNameControl)
-	cs (PidNameCrossReference)
-	cs (PidNameDisposition)
-	cs (PidNameDispositionNotificationTo)
-	cs (PidNameDistribution)
-	cs (PidNameExpires)
-	cs (PidNameExpiryDate)
-	cs (PidNameFollowupTo)
-	cs (PidNameFrom)
-	cs (PidNameImportance)
-	cs (PidNameInReplyTo)
-	cs (PidNameInternetComment)
-	cs (PidNameInternetKeywords)
-	cs (PidNameInternetSubject)
-	cs (PidNameLines)
-	cs (PidNameMessageId)
-	cs (PidNameMimeVersion)
-	cs (PidNameNewsgroups)
-	cs (PidNameNntpPostingHost)
-	cs (PidNameOrganization)
-	cs (PidNameOriginalRecipient)
-	cs (PidNameOutlookProtectionRuleOverridden)
-	cs (PidNameOutlookProtectionRuleVersion)
-	cs (PidNamePath)
-	cs (PidNamePostingVersion)
-	cs (PidNamePriority)
-	cs (PidNameReceived)
-	cs (PidNameReferences)
-	cs (PidNameRelayVersion)
-	cs (PidNameReplyBy)
-	cs (PidNameReplyTo)
-	cs (PidNameReturnPath)
-	cs (PidNameReturnReceiptTo)
-	cs (PidNameRightsProtectMessage)
-	cs (PidNameSender)
-	cs (PidNameSensitivity)
-	cs (PidNameSummary)
-	cs (PidNameThreadIndex)
-	cs (PidNameThreadTopic)
-	cs (PidNameTo)
-	cs (PidNameXCallId)
-	cs (PidNameXFaxNumberOfPages)
-	cs (PidNameXMailer)
-	cs (PidNameXMessageCompleted)
-	cs (PidNameXMessageFlag)
-	cs (PidNameXRequireProtectedPlayOnPhone)
-	cs (PidNameXSenderTelephoneNumber)
-	cs (PidNameXSharingBrowseUrl)
-	cs (PidNameXSharingCapabilities)
-	cs (PidNameXSharingConfigUrl)
-	cs (PidNameXSharingExendedCaps)
-	cs (PidNameXSharingFlavor)
-	cs (PidNameXSharingInstanceGuid)
-	cs (PidNameXSharingLocalType)
-	cs (PidNameXSharingProviderGuid)
-	cs (PidNameXSharingProviderName)
-	cs (PidNameXSharingProviderUrl)
-	cs (PidNameXSharingRemoteName)
-	cs (PidNameXSharingRemotePath)
-	cs (PidNameXSharingRemoteStoreUid)
-	cs (PidNameXSharingRemoteType)
-	cs (PidNameXSharingRemoteUid)
-	cs (PidNameXUnsent)
-	cs (PidNameXVoiceMessageAttachmentOrder)
-	cs (PidNameXVoiceMessageDuration)
-	cs (PidNameXVoiceMessageSenderName)
-	cs (PidNameApplicationName)
-	cs (PidNameAuthor)
-	cs (PidNameByteCount)
-	cs (PidNameCalendarAttendeeRole)
-	cs (PidNameCalendarBusystatus)
-	cs (PidNameCalendarContact)
-	cs (PidNameCalendarContactUrl)
-	cs (PidNameCalendarCreated)
-	cs (PidNameCalendarDescriptionUrl)
-	cs (PidNameCalendarDuration)
-	cs (PidNameCalendarExceptionDate)
-	cs (PidNameCalendarExceptionRule)
-	cs (PidNameCalendarGeoLatitude)
-	cs (PidNameCalendarGeoLongitude)
-	cs (PidNameCalendarInstanceType)
-	cs (PidNameCalendarIsOrganizer)
-	cs (PidNameCalendarLastModified)
-	cs (PidNameCalendarLocationUrl)
-	cs (PidNameCalendarMeetingStatus)
-	cs (PidNameCalendarMethod)
-	cs (PidNameCalendarProductId)
-	cs (PidNameCalendarRecurrenceIdRange)
-	cs (PidNameCalendarReminderOffset)
-	cs (PidNameCalendarResources)
-	cs (PidNameCalendarRsvp)
-	cs (PidNameCalendarSequence)
-	cs (PidNameCalendarTimeZone)
-	cs (PidNameCalendarTimeZoneId)
-	cs (PidNameCalendarTransparent)
-	cs (PidNameCalendarUid)
-	cs (PidNameCalendarVersion)
-	cs (PidNameCategory)
-	cs (PidNameCharacterCount)
-	cs (PidNameComments)
-	cs (PidNameCompany)
-	cs (PidNameContactsAlternateRecipient)
-	cs (PidNameContactsCountry)
-	cs (PidNameContactsEmail1)
-	cs (PidNameContactsEmail2)
-	cs (PidNameContactsEmail3)
-	cs (PidNameContactsFileAs)
-	cs (PidNameContactsFileasId)
-	cs (PidNameContactsHomeLatitude)
-	cs (PidNameContactsHomeLongitude)
-	cs (PidNameContactsHomeTimeZone)
-	cs (PidNameContactsMapUrl)
-	cs (PidNameContactsOtherCountryCode)
-	cs (PidNameContactsOtherPager)
-	cs (PidNameContactsOtherTimeZone)
-	cs (PidNameContactsProxyAddresses)
-	cs (PidNameContactsSecretaryUrl)
-	cs (PidNameContactsSourceUrl)
-	cs (PidNameCreateDateTimeReadOnly)
-	cs (PidNameDavGetContentType)
-	cs (PidNameDavId)
-	cs (PidNameDavIsCollection)
-	cs (PidNameDavIsStructuredDocument)
-	cs (PidNameDavParentName)
-	cs (PidNameDavResourceType)
-	cs (PidNameDavSearchRequest)
-	cs (PidNameDavSearchType)
-	cs (PidNameDavUid)
-	cs (PidNameDocumentParts)
-	cs (PidNameEditTime)
-	cs (PidNameExchangeIntendedBusyStatus)
-	cs (PidNameExchangeJunkEmailMoveStamp)
-	cs (PidNameExchangeModifyExceptionStructure)
-	cs (PidNameExchangeNoModifyExceptions)
-	cs (PidNameExchangePatternEnd)
-	cs (PidNameExchangePatternStart)
-	cs (PidNameExchangePublicFolderEmailAddress)
-	cs (PidNameExchangeReminderInterval)
-	cs (PidNameExchDatabaseSchema)
-	cs (PidNameExchDataExpectedContentClass)
-	cs (PidNameExchDataSchemaCollectionReference)
-	cs (PidNameHeadingPairs)
-	cs (PidNameHiddenCount)
-	cs (PidNameHttpmailCalendar)
-	cs (PidNameHttpmailCc)
-	cs (PidNameHttpmailContacts)
-	cs (PidNameHttpmailContentMediaType)
-	cs (PidNameHttpmailFrom)
-	cs (PidNameHttpmailFromEmail)
-	cs (PidNameHttpmailHtmlDescription)
-	cs (PidNameHttpmailOutbox)
-	cs (PidNameHttpmailSendMessage)
-	cs (PidNameHttpmailSubmitted)
-	cs (PidNameHttpmailTo)
-	cs (PidNameICalendarRecurrenceDate)
-	cs (PidNameICalendarRecurrenceRule)
-	cs (PidNameKeywords)
-	cs (PidNameLastAuthor)
-	cs (PidNameLastPrinted)
-	cs (PidNameLastSaveDateTime)
-	cs (PidNameLineCount)
-	cs (PidNameLinksDirty)
-	cs (PidNameMailSubmissionUri)
-	cs (PidNameManager)
-	cs (PidNameMultimediaClipCount)
-	cs (PidNameNoteCount)
-	cs (PidNameOMSAccountGuid)
-	cs (PidNameOMSMobileModel)
-	cs (PidNameOMSScheduleTime)
-	cs (PidNameOMSServiceType)
-	cs (PidNameOMSSourceType)
-	cs (PidNamePageCount)
-	cs (PidNameParagraphCount)
-	cs (PidNamePhishingStamp)
-	cs (PidNamePresentationFormat)
-	cs (PidNameQuarantineOriginalSender)
-	cs (PidNameRevisionNumber)
-	cs (PidNameRightsManagementLicense)
-	cs (PidNameScale)
-	cs (PidNameSecurity)
-	cs (PidNameSlideCount)
-	cs (PidNameSubject)
-	cs (PidNameTemplate)
-	cs (PidNameThumbnail)
-	cs (PidNameTitle)
-	cs (PidNameWordCount)
-	#undef cs
+	if (!names) {
+		struct _array {
+			uint32_t proptag;
+			const gchar *name;
+		} array[] = {
+			#define cs(x) { x, #x } ,
+			cs (PidLidAddressBookProviderArrayType)
+			cs (PidLidAddressBookProviderEmailList)
+			cs (PidLidAddressCountryCode)
+			cs (PidLidAnniversaryEventEntryId)
+			cs (PidLidAutoLog)
+			cs (PidLidBirthdayEventEntryId)
+			cs (PidLidBirthdayLocal)
+			cs (PidLidBusinessCardCardPicture)
+			cs (PidLidBusinessCardDisplayDefinition)
+			cs (PidLidContactCharacterSet)
+			cs (PidLidContactItemData)
+			cs (PidLidContactUserField1)
+			cs (PidLidContactUserField2)
+			cs (PidLidContactUserField3)
+			cs (PidLidContactUserField4)
+			cs (PidLidDepartment)
+			cs (PidLidDistributionListChecksum)
+			cs (PidLidDistributionListMembers)
+			cs (PidLidDistributionListName)
+			cs (PidLidDistributionListOneOffMembers)
+			cs (PidLidDistributionListStream)
+			cs (PidLidEmail1AddressType)
+			cs (PidLidEmail1DisplayName)
+			cs (PidLidEmail1EmailAddress)
+			cs (PidLidEmail1OriginalDisplayName)
+			cs (PidLidEmail1OriginalEntryId)
+			cs (PidLidEmail1RichTextFormat)
+			cs (PidLidEmail2AddressType)
+			cs (PidLidEmail2DisplayName)
+			cs (PidLidEmail2EmailAddress)
+			cs (PidLidEmail2OriginalDisplayName)
+			cs (PidLidEmail2OriginalEntryId)
+			cs (PidLidEmail2RichTextFormat)
+			cs (PidLidEmail3AddressType)
+			cs (PidLidEmail3DisplayName)
+			cs (PidLidEmail3EmailAddress)
+			cs (PidLidEmail3OriginalDisplayName)
+			cs (PidLidEmail3OriginalEntryId)
+			cs (PidLidEmail3RichTextFormat)
+			cs (PidLidEmailList)
+			cs (PidLidFax1AddressType)
+			cs (PidLidFax1EmailAddress)
+			cs (PidLidFax1OriginalDisplayName)
+			cs (PidLidFax1OriginalEntryId)
+			cs (PidLidFax2AddressType)
+			cs (PidLidFax2EmailAddress)
+			cs (PidLidFax2OriginalDisplayName)
+			cs (PidLidFax2OriginalEntryId)
+			cs (PidLidFax3AddressType)
+			cs (PidLidFax3EmailAddress)
+			cs (PidLidFax3OriginalDisplayName)
+			cs (PidLidFax3OriginalEntryId)
+			cs (PidLidFileUnder)
+			cs (PidLidFileUnderId)
+			cs (PidLidFileUnderList)
+			cs (PidLidFreeBusyLocation)
+			cs (PidLidHasPicture)
+			cs (PidLidHomeAddress)
+			cs (PidLidHomeAddressCountryCode)
+			cs (PidLidHtml)
+			cs (PidLidInstantMessagingAddress)
+			cs (PidLidOtherAddress)
+			cs (PidLidOtherAddressCountryCode)
+			cs (PidLidPostalAddressId)
+			cs (PidLidReferredBy)
+			cs (PidLidWeddingAnniversaryLocal)
+			cs (PidLidWorkAddress)
+			cs (PidLidWorkAddressCity)
+			cs (PidLidWorkAddressCountry)
+			cs (PidLidWorkAddressCountryCode)
+			cs (PidLidWorkAddressPostalCode)
+			cs (PidLidWorkAddressPostOfficeBox)
+			cs (PidLidWorkAddressState)
+			cs (PidLidYomiCompanyName)
+			cs (PidLidYomiFirstName)
+			cs (PidLidYomiLastName)
+			cs (PidLidAllAttendeesString)
+			cs (PidLidAllowExternalCheck)
+			cs (PidLidAppointmentAuxiliaryFlags)
+			cs (PidLidAppointmentColor)
+			cs (PidLidAppointmentCounterProposal)
+			cs (PidLidAppointmentDuration)
+			cs (PidLidAppointmentEndDate)
+			cs (PidLidAppointmentEndTime)
+			cs (PidLidAppointmentEndWhole)
+			cs (PidLidAppointmentLastSequence)
+			cs (PidLidAppointmentNotAllowPropose)
+			cs (PidLidAppointmentProposalNumber)
+			cs (PidLidAppointmentProposedDuration)
+			cs (PidLidAppointmentProposedEndWhole)
+			cs (PidLidAppointmentProposedStartWhole)
+			cs (PidLidAppointmentRecur)
+			cs (PidLidAppointmentReplyName)
+			cs (PidLidAppointmentReplyTime)
+			cs (PidLidAppointmentSequence)
+			cs (PidLidAppointmentSequenceTime)
+			cs (PidLidAppointmentStartDate)
+			cs (PidLidAppointmentStartTime)
+			cs (PidLidAppointmentStartWhole)
+			cs (PidLidAppointmentStateFlags)
+			cs (PidLidAppointmentSubType)
+			cs (PidLidAppointmentTimeZoneDefinitionEndDisplay)
+			cs (PidLidAppointmentTimeZoneDefinitionRecur)
+			cs (PidLidAppointmentTimeZoneDefinitionStartDisplay)
+			cs (PidLidAppointmentUnsendableRecipients)
+			cs (PidLidAppointmentUpdateTime)
+			cs (PidLidAutoFillLocation)
+			cs (PidLidAutoStartCheck)
+			cs (PidLidBusyStatus)
+			cs (PidLidCcAttendeesString)
+			cs (PidLidChangeHighlight)
+			cs (PidLidClipEnd)
+			cs (PidLidClipStart)
+			cs (PidLidCollaborateDoc)
+			cs (PidLidConferencingCheck)
+			cs (PidLidConferencingType)
+			cs (PidLidDirectory)
+			cs (PidLidExceptionReplaceTime)
+			cs (PidLidFExceptionalAttendees)
+			cs (PidLidFExceptionalBody)
+			cs (PidLidFInvited)
+			cs (PidLidForwardInstance)
+			cs (PidLidForwardNotificationRecipients)
+			cs (PidLidFOthersAppointment)
+			cs (PidLidInboundICalStream)
+			cs (PidLidIntendedBusyStatus)
+			cs (PidLidLinkedTaskItems)
+			cs (PidLidLocation)
+			cs (PidLidMeetingWorkspaceUrl)
+			cs (PidLidNetShowUrl)
+			cs (PidLidOnlinePassword)
+			cs (PidLidOrganizerAlias)
+			cs (PidLidOriginalStoreEntryId)
+			cs (PidLidOwnerName)
+			cs (PidLidRecurrencePattern)
+			cs (PidLidRecurrenceType)
+			cs (PidLidRecurring)
+			cs (PidLidResponseStatus)
+			cs (PidLidSingleBodyICal)
+			cs (PidLidTimeZoneDescription)
+			cs (PidLidTimeZoneStruct)
+			cs (PidLidToAttendeesString)
+			cs (PidLidClientIntent)
+			cs (PidLidServerProcessed)
+			cs (PidLidServerProcessingActions)
+			cs (PidLidAgingDontAgeMe)
+			cs (PidLidAutoProcessState)
+			cs (PidLidBilling)
+			cs (PidLidClassification)
+			cs (PidLidClassificationDescription)
+			cs (PidLidClassificationGuid)
+			cs (PidLidClassificationKeep)
+			cs (PidLidClassified)
+			cs (PidLidCommonEnd)
+			cs (PidLidCommonStart)
+			cs (PidLidCompanies)
+			cs (PidLidContactLinkEntry)
+			cs (PidLidContactLinkName)
+			cs (PidLidContactLinkSearchKey)
+			cs (PidLidContacts)
+			cs (PidLidConversationActionLastAppliedTime)
+			cs (PidLidConversationActionMaxDeliveryTime)
+			cs (PidLidConversationActionMoveFolderEid)
+			cs (PidLidConversationActionMoveStoreEid)
+			cs (PidLidConversationActionVersion)
+			cs (PidLidConversationProcessed)
+			cs (PidLidCurrentVersion)
+			cs (PidLidCurrentVersionName)
+			cs (PidLidDayOfMonth)
+			cs (PidLidFlagRequest)
+			cs (PidLidFlagString)
+			cs (PidLidICalendarDayOfWeekMask)
+			cs (PidLidInfoPathFormName)
+			cs (PidLidInternetAccountName)
+			cs (PidLidInternetAccountStamp)
+			cs (PidLidMonthOfYear)
+			cs (PidLidNoEndDateFlag)
+			cs (PidLidNonSendableBcc)
+			cs (PidLidNonSendableCc)
+			cs (PidLidNonSendableTo)
+			cs (PidLidNonSendBccTrackStatus)
+			cs (PidLidNonSendCcTrackStatus)
+			cs (PidLidNonSendToTrackStatus)
+			cs (PidLidOccurrences)
+			cs (PidLidPrivate)
+			cs (PidLidPromptSendUpdate)
+			cs (PidLidRecurrenceDuration)
+			cs (PidLidReferenceEntryId)
+			cs (PidLidReminderDelta)
+			cs (PidLidReminderFileParameter)
+			cs (PidLidReminderOverride)
+			cs (PidLidReminderPlaySound)
+			cs (PidLidReminderSet)
+			cs (PidLidReminderSignalTime)
+			cs (PidLidReminderTime)
+			cs (PidLidReminderTimeDate)
+			cs (PidLidReminderTimeTime)
+			cs (PidLidReminderType)
+			cs (PidLidRemoteStatus)
+			cs (PidLidSideEffects)
+			cs (PidLidSmartNoAttach)
+			cs (PidLidSpamOriginalFolder)
+			cs (PidLidTaskGlobalId)
+			cs (PidLidTaskMode)
+			cs (PidLidToDoOrdinalDate)
+			cs (PidLidToDoSubOrdinal)
+			cs (PidLidToDoTitle)
+			cs (PidLidUseTnef)
+			cs (PidLidValidFlagStringProof)
+			cs (PidLidVerbResponse)
+			cs (PidLidVerbStream)
+			cs (PidLidLogDocumentPosted)
+			cs (PidLidLogDocumentPrinted)
+			cs (PidLidLogDocumentRouted)
+			cs (PidLidLogDocumentSaved)
+			cs (PidLidLogDuration)
+			cs (PidLidLogEnd)
+			cs (PidLidLogFlags)
+			cs (PidLidLogStart)
+			cs (PidLidLogType)
+			cs (PidLidLogTypeDesc)
+			cs (PidLidAppointmentMessageClass)
+			cs (PidLidAttendeeCriticalChange)
+			cs (PidLidCalendarType)
+			cs (PidLidCleanGlobalObjectId)
+			cs (PidLidDayInterval)
+			cs (PidLidDelegateMail)
+			cs (PidLidEndRecurrenceDate)
+			cs (PidLidEndRecurrenceTime)
+			cs (PidLidGlobalObjectId)
+			cs (PidLidIsException)
+			cs (PidLidIsRecurring)
+			cs (PidLidIsSilent)
+			cs (PidLidMeetingType)
+			cs (PidLidMonthInterval)
+			cs (PidLidMonthOfYearMask)
+			cs (PidLidOldLocation)
+			cs (PidLidOldRecurrenceType)
+			cs (PidLidOldWhenEndWhole)
+			cs (PidLidOldWhenStartWhole)
+			cs (PidLidOptionalAttendees)
+			cs (PidLidOwnerCriticalChange)
+			cs (PidLidRequiredAttendees)
+			cs (PidLidResourceAttendees)
+			cs (PidLidStartRecurrenceDate)
+			cs (PidLidStartRecurrenceTime)
+			cs (PidLidTimeZone)
+			cs (PidLidWeekInterval)
+			cs (PidLidWhere)
+			cs (PidLidYearInterval)
+			cs (PidLidNoteColor)
+			cs (PidLidNoteHeight)
+			cs (PidLidNoteWidth)
+			cs (PidLidNoteX)
+			cs (PidLidNoteY)
+			cs (PidLidPostRssChannel)
+			cs (PidLidPostRssChannelLink)
+			cs (PidLidPostRssItemGuid)
+			cs (PidLidPostRssItemHash)
+			cs (PidLidPostRssItemLink)
+			cs (PidLidPostRssItemXml)
+			cs (PidLidPostRssSubscription)
+			cs (PidLidSharingAnonymity)
+			cs (PidLidSharingBindingEntryId)
+			cs (PidLidSharingBrowseUrl)
+			cs (PidLidSharingCapabilities)
+			cs (PidLidSharingConfigurationUrl)
+			cs (PidLidSharingDataRangeEnd)
+			cs (PidLidSharingDataRangeStart)
+			cs (PidLidSharingDetail)
+			cs (PidLidSharingExtensionXml)
+			cs (PidLidSharingFilter)
+			cs (PidLidSharingFlags)
+			cs (PidLidSharingFlavor)
+			cs (PidLidSharingFolderEntryId)
+			cs (PidLidSharingIndexEntryId)
+			cs (PidLidSharingInitiatorEntryId)
+			cs (PidLidSharingInitiatorName)
+			cs (PidLidSharingInitiatorSmtp)
+			cs (PidLidSharingInstanceGuid)
+			cs (PidLidSharingLastAutoSyncTime)
+			cs (PidLidSharingLastSyncTime)
+			cs (PidLidSharingLocalComment)
+			cs (PidLidSharingLocalLastModificationTime)
+			cs (PidLidSharingLocalName)
+			cs (PidLidSharingLocalPath)
+			cs (PidLidSharingLocalStoreUid)
+			cs (PidLidSharingLocalType)
+			cs (PidLidSharingLocalUid)
+			cs (PidLidSharingOriginalMessageEntryId)
+			cs (PidLidSharingParentBindingEntryId)
+			cs (PidLidSharingParticipants)
+			cs (PidLidSharingPermissions)
+			cs (PidLidSharingProviderExtension)
+			cs (PidLidSharingProviderGuid)
+			cs (PidLidSharingProviderName)
+			cs (PidLidSharingProviderUrl)
+			cs (PidLidSharingRangeEnd)
+			cs (PidLidSharingRangeStart)
+			cs (PidLidSharingReciprocation)
+			cs (PidLidSharingRemoteByteSize)
+			cs (PidLidSharingRemoteComment)
+			cs (PidLidSharingRemoteCrc)
+			cs (PidLidSharingRemoteLastModificationTime)
+			cs (PidLidSharingRemoteMessageCount)
+			cs (PidLidSharingRemoteName)
+			cs (PidLidSharingRemotePass)
+			cs (PidLidSharingRemotePath)
+			cs (PidLidSharingRemoteStoreUid)
+			cs (PidLidSharingRemoteType)
+			cs (PidLidSharingRemoteUid)
+			cs (PidLidSharingRemoteUser)
+			cs (PidLidSharingRemoteVersion)
+			cs (PidLidSharingResponseTime)
+			cs (PidLidSharingResponseType)
+			cs (PidLidSharingRoamLog)
+			cs (PidLidSharingStart)
+			cs (PidLidSharingStatus)
+			cs (PidLidSharingStop)
+			cs (PidLidSharingSyncFlags)
+			cs (PidLidSharingSyncInterval)
+			cs (PidLidSharingTimeToLive)
+			cs (PidLidSharingTimeToLiveAuto)
+			cs (PidLidSharingWorkingHoursDays)
+			cs (PidLidSharingWorkingHoursEnd)
+			cs (PidLidSharingWorkingHoursStart)
+			cs (PidLidSharingWorkingHoursTimeZone)
+			cs (PidLidPercentComplete)
+			cs (PidLidTaskAcceptanceState)
+			cs (PidLidTaskAccepted)
+			cs (PidLidTaskActualEffort)
+			cs (PidLidTaskAssigner)
+			cs (PidLidTaskAssigners)
+			cs (PidLidTaskComplete)
+			cs (PidLidTaskCustomFlags)
+			cs (PidLidTaskDateCompleted)
+			cs (PidLidTaskDeadOccurrence)
+			cs (PidLidTaskDueDate)
+			cs (PidLidTaskEstimatedEffort)
+			cs (PidLidTaskFCreator)
+			cs (PidLidTaskFFixOffline)
+			cs (PidLidTaskFRecurring)
+			cs (PidLidTaskHistory)
+			cs (PidLidTaskLastDelegate)
+			cs (PidLidTaskLastUpdate)
+			cs (PidLidTaskLastUser)
+			cs (PidLidTaskMultipleRecipients)
+			cs (PidLidTaskNoCompute)
+			cs (PidLidTaskOrdinal)
+			cs (PidLidTaskOwner)
+			cs (PidLidTaskOwnership)
+			cs (PidLidTaskRecurrence)
+			cs (PidLidTaskResetReminder)
+			cs (PidLidTaskRole)
+			cs (PidLidTaskStartDate)
+			cs (PidLidTaskState)
+			cs (PidLidTaskStatus)
+			cs (PidLidTaskStatusOnComplete)
+			cs (PidLidTaskUpdates)
+			cs (PidLidTaskVersion)
+			cs (PidLidTeamTask)
+			cs (PidLidTrustRecipientHighlights)
+			cs (PidLidCategories)
+			cs (PidNameInstantMessagingAddress2)
+			cs (PidNameInstantMessagingAddress3)
+			cs (PidNameAttachmentMacContentType)
+			cs (PidNameAttachmentMacInfo)
+			cs (PidNameOriginalSpamConfidenceLevel)
+			cs (PidNameAudioNotes)
+			cs (PidNameAutomaticSpeechRecognitionData)
+			cs (PidNameOutlookProtectionRuleTimestamp)
+			cs (PidNameXUnifiedMessagingPartnerAssignedId)
+			cs (PidNameXUnifiedMessagingPartnerContent)
+			cs (PidNameXUnifiedMessagingPartnerContext)
+			cs (PidNameXUnifiedMessagingPartnerStatus)
+			cs (PidNameAcceptLanguage)
+			cs (PidNameApprovalAllowedDecisionMakers)
+			cs (PidNameApprovalRequestor)
+			cs (PidNameApproved)
+			cs (PidNameAuthenticatedAs)
+			cs (PidNameAuthenticatedDomain)
+			cs (PidNameAuthenticatedMechanism)
+			cs (PidNameAuthenticatedSource)
+			cs (PidNameBcc)
+			cs (PidNameCc)
+			cs (PidNameContentBase)
+			cs (PidNameContentClass)
+			cs (PidNameContentDisposition)
+			cs (PidNameContentID)
+			cs (PidNameContentLanguage)
+			cs (PidNameContentLocation)
+			cs (PidNameContentTransferEncoding)
+			cs (PidNameContentType)
+			cs (PidNameControl)
+			cs (PidNameCrossReference)
+			cs (PidNameDisposition)
+			cs (PidNameDispositionNotificationTo)
+			cs (PidNameDistribution)
+			cs (PidNameExpires)
+			cs (PidNameExpiryDate)
+			cs (PidNameFollowupTo)
+			cs (PidNameFrom)
+			cs (PidNameImportance)
+			cs (PidNameInReplyTo)
+			cs (PidNameInternetComment)
+			cs (PidNameInternetKeywords)
+			cs (PidNameInternetSubject)
+			cs (PidNameLines)
+			cs (PidNameMessageId)
+			cs (PidNameMimeVersion)
+			cs (PidNameNewsgroups)
+			cs (PidNameNntpPostingHost)
+			cs (PidNameOrganization)
+			cs (PidNameOriginalRecipient)
+			cs (PidNameOutlookProtectionRuleOverridden)
+			cs (PidNameOutlookProtectionRuleVersion)
+			cs (PidNamePath)
+			cs (PidNamePostingVersion)
+			cs (PidNamePriority)
+			cs (PidNameReceived)
+			cs (PidNameReferences)
+			cs (PidNameRelayVersion)
+			cs (PidNameReplyBy)
+			cs (PidNameReplyTo)
+			cs (PidNameReturnPath)
+			cs (PidNameReturnReceiptTo)
+			cs (PidNameRightsProtectMessage)
+			cs (PidNameSender)
+			cs (PidNameSensitivity)
+			cs (PidNameSummary)
+			cs (PidNameThreadIndex)
+			cs (PidNameThreadTopic)
+			cs (PidNameTo)
+			cs (PidNameXCallId)
+			cs (PidNameXFaxNumberOfPages)
+			cs (PidNameXMailer)
+			cs (PidNameXMessageCompleted)
+			cs (PidNameXMessageFlag)
+			cs (PidNameXRequireProtectedPlayOnPhone)
+			cs (PidNameXSenderTelephoneNumber)
+			cs (PidNameXSharingBrowseUrl)
+			cs (PidNameXSharingCapabilities)
+			cs (PidNameXSharingConfigUrl)
+			cs (PidNameXSharingExendedCaps)
+			cs (PidNameXSharingFlavor)
+			cs (PidNameXSharingInstanceGuid)
+			cs (PidNameXSharingLocalType)
+			cs (PidNameXSharingProviderGuid)
+			cs (PidNameXSharingProviderName)
+			cs (PidNameXSharingProviderUrl)
+			cs (PidNameXSharingRemoteName)
+			cs (PidNameXSharingRemotePath)
+			cs (PidNameXSharingRemoteStoreUid)
+			cs (PidNameXSharingRemoteType)
+			cs (PidNameXSharingRemoteUid)
+			cs (PidNameXUnsent)
+			cs (PidNameXVoiceMessageAttachmentOrder)
+			cs (PidNameXVoiceMessageDuration)
+			cs (PidNameXVoiceMessageSenderName)
+			cs (PidNameApplicationName)
+			cs (PidNameAuthor)
+			cs (PidNameByteCount)
+			cs (PidNameCalendarAttendeeRole)
+			cs (PidNameCalendarBusystatus)
+			cs (PidNameCalendarContact)
+			cs (PidNameCalendarContactUrl)
+			cs (PidNameCalendarCreated)
+			cs (PidNameCalendarDescriptionUrl)
+			cs (PidNameCalendarDuration)
+			cs (PidNameCalendarExceptionDate)
+			cs (PidNameCalendarExceptionRule)
+			cs (PidNameCalendarGeoLatitude)
+			cs (PidNameCalendarGeoLongitude)
+			cs (PidNameCalendarInstanceType)
+			cs (PidNameCalendarIsOrganizer)
+			cs (PidNameCalendarLastModified)
+			cs (PidNameCalendarLocationUrl)
+			cs (PidNameCalendarMeetingStatus)
+			cs (PidNameCalendarMethod)
+			cs (PidNameCalendarProductId)
+			cs (PidNameCalendarRecurrenceIdRange)
+			cs (PidNameCalendarReminderOffset)
+			cs (PidNameCalendarResources)
+			cs (PidNameCalendarRsvp)
+			cs (PidNameCalendarSequence)
+			cs (PidNameCalendarTimeZone)
+			cs (PidNameCalendarTimeZoneId)
+			cs (PidNameCalendarTransparent)
+			cs (PidNameCalendarUid)
+			cs (PidNameCalendarVersion)
+			cs (PidNameCategory)
+			cs (PidNameCharacterCount)
+			cs (PidNameComments)
+			cs (PidNameCompany)
+			cs (PidNameContactsAlternateRecipient)
+			cs (PidNameContactsCountry)
+			cs (PidNameContactsEmail1)
+			cs (PidNameContactsEmail2)
+			cs (PidNameContactsEmail3)
+			cs (PidNameContactsFileAs)
+			cs (PidNameContactsFileasId)
+			cs (PidNameContactsHomeLatitude)
+			cs (PidNameContactsHomeLongitude)
+			cs (PidNameContactsHomeTimeZone)
+			cs (PidNameContactsMapUrl)
+			cs (PidNameContactsOtherCountryCode)
+			cs (PidNameContactsOtherPager)
+			cs (PidNameContactsOtherTimeZone)
+			cs (PidNameContactsProxyAddresses)
+			cs (PidNameContactsSecretaryUrl)
+			cs (PidNameContactsSourceUrl)
+			cs (PidNameCreateDateTimeReadOnly)
+			cs (PidNameDavGetContentType)
+			cs (PidNameDavId)
+			cs (PidNameDavIsCollection)
+			cs (PidNameDavIsStructuredDocument)
+			cs (PidNameDavParentName)
+			cs (PidNameDavResourceType)
+			cs (PidNameDavSearchRequest)
+			cs (PidNameDavSearchType)
+			cs (PidNameDavUid)
+			cs (PidNameDocumentParts)
+			cs (PidNameEditTime)
+			cs (PidNameExchangeIntendedBusyStatus)
+			cs (PidNameExchangeJunkEmailMoveStamp)
+			cs (PidNameExchangeModifyExceptionStructure)
+			cs (PidNameExchangeNoModifyExceptions)
+			cs (PidNameExchangePatternEnd)
+			cs (PidNameExchangePatternStart)
+			cs (PidNameExchangePublicFolderEmailAddress)
+			cs (PidNameExchangeReminderInterval)
+			cs (PidNameExchDatabaseSchema)
+			cs (PidNameExchDataExpectedContentClass)
+			cs (PidNameExchDataSchemaCollectionReference)
+			cs (PidNameHeadingPairs)
+			cs (PidNameHiddenCount)
+			cs (PidNameHttpmailCalendar)
+			cs (PidNameHttpmailCc)
+			cs (PidNameHttpmailContacts)
+			cs (PidNameHttpmailContentMediaType)
+			cs (PidNameHttpmailFrom)
+			cs (PidNameHttpmailFromEmail)
+			cs (PidNameHttpmailHtmlDescription)
+			cs (PidNameHttpmailOutbox)
+			cs (PidNameHttpmailSendMessage)
+			cs (PidNameHttpmailSubmitted)
+			cs (PidNameHttpmailTo)
+			cs (PidNameICalendarRecurrenceDate)
+			cs (PidNameICalendarRecurrenceRule)
+			cs (PidNameKeywords)
+			cs (PidNameLastAuthor)
+			cs (PidNameLastPrinted)
+			cs (PidNameLastSaveDateTime)
+			cs (PidNameLineCount)
+			cs (PidNameLinksDirty)
+			cs (PidNameMailSubmissionUri)
+			cs (PidNameManager)
+			cs (PidNameMultimediaClipCount)
+			cs (PidNameNoteCount)
+			cs (PidNameOMSAccountGuid)
+			cs (PidNameOMSMobileModel)
+			cs (PidNameOMSScheduleTime)
+			cs (PidNameOMSServiceType)
+			cs (PidNameOMSSourceType)
+			cs (PidNamePageCount)
+			cs (PidNameParagraphCount)
+			cs (PidNamePhishingStamp)
+			cs (PidNamePresentationFormat)
+			cs (PidNameQuarantineOriginalSender)
+			cs (PidNameRevisionNumber)
+			cs (PidNameRightsManagementLicense)
+			cs (PidNameScale)
+			cs (PidNameSecurity)
+			cs (PidNameSlideCount)
+			cs (PidNameSubject)
+			cs (PidNameTemplate)
+			cs (PidNameThumbnail)
+			cs (PidNameTitle)
+			cs (PidNameWordCount)
+			#undef cs
+			{ 0, NULL }
+		};
+		gint ii;
+
+		names = g_hash_table_new (g_direct_hash, g_direct_equal);
+		for (ii = 0; array[ii].proptag; ii++) {
+			gpointer name = (gpointer) array[ii].name;
+
+			g_hash_table_insert (names, GUINT_TO_POINTER (array[ii].proptag), name);
+			g_hash_table_insert (names, GUINT_TO_POINTER ((array[ii].proptag & ~0xFFFF) | PT_ERROR), name);
+		}
 	}
 
-	return NULL;
+	return g_hash_table_lookup (names, GUINT_TO_POINTER (proptag));
 }
 
 void
-e_mapi_debug_dump_properties (EMapiConnection *conn, mapi_id_t fid, struct mapi_SPropValue_array *properties, gint indent)
+e_mapi_debug_dump_properties (struct mapi_SPropValue_array *properties,
+			      gint indent)
 {
 	gint i = 0;
 
@@ -714,12 +729,13 @@ e_mapi_debug_dump_properties (EMapiConnection *conn, mapi_id_t fid, struct mapi_
 
 	for (i = 0; i < properties->cValues; i++) {
 		struct mapi_SPropValue *lpProp = &properties->lpProps[i];
-		const gchar *tmp = get_proptag_name (lpProp->ulPropTag);
+		const gchar *tmp;
 		gchar t_str[26];
 		gint j = 0;
 
+		tmp = get_proptag_name (lpProp->ulPropTag);
 		if (!tmp || !*tmp)
-			tmp = get_namedid_name (conn, fid, lpProp->ulPropTag);
+			tmp = get_namedid_name (lpProp->ulPropTag);
 
 		if (tmp && *tmp)
 			g_print ("%*s%s ", indent, "", tmp);
@@ -862,18 +878,18 @@ e_mapi_debug_dump_object (EMapiObject *object, gboolean with_properties, gint in
 		return;
 
 	if (with_properties)
-		e_mapi_debug_dump_properties (NULL, 0, &object->properties, indent + 3);
+		e_mapi_debug_dump_properties (&object->properties, indent + 3);
 
 	for (index = 0, recipient = object->recipients; recipient; index++, recipient = recipient->next) {
 		g_print ("%*sRecipient[%d]:\n", indent + 2, "", index);
 		if (with_properties)
-			e_mapi_debug_dump_properties (NULL, 0, &recipient->properties, indent + 5);
+			e_mapi_debug_dump_properties (&recipient->properties, indent + 5);
 	}
 
 	for (index = 0, attachment = object->attachments; attachment; index++, attachment = attachment->next) {
 		g_print ("%*sAttachment[%d]:\n", indent + 2, "", index);
 		if (with_properties)
-			e_mapi_debug_dump_properties (NULL, 0, &attachment->properties, indent + 3);
+			e_mapi_debug_dump_properties (&attachment->properties, indent + 3);
 		if (attachment->embedded_object) {
 			g_print ("%*sEmbedded object:\n", indent + 3, "");
 			e_mapi_debug_dump_object (attachment->embedded_object, with_properties, indent + 5);
diff --git a/src/libexchangemapi/e-mapi-debug.h b/src/libexchangemapi/e-mapi-debug.h
index b3479ca..3056aff 100644
--- a/src/libexchangemapi/e-mapi-debug.h
+++ b/src/libexchangemapi/e-mapi-debug.h
@@ -30,9 +30,7 @@ G_BEGIN_DECLS
 gboolean	e_mapi_debug_is_enabled (void);
 void		e_mapi_debug_print (const gchar *format, ...);
 
-void		e_mapi_debug_dump_properties	(EMapiConnection *conn,
-						 mapi_id_t fid,
-						 struct mapi_SPropValue_array *properties,
+void		e_mapi_debug_dump_properties	(struct mapi_SPropValue_array *properties,
 						 gint indent);
 void		e_mapi_debug_dump_object	(EMapiObject *object,
 						 gboolean with_properties,
diff --git a/src/libexchangemapi/e-mapi-utils.c b/src/libexchangemapi/e-mapi-utils.c
index dbda865..ed99c18 100644
--- a/src/libexchangemapi/e-mapi-utils.c
+++ b/src/libexchangemapi/e-mapi-utils.c
@@ -146,25 +146,6 @@ e_mapi_util_find_row_propval (struct SRow *aRow, uint32_t proptag)
 	return (find_SPropValue_data(aRow, proptag));
 }
 
-gconstpointer
-e_mapi_util_find_row_namedid (struct SRow *aRow, EMapiConnection *conn, mapi_id_t fid, uint32_t namedid)
-{
-	uint32_t proptag;
-	gconstpointer res = NULL;
-
-	g_return_val_if_fail (aRow != NULL, NULL);
-	g_return_val_if_fail (conn != NULL, NULL);
-
-	proptag = e_mapi_connection_resolve_named_prop (conn, fid, namedid, NULL, NULL);
-	if (proptag != MAPI_E_RESERVED)
-		res = e_mapi_util_find_row_propval (aRow, proptag);
-
-	if (!res)
-		res = e_mapi_util_find_row_propval (aRow, namedid);
-
-	return res;
-}
-
 /*
  * Retrieve the property value for a given mapi_SPropValue_array and property tag.
  *
@@ -205,25 +186,6 @@ e_mapi_util_find_array_propval (struct mapi_SPropValue_array *properties, uint32
 	return (find_mapi_SPropValue_data(properties, proptag));
 }
 
-gconstpointer
-e_mapi_util_find_array_namedid (struct mapi_SPropValue_array *properties, EMapiConnection *conn, mapi_id_t fid, uint32_t namedid)
-{
-	uint32_t proptag;
-	gconstpointer res = NULL;
-
-	g_return_val_if_fail (properties != NULL, NULL);
-	g_return_val_if_fail (conn != NULL, NULL);
-
-	proptag = e_mapi_connection_resolve_named_prop (conn, fid, namedid, NULL, NULL);
-	if (proptag != MAPI_E_RESERVED)
-		res = e_mapi_util_find_array_propval (properties, proptag);
-
-	if (!res)
-		res = e_mapi_util_find_array_propval (properties, namedid);
-
-	return res;
-}
-
 uint32_t
 e_mapi_util_find_array_proptag (struct mapi_SPropValue_array *properties, uint32_t proptag)
 {
@@ -753,60 +715,6 @@ e_mapi_util_profile_name (struct mapi_context *mapi_ctx, const EMapiProfileData
 }
 
 /**
- * Adds prop_ids to props array. props should be created within the given mem_ctx.
- **/
-gboolean
-e_mapi_utils_add_props_to_props_array (TALLOC_CTX *mem_ctx,
-				       struct SPropTagArray *props,
-				       const uint32_t *prop_ids,
-				       guint prop_ids_n_elems)
-{
-	guint i;
-
-	g_return_val_if_fail (mem_ctx != NULL, FALSE);
-	g_return_val_if_fail (props != NULL, FALSE);
-	g_return_val_if_fail (prop_ids != NULL, FALSE);
-	g_return_val_if_fail (prop_ids_n_elems > 0, FALSE);
-
-	for (i = 0; i < prop_ids_n_elems; i++) {
-		SPropTagArray_add (mem_ctx, props, prop_ids[i]);
-	}
-
-	return TRUE;
-}
-
-/* Beware, the named_ids_list array is modified */
-gboolean
-e_mapi_utils_add_named_ids_to_props_array (EMapiConnection *conn,
-					   mapi_id_t fid,
-					   TALLOC_CTX *mem_ctx,
-					   struct SPropTagArray *props,
-					   ResolveNamedIDsData *named_ids_list,
-					   guint named_ids_n_elems,
-					   GCancellable *cancellable,
-					   GError **perror)
-{
-	guint i;
-
-	g_return_val_if_fail (conn != NULL, FALSE);
-	g_return_val_if_fail (mem_ctx != NULL, FALSE);
-	g_return_val_if_fail (props != NULL, FALSE);
-	g_return_val_if_fail (fid > 0, FALSE);
-	g_return_val_if_fail (named_ids_list != NULL, FALSE);
-	g_return_val_if_fail (named_ids_n_elems > 0, FALSE);
-
-	if (!e_mapi_connection_resolve_named_props (conn, fid, named_ids_list, named_ids_n_elems, cancellable, perror))
-		return FALSE;
-
-	for (i = 0; i < named_ids_n_elems; i++) {
-		if (named_ids_list[i].propid != MAPI_E_RESERVED)
-			SPropTagArray_add (mem_ctx, props, named_ids_list[i].propid);
-	}
-
-	return TRUE;
-}
-
-/**
  * Adds a new SPropValue at the end of values_array, allocating its memory in the mem_ctx.
  * *n_values holds number of items stored in the array, and will be increased by one.
  **/
@@ -826,33 +734,6 @@ e_mapi_utils_add_spropvalue (TALLOC_CTX *mem_ctx,
 	return TRUE;
 }
 
-/* similar as e_mapi_utils_add_spropvalue, just here is not used prop_tag, but named id */
-gboolean
-e_mapi_utils_add_spropvalue_namedid (EMapiConnection *conn,
-				     mapi_id_t fid,
-				     TALLOC_CTX *mem_ctx,
-				     struct SPropValue **values_array,
-				     uint32_t *n_values,
-				     uint32_t named_id,
-				     gconstpointer prop_value,
-				     GCancellable *cancellable,
-				     GError **perror)
-{
-	uint32_t prop_tag;
-
-	g_return_val_if_fail (conn != NULL, FALSE);
-	g_return_val_if_fail (fid != 0, FALSE);
-	g_return_val_if_fail (mem_ctx != NULL, FALSE);
-	g_return_val_if_fail (values_array != NULL, FALSE);
-	g_return_val_if_fail (n_values != NULL, FALSE);
-
-	prop_tag = e_mapi_connection_resolve_named_prop (conn, fid, named_id, cancellable, perror);
-	if (prop_tag == MAPI_E_RESERVED)
-		return FALSE;
-
-	return e_mapi_utils_add_spropvalue (mem_ctx, values_array, n_values, prop_tag, prop_value);
-}
-
 gboolean
 e_mapi_utils_add_property (struct mapi_SPropValue_array *properties,
 			   uint32_t proptag,
diff --git a/src/libexchangemapi/e-mapi-utils.h b/src/libexchangemapi/e-mapi-utils.h
index 25f6a39..7dc02ed 100644
--- a/src/libexchangemapi/e-mapi-utils.h
+++ b/src/libexchangemapi/e-mapi-utils.h
@@ -28,81 +28,76 @@
 #include "e-mapi-debug.h"
 #include "e-mapi-connection.h"
 
-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);
-
-gconstpointer	e_mapi_util_find_SPropVal_array_propval (struct SPropValue *values, uint32_t proptag);
-gconstpointer	e_mapi_util_find_row_propval (struct SRow *aRow, uint32_t proptag);
-gconstpointer	e_mapi_util_find_row_namedid (struct SRow *aRow, EMapiConnection *conn, mapi_id_t fid, uint32_t namedid);
-gconstpointer	e_mapi_util_find_array_propval (struct mapi_SPropValue_array *properties, uint32_t proptag);
-gconstpointer	e_mapi_util_find_array_namedid (struct mapi_SPropValue_array *properties, EMapiConnection *conn, mapi_id_t fid, uint32_t namedid);
-uint32_t	e_mapi_util_find_array_proptag (struct mapi_SPropValue_array *properties, uint32_t proptag);
-
-enum MAPISTATUS e_mapi_util_find_array_datetime_propval (struct timeval *tv, struct mapi_SPropValue_array *properties, uint32_t proptag);
-
-void	 e_mapi_util_recip_entryid_generate_smtp (TALLOC_CTX *mem_ctx, struct Binary_r *entryid, const gchar *display_name, const gchar *email);
-void	 e_mapi_util_recip_entryid_generate_ex  (TALLOC_CTX *mem_ctx, struct Binary_r *entryid, const gchar *exchange_dn);
-gboolean e_mapi_util_recip_entryid_decode (EMapiConnection *conn, const struct Binary_r *entyrid, gchar **display_name, gchar **email);
-
-void e_mapi_util_profiledata_from_settings (EMapiProfileData *empd, CamelMapiSettings *settings);
-gchar *		e_mapi_util_profile_name			(struct mapi_context *mapi_ctx,
-								 const EMapiProfileData *empd,
-								 gboolean migrate);
-gboolean e_mapi_util_trigger_krb_auth (const EMapiProfileData *empd, GError **error);
-
-gboolean	e_mapi_utils_add_props_to_props_array		(TALLOC_CTX *mem_ctx,
-								 struct SPropTagArray *props,
-								 const uint32_t *prop_ids,
-								 guint prop_ids_n_elems);
-gboolean	e_mapi_utils_add_named_ids_to_props_array	(EMapiConnection *conn,
-								 mapi_id_t fid,
-								 TALLOC_CTX *mem_ctx,
-								 struct SPropTagArray *props,
-								 ResolveNamedIDsData *named_ids_list,
-								 guint named_ids_n_elems,
-								 GCancellable *cancellable,
-								 GError **perror);
-
-gboolean	e_mapi_utils_add_spropvalue			(TALLOC_CTX *mem_ctx,
-								 struct SPropValue **values_array,
-								 uint32_t *n_values,
-								 uint32_t prop_tag,
-								 gconstpointer prop_value);
-gboolean	e_mapi_utils_add_spropvalue_namedid		(EMapiConnection *conn,
-								 mapi_id_t fid,
-								 TALLOC_CTX *mem_ctx,
-								 struct SPropValue **values_array,
-								 uint32_t *n_values,
-								 uint32_t named_id,
-								 gconstpointer prop_value,
-								 GCancellable *cancellable,
-								 GError **perror);
-gboolean	e_mapi_utils_add_property			(struct mapi_SPropValue_array *properties,
-								 uint32_t proptag,
-								 gconstpointer propvalue,
-								 TALLOC_CTX *mem_ctx);
-gboolean	e_mapi_utils_ensure_utf8_string			(uint32_t proptag,
-								 const uint32_t *cpid,
-								 const guint8 *buf_data,
-								 guint32 buf_len,
-								 gchar **out_utf8);
-
-uint32_t e_mapi_utils_push_crc32 (uint32_t crc32, uint8_t *bytes, uint32_t n_bytes);
-
-struct Binary_r *e_mapi_util_copy_binary_r (const struct Binary_r *bin);
-void e_mapi_util_free_binary_r (struct Binary_r *bin);
-
-time_t e_mapi_util_filetime_to_time_t (const struct FILETIME *filetime);
-void e_mapi_util_time_t_to_filetime (const time_t tt, struct FILETIME *filetime);
-
-gboolean	e_mapi_utils_propagate_cancelled_error		(const GError *mapi_error,
-								 GError **error);
-
-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);
-void		e_mapi_utils_destroy_mapi_context		(struct mapi_context *mapi_ctx);
+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);
+
+gconstpointer	e_mapi_util_find_SPropVal_array_propval	(struct SPropValue *values, uint32_t proptag);
+gconstpointer	e_mapi_util_find_row_propval		(struct SRow *aRow,
+							 uint32_t proptag);
+gconstpointer	e_mapi_util_find_array_propval		(struct mapi_SPropValue_array *properties,
+							 uint32_t proptag);
+uint32_t	e_mapi_util_find_array_proptag		(struct mapi_SPropValue_array *properties,
+							 uint32_t proptag);
+
+enum MAPISTATUS	e_mapi_util_find_array_datetime_propval	(struct timeval *tv,
+							 struct mapi_SPropValue_array *properties,
+							 uint32_t proptag);
+
+void		e_mapi_util_recip_entryid_generate_smtp	(TALLOC_CTX *mem_ctx,
+							 struct Binary_r *entryid,
+							 const gchar *display_name,
+							 const gchar *email);
+void		e_mapi_util_recip_entryid_generate_ex	(TALLOC_CTX *mem_ctx,
+							 struct Binary_r *entryid,
+							 const gchar *exchange_dn);
+gboolean	e_mapi_util_recip_entryid_decode	(EMapiConnection *conn,
+							 const struct Binary_r *entyrid,
+							 gchar **display_name,
+							 gchar **email);
+
+void		e_mapi_util_profiledata_from_settings	(EMapiProfileData *empd,
+							 CamelMapiSettings *settings);
+gchar *		e_mapi_util_profile_name		(struct mapi_context *mapi_ctx,
+							 const EMapiProfileData *empd,
+							 gboolean migrate);
+gboolean	e_mapi_util_trigger_krb_auth		(const EMapiProfileData *empd,
+							 GError **error);
+
+gboolean	e_mapi_utils_add_spropvalue		(TALLOC_CTX *mem_ctx,
+							 struct SPropValue **values_array,
+							 uint32_t *n_values,
+							 uint32_t prop_tag,
+							 gconstpointer prop_value);
+gboolean	e_mapi_utils_add_property		(struct mapi_SPropValue_array *properties,
+							 uint32_t proptag,
+							 gconstpointer propvalue,
+							 TALLOC_CTX *mem_ctx);
+gboolean	e_mapi_utils_ensure_utf8_string		(uint32_t proptag,
+							 const uint32_t *cpid,
+							 const guint8 *buf_data,
+							 guint32 buf_len,
+							 gchar **out_utf8);
+
+uint32_t	e_mapi_utils_push_crc32			(uint32_t crc32,
+							 uint8_t *bytes,
+							 uint32_t n_bytes);
+
+struct
+Binary_r *	e_mapi_util_copy_binary_r		(const struct Binary_r *bin);
+void		e_mapi_util_free_binary_r		(struct Binary_r *bin);
+
+time_t		e_mapi_util_filetime_to_time_t		(const struct FILETIME *filetime);
+void		e_mapi_util_time_t_to_filetime		(const time_t tt,
+							 struct FILETIME *filetime);
+
+gboolean	e_mapi_utils_propagate_cancelled_error	(const GError *mapi_error,
+							 GError **error);
+
+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);
+void		e_mapi_utils_destroy_mapi_context	(struct mapi_context *mapi_ctx);
 
 gboolean	e_mapi_utils_build_last_modify_restriction	(EMapiConnection *conn,
 								 TALLOC_CTX *mem_ctx,



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