[evolution-mapi] Bug #667714 - Support for setting user's folder permissions



commit b31a29b1d2cd82be56e8f84d1091c18bb96eb7a0
Author: Milan Crha <mcrha redhat com>
Date:   Tue Jan 24 13:35:51 2012 +0100

    Bug #667714 - Support for setting user's folder permissions

 po/POTFILES.in                                     |    2 +
 src/account-setup-eplugin/Makefile.am              |    6 +-
 .../e-mapi-account-settings.c                      |  469 ++++++--
 src/account-setup-eplugin/e-mapi-account-setup.c   |   98 ++-
 src/account-setup-eplugin/e-mapi-account-setup.h   |   10 +
 .../e-mapi-edit-folder-permissions.c               | 1283 ++++++++++++++++++++
 .../e-mapi-edit-folder-permissions.h               |   43 +
 src/account-setup-eplugin/e-mapi-search-gal-user.c |  756 ++++++++++++
 src/account-setup-eplugin/e-mapi-search-gal-user.h |   46 +
 .../e-mapi-subscribe-foreign-folder.c              |    1 -
 .../org-gnome-exchange-mapi.eplug.xml              |   43 +-
 src/addressbook/e-book-backend-mapi-gal.c          |    2 +-
 src/libexchangemapi/e-mapi-connection.c            |  335 +++++-
 src/libexchangemapi/e-mapi-connection.h            |   48 +
 src/libexchangemapi/e-mapi-utils.c                 |   38 +
 src/libexchangemapi/e-mapi-utils.h                 |    4 +
 16 files changed, 3075 insertions(+), 109 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1cdc8dc..c44f2ec 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,6 +1,8 @@
 src/account-setup-eplugin/e-mapi-account-listener.c
 src/account-setup-eplugin/e-mapi-account-setup.c
 src/account-setup-eplugin/e-mapi-account-settings.c
+src/account-setup-eplugin/e-mapi-edit-folder-permissions.c
+src/account-setup-eplugin/e-mapi-search-gal-user.c
 src/account-setup-eplugin/e-mapi-subscribe-foreign-folder.c
 src/account-setup-eplugin/org-gnome-exchange-mapi.eplug.xml
 src/addressbook/e-book-backend-mapi.c
diff --git a/src/account-setup-eplugin/Makefile.am b/src/account-setup-eplugin/Makefile.am
index 6058f40..22193e9 100644
--- a/src/account-setup-eplugin/Makefile.am
+++ b/src/account-setup-eplugin/Makefile.am
@@ -23,7 +23,11 @@ liborg_gnome_exchange_mapi_la_SOURCES = 	\
 	e-mapi-account-listener.c		\
 	e-mapi-account-listener.h		\
 	e-mapi-subscribe-foreign-folder.c	\
-	e-mapi-subscribe-foreign-folder.h
+	e-mapi-subscribe-foreign-folder.h	\
+	e-mapi-edit-folder-permissions.c	\
+	e-mapi-edit-folder-permissions.h	\
+	e-mapi-search-gal-user.c		\
+	e-mapi-search-gal-user.h
 
 liborg_gnome_exchange_mapi_la_LIBADD = 			\
 	$(top_builddir)/src/libexchangemapi/libexchangemapi-1.0.la \
diff --git a/src/account-setup-eplugin/e-mapi-account-settings.c b/src/account-setup-eplugin/e-mapi-account-settings.c
index c079280..6aa4a85 100644
--- a/src/account-setup-eplugin/e-mapi-account-settings.c
+++ b/src/account-setup-eplugin/e-mapi-account-settings.c
@@ -30,6 +30,7 @@
 
 #include <libedataserver/e-xml-hash-utils.h>
 #include <libedataserverui/e-passwords.h>
+#include <libedataserverui/e-source-selector.h>
 #include <libedataserver/e-account.h>
 #include <e-util/e-util.h>
 #include <e-util/e-dialog-utils.h>
@@ -48,11 +49,12 @@
 
 #include "e-mapi-account-listener.h"
 #include "e-mapi-subscribe-foreign-folder.h"
+#include "e-mapi-edit-folder-permissions.h"
 
-#define FOLDERSIZE_MENU_ITEM 0
+#include "camel/camel-mapi-store.h"
+#include "camel/camel-mapi-store-summary.h"
 
-gboolean  e_plugin_ui_init (GtkUIManager *ui_manager, EShellView *shell_view);
-GtkWidget *org_gnome_e_mapi_settings (EPlugin *epl, EConfigHookItemFactoryData *data);
+#define FOLDERSIZE_MENU_ITEM 0
 
 enum {
 	COL_FOLDERSIZE_NAME = 0,
@@ -218,61 +220,49 @@ folder_size_clicked (GtkButton *button,
 
 static gchar *
 get_profile_name_from_folder_tree (EShellView *shell_view,
-				   gchar **pfolder_uri,
+				   gchar **pfolder_path,
 				   CamelStore **pstore)
 {
 	EShellSidebar *shell_sidebar;
 	EMFolderTree *folder_tree;
-	gchar *folder_uri;
-	GtkTreeSelection *selection;
-	gchar *profile = NULL;
+	gchar *profile = NULL, *selected_path = NULL;
+	CamelStore *selected_store = NULL;
 
 	/* Get hold of Folder Tree */
 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
 	g_object_get (shell_sidebar, "folder-tree", &folder_tree, NULL);
-	folder_uri = em_folder_tree_get_selected_uri (folder_tree);
-	selection = em_folder_tree_model_get_selection (EM_FOLDER_TREE_MODEL (gtk_tree_view_get_model (GTK_TREE_VIEW (folder_tree))));
-	if (selection) {
-		GtkTreeIter iter;
-		GtkTreeModel *model = NULL;
-
-		if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
-			gboolean is_folder = FALSE;
-			CamelStore *store = NULL;
-
-			gtk_tree_model_get (model, &iter,
-				COL_BOOL_IS_FOLDER, &is_folder,
-				COL_POINTER_CAMEL_STORE, &store,
-				-1);
-
-			if (is_folder && !store) {
-				CamelFolder *folder = em_folder_tree_get_selected_folder (folder_tree);
-
-				if (folder)
-					store = camel_folder_get_parent_store (folder);
-			}
+	if (em_folder_tree_get_selected (folder_tree, &selected_store, &selected_path) ||
+	    em_folder_tree_store_root_selected (folder_tree, &selected_store)) {
+		if (selected_store) {
+			CamelProvider *provider = camel_service_get_provider (CAMEL_SERVICE (selected_store));
 
-			if (CAMEL_IS_SERVICE (store)) {
+			if (provider && g_ascii_strcasecmp (provider->protocol, "mapi") == 0) {
 				CamelService *service;
 				CamelSettings *settings;
 
-				service = CAMEL_SERVICE (store);
+				service = CAMEL_SERVICE (selected_store);
 				settings = camel_service_get_settings (service);
 				g_object_get (settings, "profile", &profile, NULL);
 
 				if (pstore && profile)
-					*pstore = g_object_ref (store);
+					*pstore = g_object_ref (selected_store);
+
+				if (pfolder_path)
+					*pfolder_path = selected_path;
+				else
+					g_free (selected_path);
+
+				selected_path = NULL;
 			}
+
+			g_object_unref (selected_store);
 		}
+
+		g_free (selected_path);
 	}
 
 	g_object_unref (folder_tree);
 
-	if (pfolder_uri)
-		*pfolder_uri = folder_uri;
-	else
-		g_free (folder_uri);
-
 	return profile;
 }
 
@@ -280,16 +270,12 @@ static void
 action_folder_size_cb (GtkAction *action,
 		       EShellView *shell_view)
 {
-	gchar *folder_uri = NULL;
-	gchar *profile = NULL;
-
-	profile = get_profile_name_from_folder_tree (shell_view, &folder_uri, NULL);
-	g_return_if_fail (folder_uri != NULL);
+	gchar *profile;
 
-	if (g_str_has_prefix (folder_uri, "mapi://"))
+	profile = get_profile_name_from_folder_tree (shell_view, NULL, NULL);
+	if (profile)
 		mapi_settings_run_folder_size_dialog (profile, NULL);
 
-	g_free (folder_uri);
 	g_free (profile);
 }
 
@@ -307,8 +293,6 @@ action_subscribe_foreign_folder_cb (GtkAction *action,
 	if (!profile)
 		return;
 
-	g_free (profile);
-
 	parent = GTK_WINDOW (e_shell_view_get_shell_window (shell_view));
 	backend = e_shell_view_get_shell_backend (shell_view);
 	g_object_get (G_OBJECT (backend), "session", &session, NULL);
@@ -317,8 +301,59 @@ action_subscribe_foreign_folder_cb (GtkAction *action,
 
 	g_object_unref (session);
 	g_object_unref (store);
+	g_free (profile);
 }
 
+static void
+action_folder_permissions_mail_cb (GtkAction *action,
+				   EShellView *shell_view)
+{
+	gchar *profile, *folder_path = NULL;
+	GtkWindow *parent;
+	CamelStore *store = NULL;
+	CamelMapiStore *mapi_store;
+	CamelNetworkSettings *network_settings;
+	CamelStoreInfo *si;
+
+	profile = get_profile_name_from_folder_tree (shell_view, &folder_path, &store);
+	if (!profile)
+		return;
+
+	mapi_store = CAMEL_MAPI_STORE (store);
+	g_return_if_fail (mapi_store != NULL);
+	g_return_if_fail (folder_path != NULL);
+
+	network_settings = CAMEL_NETWORK_SETTINGS (camel_service_get_settings (CAMEL_SERVICE (store)));
+	g_return_if_fail (network_settings != NULL);
+
+	parent = GTK_WINDOW (e_shell_view_get_shell_window (shell_view));
+
+	si = camel_store_summary_path (mapi_store->summary, folder_path);
+	if (!si) {
+		e_notice (parent, GTK_MESSAGE_ERROR, _("Cannot edit permissions of folder '%s', choose other folder."), folder_path);
+	} else {
+		CamelMapiStoreInfo *msi = (CamelMapiStoreInfo *) si;
+
+		e_mapi_edit_folder_permissions (parent,
+			profile,
+			camel_network_settings_get_user (network_settings),
+			camel_network_settings_get_host (network_settings),
+			camel_service_get_display_name (CAMEL_SERVICE (store)),
+			folder_path,
+			msi->folder_id,
+			(msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0 ? E_MAPI_FOLDER_CATEGORY_FOREIGN :
+			(msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) != 0 ? E_MAPI_FOLDER_CATEGORY_PUBLIC :
+			E_MAPI_FOLDER_CATEGORY_PERSONAL,
+			msi->foreign_username,
+			FALSE);
+	}
+
+	g_object_unref (store);
+	g_free (folder_path);
+}
+
+GtkWidget *org_gnome_e_mapi_settings (EPlugin *epl, EConfigHookItemFactoryData *data);
+
 /* used only in Account Editor */
 GtkWidget *
 org_gnome_e_mapi_settings (EPlugin *epl, EConfigHookItemFactoryData *data)
@@ -378,7 +413,32 @@ org_gnome_e_mapi_settings (EPlugin *epl, EConfigHookItemFactoryData *data)
 	return GTK_WIDGET (vsettings);
 }
 
-static GtkActionEntry folder_context_entries[] = {
+static void
+mapi_plugin_enable_actions (GtkActionGroup *action_group,
+			    const GtkActionEntry *entries,
+			    guint n_entries,
+			    gboolean can_show,
+			    gboolean is_online)
+{
+	gint ii;
+
+	g_return_if_fail (action_group != NULL);
+	g_return_if_fail (entries != NULL);
+
+	for (ii = 0; ii < n_entries; ii++) {
+		GtkAction *action;
+
+		action = gtk_action_group_get_action (action_group, entries[ii].name);
+		if (!action)
+			continue;
+
+		gtk_action_set_visible (action, can_show);
+		if (can_show)
+			gtk_action_set_sensitive (action, is_online);
+	}
+}
+
+static GtkActionEntry mail_account_context_entries[] = {
 
 	{ "mail-mapi-folder-size",
 	  NULL,
@@ -395,56 +455,53 @@ static GtkActionEntry folder_context_entries[] = {
 	  G_CALLBACK (action_subscribe_foreign_folder_cb) }
 };
 
+static GtkActionEntry mail_folder_context_entries[] = {
+	{ "mail-mapi-folder-permissions",
+	  "folder-new",
+	  N_("Permissions..."),
+	  NULL,
+	  N_("Edit MAPI folder permissions"),
+	  G_CALLBACK (action_folder_permissions_mail_cb) }
+};
+
 static void
-mapi_plugin_update_actions_cb (EShellView *shell_view,
-			       GtkActionEntry *entries)
+mapi_plugin_update_actions_mail_cb (EShellView *shell_view,
+				    GtkActionEntry *entries)
 {
 	EShellWindow *shell_window;
 	GtkActionGroup *action_group;
 	GtkUIManager *ui_manager;
 	EShellSidebar *shell_sidebar;
 	EMFolderTree *folder_tree;
-	gchar *folder_uri = NULL;
-	CamelURL *url = NULL;
-	gboolean show_menu_entry = FALSE;
-	gint ii;
-	GSList *actions = NULL, *iter;
+	CamelStore *selected_store = NULL;
+	gchar *selected_path = NULL;
+	gboolean account_node = FALSE, folder_node = FALSE;
 	gboolean online = FALSE;
 
 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
 	g_object_get (shell_sidebar, "folder-tree", &folder_tree, NULL);
-	folder_uri = em_folder_tree_get_selected_uri (folder_tree);
-	g_object_unref (folder_tree);
-	if (!(folder_uri && *folder_uri)) {
-		g_free (folder_uri);
-		return;
+	if (em_folder_tree_get_selected (folder_tree, &selected_store, &selected_path) ||
+	    em_folder_tree_store_root_selected (folder_tree, &selected_store)) {
+		if (selected_store) {
+			CamelProvider *provider = camel_service_get_provider (CAMEL_SERVICE (selected_store));
+
+			if (provider && g_ascii_strcasecmp (provider->protocol, "mapi") == 0) {
+				account_node = !selected_path || !*selected_path;
+				folder_node = !account_node;
+			}
+
+			g_object_unref (selected_store);
+		}
 	}
+	g_object_unref (folder_tree);
 
-	shell_window = e_shell_view_get_shell_window (shell_view);
+	g_free (selected_path);
 
+	shell_window = e_shell_view_get_shell_window (shell_view);
 	ui_manager = e_shell_window_get_ui_manager (shell_window);
 	action_group = e_lookup_action_group (ui_manager, "mail");
 
-	for (ii = 0; ii < G_N_ELEMENTS (folder_context_entries); ii++) {
-		GtkAction *action;
-
-		action = gtk_action_group_get_action (action_group, folder_context_entries[ii].name);
-		if (action)
-			actions = g_slist_prepend (actions, action);
-	}
-
-	/* Show / Hide action entry */
-	if (g_str_has_prefix (folder_uri, "mapi://")) {
-		show_menu_entry = TRUE;
-		url = camel_url_new (folder_uri, NULL);
-		if (url && url->path && strlen (url->path) > 1)
-			show_menu_entry = FALSE;
-		camel_url_free (url);
-	}
-
-	g_free (folder_uri);
-
-	if (show_menu_entry) {
+	if (account_node || folder_node) {
 		EShellBackend *backend;
 		CamelSession *session = NULL;
 
@@ -457,20 +514,15 @@ mapi_plugin_update_actions_cb (EShellView *shell_view,
 			g_object_unref (session);
 	}
 
-	for (iter = actions; iter; iter = iter->next) {
-		GtkAction *action = iter->data;
-
-		gtk_action_set_visible (action, show_menu_entry);
-		if (show_menu_entry)
-			gtk_action_set_sensitive (action, online);
-	}
-
-	g_slist_free (actions);
+	mapi_plugin_enable_actions (action_group, mail_account_context_entries, G_N_ELEMENTS (mail_account_context_entries), account_node, online);
+	mapi_plugin_enable_actions (action_group, mail_folder_context_entries, G_N_ELEMENTS (mail_folder_context_entries), folder_node, online);
 }
 
+gboolean mapi_ui_init_mail (GtkUIManager *ui_manager, EShellView *shell_view);
+
 gboolean
-e_plugin_ui_init (GtkUIManager *ui_manager,
-                  EShellView *shell_view)
+mapi_ui_init_mail (GtkUIManager *ui_manager,
+                   EShellView *shell_view)
 {
 	EShellWindow *shell_window;
 	GtkActionGroup *action_group;
@@ -480,14 +532,247 @@ e_plugin_ui_init (GtkUIManager *ui_manager,
 
 	/* Add actions to the "mail" action group. */
 	e_action_group_add_actions_localized (action_group, GETTEXT_PACKAGE,
-		folder_context_entries, G_N_ELEMENTS (folder_context_entries), shell_view);
+		mail_account_context_entries, G_N_ELEMENTS (mail_account_context_entries), shell_view);
+	e_action_group_add_actions_localized (action_group, GETTEXT_PACKAGE,
+		mail_folder_context_entries, G_N_ELEMENTS (mail_folder_context_entries), shell_view);
 
 	/* Decide whether we want this option to be visible or not */
 	g_signal_connect (shell_view, "update-actions",
-			  G_CALLBACK (mapi_plugin_update_actions_cb),
+			  G_CALLBACK (mapi_plugin_update_actions_mail_cb),
 			  shell_view);
 
 	g_object_unref (action_group);
 
 	return TRUE;
 }
+
+static gboolean
+get_selected_mapi_source (EShellView *shell_view,
+			  ESource **selected_source)
+{
+	ESource *source;
+	gchar *uri = NULL;
+	EShellSidebar *shell_sidebar;
+	ESourceSelector *selector = NULL;
+
+	g_return_val_if_fail (shell_view != NULL, FALSE);
+
+	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+	g_return_val_if_fail (shell_sidebar != NULL, FALSE);
+
+	g_object_get (shell_sidebar, "selector", &selector, NULL);
+	g_return_val_if_fail (selector != NULL, FALSE);
+
+	source = e_source_selector_peek_primary_selection (selector);
+	uri = source ? e_source_get_uri (source) : NULL;
+	if (uri && g_str_has_prefix (uri, "mapi://"))
+		source = g_object_ref (source);
+	else
+		source = NULL;
+
+	g_free (uri);
+	g_object_unref (selector);
+
+	if (selected_source)
+		*selected_source = source;
+	else if (source)
+		g_object_unref (source);
+
+	return source != NULL;
+}
+
+/* how many menu entries are defined; all calendar/tasks/memos/contacts
+   actions should have same count */
+#define MAPI_ESOURCE_NUM_ENTRIES 1
+
+static void
+update_mapi_source_entries_cb (EShellView *shell_view,
+			       GtkActionEntry *entries)
+{
+	GtkActionGroup *action_group;
+	EShell *shell;
+	EShellWindow *shell_window;
+	const gchar *group;
+	gboolean is_mapi_source, is_online;
+
+	g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
+	g_return_if_fail (entries != NULL);
+
+	if (strstr (entries->name, "calendar"))
+		group = "calendar";
+	else if (strstr (entries->name, "tasks"))
+		group = "tasks";
+	else if (strstr (entries->name, "memos"))
+		group = "memos";
+	else if (strstr (entries->name, "contacts"))
+		group = "contacts";
+	else
+		g_return_if_reached ();
+
+	is_mapi_source = get_selected_mapi_source (shell_view, NULL);
+	shell_window = e_shell_view_get_shell_window (shell_view);
+	shell = e_shell_window_get_shell (shell_window);
+
+	is_online = shell && e_shell_get_online (shell);
+	action_group = e_shell_window_get_action_group (shell_window, group);
+
+	mapi_plugin_enable_actions (action_group, entries, MAPI_ESOURCE_NUM_ENTRIES, is_mapi_source, is_online);
+}
+
+static void
+setup_mapi_source_actions (EShellView *shell_view,
+			   GtkActionEntry *entries,
+			   guint n_entries)
+{
+	EShellWindow *shell_window;
+	const gchar *group;
+
+	g_return_if_fail (shell_view != NULL);
+	g_return_if_fail (entries != NULL);
+	g_return_if_fail (n_entries > 0);
+	g_return_if_fail (n_entries == MAPI_ESOURCE_NUM_ENTRIES);
+
+	if (strstr (entries->name, "calendar"))
+		group = "calendar";
+	else if (strstr (entries->name, "tasks"))
+		group = "tasks";
+	else if (strstr (entries->name, "memos"))
+		group = "memos";
+	else if (strstr (entries->name, "contacts"))
+		group = "contacts";
+	else
+		g_return_if_reached ();
+
+	shell_window = e_shell_view_get_shell_window (shell_view);
+
+	e_action_group_add_actions_localized (
+		e_shell_window_get_action_group (shell_window, group), GETTEXT_PACKAGE,
+		entries, MAPI_ESOURCE_NUM_ENTRIES, shell_view);
+
+	g_signal_connect (shell_view, "update-actions", G_CALLBACK (update_mapi_source_entries_cb), entries);
+}
+
+static void
+action_folder_permissions_source_cb (GtkAction *action,
+				     EShellView *shell_view)
+{
+	ESource *source = NULL;
+	mapi_id_t folder_id = 0;
+	const gchar *foreign_username;
+	gboolean is_public;
+
+	g_return_if_fail (action != NULL);
+	g_return_if_fail (shell_view != NULL);
+	g_return_if_fail (get_selected_mapi_source (shell_view, &source));
+	g_return_if_fail (source != NULL);
+	g_return_if_fail (e_mapi_util_mapi_id_from_string (e_source_get_property (source, "folder-id"), &folder_id));
+	g_return_if_fail (gtk_action_get_name (action) != NULL);
+
+	foreign_username = e_source_get_property (source, "foreign-username");
+	is_public = !foreign_username && g_strcmp0 (e_source_get_property (source, "public"), "yes") == 0;
+
+	e_mapi_edit_folder_permissions (NULL,
+		e_source_get_property (source, "profile"),
+		e_source_get_property (source, "username"),
+		e_source_get_property (source, "host"),
+		e_source_group_peek_name (e_source_peek_group (source)),
+		e_source_peek_name (source),
+		folder_id,
+		foreign_username ? E_MAPI_FOLDER_CATEGORY_FOREIGN :
+		is_public ? E_MAPI_FOLDER_CATEGORY_PUBLIC :
+		E_MAPI_FOLDER_CATEGORY_PERSONAL,
+		foreign_username,
+		strstr (gtk_action_get_name (action), "calendar") != NULL);
+
+	g_object_unref (source);
+}
+
+static GtkActionEntry calendar_context_entries[] = {
+
+	{ "calendar-mapi-folder-permissions",
+	  "folder-new",
+	  N_("Permissions..."),
+	  NULL,
+	  N_("Edit MAPI calendar permissions"),
+	  G_CALLBACK (action_folder_permissions_source_cb) }
+};
+
+gboolean mapi_ui_init_calendar (GtkUIManager *ui_manager, EShellView *shell_view);
+
+gboolean
+mapi_ui_init_calendar (GtkUIManager *ui_manager,
+		       EShellView *shell_view)
+{
+	setup_mapi_source_actions (shell_view,
+		calendar_context_entries,
+		G_N_ELEMENTS (calendar_context_entries));
+
+	return TRUE;
+}
+
+static GtkActionEntry tasks_context_entries[] = {
+
+	{ "tasks-mapi-folder-permissions",
+	  "folder-new",
+	  N_("Permissions..."),
+	  NULL,
+	  N_("Edit MAPI tasks permissions"),
+	  G_CALLBACK (action_folder_permissions_source_cb) }
+};
+
+gboolean mapi_ui_init_tasks (GtkUIManager *ui_manager, EShellView *shell_view);
+
+gboolean
+mapi_ui_init_tasks (GtkUIManager *ui_manager,
+		    EShellView *shell_view)
+{
+	setup_mapi_source_actions (shell_view,
+		tasks_context_entries,
+		G_N_ELEMENTS (tasks_context_entries));
+
+	return TRUE;
+}
+static GtkActionEntry memos_context_entries[] = {
+
+	{ "memos-mapi-folder-permissions",
+	  "folder-new",
+	  N_("Permissions..."),
+	  NULL,
+	  N_("Edit MAPI memos permissions"),
+	  G_CALLBACK (action_folder_permissions_source_cb) }
+};
+
+gboolean mapi_ui_init_memos (GtkUIManager *ui_manager, EShellView *shell_view);
+
+gboolean
+mapi_ui_init_memos (GtkUIManager *ui_manager,
+		    EShellView *shell_view)
+{
+	setup_mapi_source_actions (shell_view,
+		memos_context_entries,
+		G_N_ELEMENTS (memos_context_entries));
+
+	return TRUE;
+}
+static GtkActionEntry contacts_context_entries[] = {
+
+	{ "contacts-mapi-folder-permissions",
+	  "folder-new",
+	  N_("Permissions..."),
+	  NULL,
+	  N_("Edit MAPI contacts permissions"),
+	  G_CALLBACK (action_folder_permissions_source_cb) }
+};
+
+gboolean mapi_ui_init_contacts (GtkUIManager *ui_manager, EShellView *shell_view);
+
+gboolean
+mapi_ui_init_contacts (GtkUIManager *ui_manager,
+		       EShellView *shell_view)
+{
+	setup_mapi_source_actions (shell_view,
+		contacts_context_entries,
+		G_N_ELEMENTS (contacts_context_entries));
+
+	return TRUE;
+}
diff --git a/src/account-setup-eplugin/e-mapi-account-setup.c b/src/account-setup-eplugin/e-mapi-account-setup.c
index 03d18ef..fd8af88 100644
--- a/src/account-setup-eplugin/e-mapi-account-setup.c
+++ b/src/account-setup-eplugin/e-mapi-account-setup.c
@@ -32,6 +32,7 @@
 
 #include <gtk/gtk.h>
 #include <libedataserver/e-xml-hash-utils.h>
+#include <libedataserver/e-credentials.h>
 #include <libedataserverui/e-passwords.h>
 #include <libedataserver/e-account.h>
 #include <e-util/e-dialog-utils.h>
@@ -248,7 +249,7 @@ static char*
 prompt_password(const gchar *user, const gchar *host, const gchar *key,
 		EMConfigTargetSettings *account)
 {
-	int pw_flags = E_PASSWORDS_REMEMBER_FOREVER|E_PASSWORDS_SECRET;
+	guint32 pw_flags = E_PASSWORDS_REMEMBER_FOREVER|E_PASSWORDS_SECRET;
 	gchar *password, *title;
 	gboolean save = TRUE;
 
@@ -1235,7 +1236,7 @@ e_mapi_run_in_thread_with_feedback (GtkWindow *parent,
 
 	dialog = gtk_dialog_new_with_buttons ("",
 		parent,
-		GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+		GTK_DIALOG_MODAL,
 		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 		NULL);
 
@@ -1264,3 +1265,96 @@ e_mapi_run_in_thread_with_feedback (GtkWindow *parent,
 
 	g_return_if_fail (g_thread_create (run_with_feedback_thread, rfd, FALSE, NULL));
 }
+
+EMapiConnection	*
+e_mapi_account_open_connection_for (GtkWindow *parent,
+				    const gchar *login_profile,
+				    const gchar *login_username,
+				    const gchar *login_url,
+				    GCancellable *cancellable,
+				    GError **perror)
+{
+	guint32 prompt_flags = E_PASSWORDS_SECRET | E_PASSWORDS_ONLINE | E_PASSWORDS_DISABLE_REMEMBER;
+	EMapiConnection *conn = NULL;
+	gchar *password = NULL;
+	SoupURI *suri;
+	gchar *key_str, *title;
+
+	g_return_val_if_fail (login_profile != NULL, NULL);
+	g_return_val_if_fail (login_username != NULL, NULL);
+	g_return_val_if_fail (login_url != NULL, NULL);
+
+	/* use the one from mailer, if there, otherwise open new */
+	conn = e_mapi_connection_find (login_profile);
+	if (conn)
+		return conn;
+
+	if (strchr (login_url, '/') != NULL) {
+		suri = soup_uri_new (login_url);
+	} else {
+		gchar *url = g_strconcat ("http://";, login_url, NULL);
+		suri = soup_uri_new (url);
+		g_free (url);
+	}
+
+	g_return_val_if_fail (suri != NULL, NULL);
+
+	soup_uri_set_user (suri, login_username);
+	soup_uri_set_password (suri, NULL);
+	soup_uri_set_fragment (suri, NULL);
+
+	key_str = soup_uri_to_string (suri, FALSE);
+	title = g_strdup_printf (_("Enter Password for %s %s"), soup_uri_get_user (suri), soup_uri_get_host (suri));
+
+	soup_uri_free (suri);
+
+	g_return_val_if_fail (key_str != NULL, NULL);
+
+	password = e_passwords_get_password (NULL, key_str);
+	if (!password)
+		password = e_passwords_ask_password (title, NULL, key_str, NULL, prompt_flags, NULL, parent);
+
+	prompt_flags |= E_PASSWORDS_REPROMPT;
+
+	do {
+		conn = e_mapi_connection_new (login_profile, password, cancellable, perror);
+
+		if (!conn && !g_cancellable_is_cancelled (cancellable)) {
+			e_credentials_util_safe_free_string (password);
+			password = e_passwords_ask_password (title, NULL, key_str, NULL, prompt_flags, NULL, parent);
+		}
+	} while (!conn && !g_cancellable_is_cancelled (cancellable));
+
+	e_credentials_util_safe_free_string (password);
+	g_free (key_str);
+	g_free (title);
+
+	return conn;
+}
+
+static gpointer
+unref_conn_in_thread (gpointer ptr)
+{
+	EMapiConnection *conn = ptr;
+
+	g_return_val_if_fail (conn != NULL, NULL);
+
+	g_object_unref (conn);
+
+	return NULL;
+}
+
+/* because this also disconnects from a server, which can take its time */
+void
+e_mapi_account_unref_conn_in_thread (EMapiConnection *conn)
+{
+	GError *error = NULL;
+
+	if (!conn)
+		return;
+
+	if (!g_thread_create (unref_conn_in_thread, conn, FALSE, &error)) {
+		g_warning ("%s: Failed to run thread: %s", G_STRFUNC, error ? error->message : "Unknown error");
+		g_object_unref (conn);
+	}
+}
diff --git a/src/account-setup-eplugin/e-mapi-account-setup.h b/src/account-setup-eplugin/e-mapi-account-setup.h
index 7551db8..3239872 100644
--- a/src/account-setup-eplugin/e-mapi-account-setup.h
+++ b/src/account-setup-eplugin/e-mapi-account-setup.h
@@ -27,6 +27,7 @@
 #include <gtk/gtk.h>
 
 #include "e-mapi-account-listener.h"
+#include <e-mapi-connection.h>
 
 #define MAPI_URI_PREFIX   "mapi://" 
 #define MAPI_PREFIX_LENGTH 7
@@ -46,4 +47,13 @@ void			e_mapi_run_in_thread_with_feedback	(GtkWindow *parent,
 								 gpointer user_data,
 								 GDestroyNotify free_user_data);
 
+EMapiConnection	*	e_mapi_account_open_connection_for	(GtkWindow *parent,
+								 const gchar *login_profile,
+								 const gchar *login_username,
+								 const gchar *login_url,
+								 GCancellable *cancellable,
+								 GError **perror);
+
+void			e_mapi_account_unref_conn_in_thread	(EMapiConnection *conn);
+
 #endif /* E_MAPI_ACCOUNT_SETUP_H */
diff --git a/src/account-setup-eplugin/e-mapi-edit-folder-permissions.c b/src/account-setup-eplugin/e-mapi-edit-folder-permissions.c
new file mode 100644
index 0000000..4692d1c
--- /dev/null
+++ b/src/account-setup-eplugin/e-mapi-edit-folder-permissions.c
@@ -0,0 +1,1283 @@
+/* -*- 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:
+ *    Milan Crha <mcrha redhat com>
+ *
+ * Copyright (C) 2012 Red Hat, Inc. (www.redhat.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include "e-mapi-account-setup.h"
+#include "e-mapi-edit-folder-permissions.h"
+#include "e-mapi-search-gal-user.h"
+#include "e-mapi-utils.h"
+
+#define E_MAPI_PERM_DLG_WIDGETS "e-mapi-perm-dlg-widgets"
+
+enum {
+	COL_NAME = 0,
+	COL_PERMISSION_LEVEL,
+	COL_E_MAPI_PERMISSION_ENTRY,
+	COL_E_MAPI_GAL_USER_TYPE,
+	COL_IS_NEW
+};
+
+struct EMapiPermissionsDialogWidgets
+{
+	gchar *login_profile;
+	gchar *login_username;
+	gchar *login_url;
+	mapi_id_t folder_id;
+	EMapiFolderCategory folder_category;
+	gchar *foreign_username;
+
+	EMapiConnection *conn;
+
+	gboolean updating;
+
+	GtkWidget *dialog;
+	GtkWidget *tree_view;
+
+	GtkWidget *add_button;
+	GtkWidget *remove_button;
+	GtkWidget *level_combo;
+
+	GtkWidget *read_none_radio;
+	GtkWidget *read_full_radio;
+	GtkWidget *read_fb_simple_check;
+	GtkWidget *read_fb_detail_check;
+
+	GtkWidget *write_create_items_check;
+	GtkWidget *write_create_subfolders_check;
+	GtkWidget *write_edit_own_check;
+	GtkWidget *write_edit_all_check;
+
+	GtkWidget *delete_none_radio;
+	GtkWidget *delete_own_radio;
+	GtkWidget *delete_all_radio;
+
+	GtkWidget *other_folder_owner_check;
+	GtkWidget *other_folder_contact_check;
+	GtkWidget *other_folder_visible_check;
+};
+
+static const struct EMapiPredefinedLevels {
+	const gchar *name;
+	uint32_t rights;
+} predefined_levels[] = {
+	{ NC_("PermissionsLevel", "None"), 0 },
+	{ NC_("PermissionsLevel", "Owner"), 	E_MAPI_PERMISSION_BIT_READ_ANY |
+						E_MAPI_PERMISSION_BIT_CREATE |
+						E_MAPI_PERMISSION_BIT_CREATE_SUBFOLDER |
+						E_MAPI_PERMISSION_BIT_EDIT_OWNED |
+						E_MAPI_PERMISSION_BIT_EDIT_ANY |
+						E_MAPI_PERMISSION_BIT_DELETE_OWNED |
+						E_MAPI_PERMISSION_BIT_DELETE_ANY |
+						E_MAPI_PERMISSION_BIT_FOLDER_OWNER |
+						E_MAPI_PERMISSION_BIT_FOLDER_CONTACT |
+						E_MAPI_PERMISSION_BIT_FOLDER_VISIBLE },
+	{ NC_("PermissionsLevel", "Publishing Editor"),
+						E_MAPI_PERMISSION_BIT_READ_ANY |
+						E_MAPI_PERMISSION_BIT_CREATE |
+						E_MAPI_PERMISSION_BIT_CREATE_SUBFOLDER |
+						E_MAPI_PERMISSION_BIT_EDIT_OWNED |
+						E_MAPI_PERMISSION_BIT_EDIT_ANY |
+						E_MAPI_PERMISSION_BIT_DELETE_OWNED |
+						E_MAPI_PERMISSION_BIT_DELETE_ANY |
+						E_MAPI_PERMISSION_BIT_FOLDER_VISIBLE  },
+	{ NC_("PermissionsLevel", "Editor"),
+						E_MAPI_PERMISSION_BIT_READ_ANY |
+						E_MAPI_PERMISSION_BIT_CREATE |
+						E_MAPI_PERMISSION_BIT_EDIT_OWNED |
+						E_MAPI_PERMISSION_BIT_EDIT_ANY |
+						E_MAPI_PERMISSION_BIT_DELETE_OWNED |
+						E_MAPI_PERMISSION_BIT_DELETE_ANY |
+						E_MAPI_PERMISSION_BIT_FOLDER_VISIBLE  },
+	{ NC_("PermissionsLevel", "Publishing Author"),
+						E_MAPI_PERMISSION_BIT_READ_ANY |
+						E_MAPI_PERMISSION_BIT_CREATE |
+						E_MAPI_PERMISSION_BIT_CREATE_SUBFOLDER |
+						E_MAPI_PERMISSION_BIT_EDIT_OWNED |
+						E_MAPI_PERMISSION_BIT_DELETE_OWNED |
+						E_MAPI_PERMISSION_BIT_FOLDER_VISIBLE  },
+	{ NC_("PermissionsLevel", "Author"),
+						E_MAPI_PERMISSION_BIT_READ_ANY |
+						E_MAPI_PERMISSION_BIT_CREATE |
+						E_MAPI_PERMISSION_BIT_EDIT_OWNED |
+						E_MAPI_PERMISSION_BIT_DELETE_OWNED |
+						E_MAPI_PERMISSION_BIT_FOLDER_VISIBLE  },
+	{ NC_("PermissionsLevel", "Nonediting Author"),
+						E_MAPI_PERMISSION_BIT_READ_ANY |
+						E_MAPI_PERMISSION_BIT_CREATE |
+						E_MAPI_PERMISSION_BIT_DELETE_OWNED |
+						E_MAPI_PERMISSION_BIT_FOLDER_VISIBLE  },
+	{ NC_("PermissionsLevel", "Reviewer"),
+						E_MAPI_PERMISSION_BIT_READ_ANY |
+						E_MAPI_PERMISSION_BIT_FOLDER_VISIBLE  },
+	{ NC_("PermissionsLevel", "Contributor"),
+						E_MAPI_PERMISSION_BIT_CREATE |
+						E_MAPI_PERMISSION_BIT_FOLDER_VISIBLE  },
+	{ NC_("PermissionsLevel", "Custom"), ~0 } /* make sure 'Custom' is always the last */
+};
+
+static void
+edit_permissions_widgets_free (gpointer ptr)
+{
+	struct EMapiPermissionsDialogWidgets *widgets = ptr;
+
+	if (!widgets)
+		return;
+
+	g_free (widgets->login_profile);
+	g_free (widgets->login_username);
+	g_free (widgets->login_url);
+	g_free (widgets->foreign_username);
+	if (widgets->conn)
+		e_mapi_account_unref_conn_in_thread (widgets->conn);
+	g_free (widgets);
+}
+
+static void
+folder_permissions_clear_all_entries (GObject *dialog)
+{
+	struct EMapiPermissionsDialogWidgets *widgets;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+
+	g_return_if_fail (dialog != NULL);
+
+	widgets = g_object_get_data (dialog, E_MAPI_PERM_DLG_WIDGETS);
+	g_return_if_fail (widgets != NULL);
+	g_return_if_fail (widgets->tree_view != NULL);
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (widgets->tree_view));
+	g_return_if_fail (model != NULL);
+
+	if (!gtk_tree_model_get_iter_first (model, &iter))
+		return;
+
+	do {
+		EMapiPermissionEntry *pem = NULL;
+
+		gtk_tree_model_get (model, &iter, COL_E_MAPI_PERMISSION_ENTRY, &pem, -1);
+
+		e_mapi_permission_entry_free (pem);
+	} while (gtk_tree_model_iter_next (model, &iter));
+
+	gtk_list_store_clear (GTK_LIST_STORE (model));
+}
+
+static void
+write_folder_permissions_thread (GObject *dialog,
+				 gpointer user_data,
+				 GCancellable *cancellable,
+				 GError **perror)
+{
+	struct EMapiPermissionsDialogWidgets *widgets;
+	mapi_object_t obj_folder;
+	gboolean opened;
+	const GSList *entries = user_data;
+
+	g_return_if_fail (dialog != NULL);
+
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
+
+	widgets = g_object_get_data (dialog, E_MAPI_PERM_DLG_WIDGETS);
+	g_return_if_fail (widgets != NULL);
+	g_return_if_fail (widgets->conn != NULL);
+
+	if (widgets->folder_category == E_MAPI_FOLDER_CATEGORY_FOREIGN)
+		opened = e_mapi_connection_open_foreign_folder (widgets->conn, widgets->foreign_username, widgets->folder_id, &obj_folder, cancellable, perror);
+	else if (widgets->folder_category == E_MAPI_FOLDER_CATEGORY_PUBLIC)
+		opened = e_mapi_connection_open_public_folder (widgets->conn, widgets->folder_id, &obj_folder, cancellable, perror);
+	else
+		opened = e_mapi_connection_open_personal_folder (widgets->conn, widgets->folder_id, &obj_folder, cancellable, perror);
+
+	if (opened) {
+		e_mapi_connection_set_permissions (widgets->conn, &obj_folder, widgets->read_fb_simple_check != NULL, entries, cancellable, perror);
+		e_mapi_connection_close_folder (widgets->conn, &obj_folder, cancellable, perror);
+	}
+}
+
+static void
+write_folder_permissions_idle (GObject *dialog,
+			       gpointer user_data,
+			       GCancellable *cancellable,
+			       GError **perror)
+{
+	/* does this only if no error was raised from the thread function */
+	folder_permissions_clear_all_entries (dialog);
+	gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+edit_permissions_response_cb (GObject *dialog,
+			      gint response_id)
+{
+	struct EMapiPermissionsDialogWidgets *widgets;
+	GSList *write_entries = NULL;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+
+	g_return_if_fail (dialog != NULL);
+
+	if (response_id != GTK_RESPONSE_OK) {
+		folder_permissions_clear_all_entries (dialog);
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+		return;
+	}
+
+	widgets = g_object_get_data (dialog, E_MAPI_PERM_DLG_WIDGETS);
+	g_return_if_fail (widgets != NULL);
+	g_return_if_fail (widgets->tree_view != NULL);
+	g_return_if_fail (widgets->conn != NULL);
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (widgets->tree_view));
+	g_return_if_fail (model != NULL);
+
+	if (gtk_tree_model_get_iter_first (model, &iter)) {
+		do {
+			EMapiPermissionEntry *pem = NULL;
+
+			gtk_tree_model_get (model, &iter, COL_E_MAPI_PERMISSION_ENTRY, &pem, -1);
+
+			if (pem)
+				write_entries = g_slist_prepend (write_entries, pem);
+		} while (gtk_tree_model_iter_next (model, &iter));
+
+		write_entries = g_slist_reverse (write_entries);
+	}
+
+	e_mapi_run_in_thread_with_feedback (GTK_WINDOW (dialog), dialog,
+		_("Writing folder permissions, please wait..."),
+		write_folder_permissions_thread,
+		write_folder_permissions_idle,
+		write_entries, (GDestroyNotify) g_slist_free);
+}
+
+static void
+enable_all_widgets (struct EMapiPermissionsDialogWidgets *widgets,
+		    gboolean enabled)
+{
+	g_return_if_fail (widgets != NULL);
+
+	gtk_widget_set_sensitive (widgets->add_button, enabled || gtk_widget_get_sensitive (widgets->tree_view));
+	gtk_widget_set_sensitive (widgets->remove_button, enabled);
+	gtk_widget_set_sensitive (widgets->level_combo, enabled);
+	gtk_widget_set_sensitive (widgets->read_none_radio, enabled);
+	gtk_widget_set_sensitive (widgets->read_full_radio, enabled);
+	if (widgets->read_fb_simple_check)
+		gtk_widget_set_sensitive (widgets->read_fb_simple_check, enabled);
+	if (widgets->read_fb_detail_check)
+		gtk_widget_set_sensitive (widgets->read_fb_detail_check, enabled);
+	gtk_widget_set_sensitive (widgets->write_create_items_check, enabled);
+	gtk_widget_set_sensitive (widgets->write_create_subfolders_check, enabled);
+	gtk_widget_set_sensitive (widgets->write_edit_own_check, enabled);
+	gtk_widget_set_sensitive (widgets->write_edit_all_check, enabled);
+	gtk_widget_set_sensitive (widgets->delete_none_radio, enabled);
+	gtk_widget_set_sensitive (widgets->delete_own_radio, enabled);
+	gtk_widget_set_sensitive (widgets->delete_all_radio, enabled);
+	gtk_widget_set_sensitive (widgets->other_folder_owner_check, enabled);
+	gtk_widget_set_sensitive (widgets->other_folder_contact_check, enabled);
+	gtk_widget_set_sensitive (widgets->other_folder_visible_check, enabled);
+}
+
+static uint32_t
+folder_permissions_dialog_to_rights (GObject *dialog)
+{
+	struct EMapiPermissionsDialogWidgets *widgets;
+	uint32_t rights;
+
+	g_return_val_if_fail (dialog != NULL, 0);
+
+	widgets = g_object_get_data (dialog, E_MAPI_PERM_DLG_WIDGETS);
+	g_return_val_if_fail (widgets != NULL, 0);
+
+	#define set_bit_by_active(x, bt) G_STMT_START {					\
+		if (widgets->x &&							\
+		    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widgets->x)) &&	\
+		    gtk_widget_get_sensitive (widgets->x)) {				\
+			rights |= bt;							\
+		} } G_STMT_END
+
+	rights = 0;
+
+	set_bit_by_active (read_none_radio, 0);
+	set_bit_by_active (read_full_radio, E_MAPI_PERMISSION_BIT_READ_ANY);
+	set_bit_by_active (read_fb_simple_check, E_MAPI_PERMISSION_BIT_FREE_BUSY_SIMPLE);
+	set_bit_by_active (read_fb_detail_check, E_MAPI_PERMISSION_BIT_FREE_BUSY_DETAILED);
+	set_bit_by_active (write_create_items_check, E_MAPI_PERMISSION_BIT_CREATE);
+	set_bit_by_active (write_create_subfolders_check, E_MAPI_PERMISSION_BIT_CREATE_SUBFOLDER);
+	set_bit_by_active (write_edit_own_check, E_MAPI_PERMISSION_BIT_EDIT_OWNED);
+	set_bit_by_active (write_edit_all_check, E_MAPI_PERMISSION_BIT_EDIT_ANY | E_MAPI_PERMISSION_BIT_EDIT_OWNED);
+	set_bit_by_active (delete_none_radio, 0);
+	set_bit_by_active (delete_own_radio, E_MAPI_PERMISSION_BIT_DELETE_OWNED);
+	set_bit_by_active (delete_all_radio, E_MAPI_PERMISSION_BIT_DELETE_ANY | E_MAPI_PERMISSION_BIT_DELETE_OWNED);
+	set_bit_by_active (other_folder_owner_check, E_MAPI_PERMISSION_BIT_FOLDER_OWNER);
+	set_bit_by_active (other_folder_contact_check, E_MAPI_PERMISSION_BIT_FOLDER_CONTACT);
+	set_bit_by_active (other_folder_visible_check, E_MAPI_PERMISSION_BIT_FOLDER_VISIBLE);
+
+	#undef set_bit_by_active
+
+	return rights;
+}
+
+static void
+update_folder_permissions_sensitivity (GObject *dialog,
+				       gboolean member_valid,
+				       EMapiGalUserType user_type)
+{
+	struct EMapiPermissionsDialogWidgets *widgets;
+
+	g_return_if_fail (dialog != NULL);
+
+	widgets = g_object_get_data (dialog, E_MAPI_PERM_DLG_WIDGETS);
+	g_return_if_fail (widgets != NULL);
+
+	enable_all_widgets (widgets, member_valid);
+
+	if (user_type == E_MAPI_GAL_USER_DEFAULT ||
+	    user_type == E_MAPI_GAL_USER_ANONYMOUS)
+		gtk_widget_set_sensitive (widgets->other_folder_contact_check, FALSE);
+
+	if (member_valid)
+		gtk_widget_set_sensitive (widgets->remove_button,
+			user_type != E_MAPI_GAL_USER_DEFAULT &&
+			user_type != E_MAPI_GAL_USER_ANONYMOUS);
+
+	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widgets->write_edit_all_check))) {
+		gtk_widget_set_sensitive (widgets->write_edit_own_check, FALSE);
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widgets->write_edit_own_check), TRUE);
+	}
+}
+
+static void
+update_folder_permissions_by_rights (GObject *dialog,
+				     uint32_t rights)
+{
+	struct EMapiPermissionsDialogWidgets *widgets;
+
+	g_return_if_fail (dialog != NULL);
+
+	widgets = g_object_get_data (dialog, E_MAPI_PERM_DLG_WIDGETS);
+	g_return_if_fail (widgets != NULL);
+
+	#define set_active(x, act) G_STMT_START { if (widgets->x) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widgets->x), act); } G_STMT_END
+	#define set_active_by_bit(x, bt) set_active (x, (rights & (bt)) != 0)
+
+	widgets->updating = TRUE;
+
+	set_active (read_none_radio, TRUE);
+	set_active_by_bit (read_full_radio, E_MAPI_PERMISSION_BIT_READ_ANY);
+	set_active_by_bit (read_fb_simple_check, E_MAPI_PERMISSION_BIT_FREE_BUSY_SIMPLE);
+	set_active_by_bit (read_fb_detail_check, E_MAPI_PERMISSION_BIT_FREE_BUSY_DETAILED);
+	set_active_by_bit (write_create_items_check, E_MAPI_PERMISSION_BIT_CREATE);
+	set_active_by_bit (write_create_subfolders_check, E_MAPI_PERMISSION_BIT_CREATE_SUBFOLDER);
+	set_active_by_bit (write_edit_own_check, E_MAPI_PERMISSION_BIT_EDIT_OWNED | E_MAPI_PERMISSION_BIT_EDIT_ANY);
+	set_active_by_bit (write_edit_all_check, E_MAPI_PERMISSION_BIT_EDIT_ANY);
+	set_active (delete_none_radio, TRUE);
+	set_active_by_bit (delete_own_radio, E_MAPI_PERMISSION_BIT_DELETE_OWNED);
+	set_active_by_bit (delete_all_radio, E_MAPI_PERMISSION_BIT_DELETE_ANY);
+	set_active_by_bit (other_folder_owner_check, E_MAPI_PERMISSION_BIT_FOLDER_OWNER);
+	set_active_by_bit (other_folder_contact_check, E_MAPI_PERMISSION_BIT_FOLDER_CONTACT);
+	set_active_by_bit (other_folder_visible_check, E_MAPI_PERMISSION_BIT_FOLDER_VISIBLE);
+
+	if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widgets->write_edit_all_check)) &&
+	    gtk_widget_get_sensitive (widgets->write_edit_all_check)) {
+		gtk_widget_set_sensitive (widgets->write_edit_own_check, TRUE);
+	} else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widgets->write_edit_all_check))) {
+		gtk_widget_set_sensitive (widgets->write_edit_own_check, FALSE);
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widgets->write_edit_own_check), TRUE);
+	}
+
+	if (widgets->read_fb_simple_check && widgets->read_fb_detail_check) {
+		if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widgets->read_fb_detail_check)) &&
+		    gtk_widget_get_sensitive (widgets->read_fb_detail_check)) {
+			gtk_widget_set_sensitive (widgets->read_fb_simple_check, TRUE);
+		} else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widgets->read_fb_detail_check))) {
+			gtk_widget_set_sensitive (widgets->read_fb_simple_check, FALSE);
+			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widgets->read_fb_simple_check), TRUE);
+		}
+	}
+
+	widgets->updating = FALSE;
+
+	#undef set_active_by_bit
+	#undef set_active
+}
+
+static void
+update_folder_permissions_tree_view (GObject *dialog,
+				     struct EMapiPermissionsDialogWidgets *widgets)
+{
+	GtkTreeSelection *selection;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	uint32_t rights;
+
+	g_return_if_fail (dialog != NULL);
+	g_return_if_fail (widgets != NULL);
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widgets->tree_view));
+	if (selection && gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		gchar *combo_text;
+		EMapiPermissionEntry *pem = NULL;
+
+		combo_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (widgets->level_combo));
+		rights = folder_permissions_dialog_to_rights (dialog);
+
+		gtk_tree_model_get (model, &iter, COL_E_MAPI_PERMISSION_ENTRY, &pem, -1);
+
+		if (pem) {
+			if (!widgets->read_fb_simple_check)
+				rights = rights | (pem->member_rights & (E_MAPI_PERMISSION_BIT_FREE_BUSY_DETAILED | E_MAPI_PERMISSION_BIT_FREE_BUSY_SIMPLE));
+
+			pem->member_rights = rights;
+
+			gtk_list_store_set (GTK_LIST_STORE (model), &iter, COL_PERMISSION_LEVEL, combo_text, -1);
+		}
+
+		g_free (combo_text);
+	}
+}
+
+static void
+update_permission_level_combo_by_dialog (GObject *dialog)
+{
+	struct EMapiPermissionsDialogWidgets *widgets;
+	uint32_t rights;
+	gint ii;
+
+	g_return_if_fail (dialog != NULL);
+
+	widgets = g_object_get_data (dialog, E_MAPI_PERM_DLG_WIDGETS);
+	g_return_if_fail (widgets != NULL);
+
+	if (widgets->updating)
+		return;
+
+	rights = folder_permissions_dialog_to_rights (dialog);
+	rights = rights & ~(E_MAPI_PERMISSION_BIT_FREE_BUSY_DETAILED |
+		E_MAPI_PERMISSION_BIT_FREE_BUSY_SIMPLE);
+
+	for (ii = 0; ii < G_N_ELEMENTS (predefined_levels) - 1; ii++) {
+		if (predefined_levels[ii].rights == rights) {
+			break;
+		}
+	}
+
+	/* ii points to the matched or the last item, which is 'Custom' */
+	widgets->updating = TRUE;
+	gtk_combo_box_set_active (GTK_COMBO_BOX (widgets->level_combo), ii);
+
+	if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widgets->write_edit_all_check)) &&
+	    gtk_widget_get_sensitive (widgets->write_edit_all_check)) {
+		gtk_widget_set_sensitive (widgets->write_edit_own_check, TRUE);
+		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widgets->write_edit_own_check))) {
+			rights |= E_MAPI_PERMISSION_BIT_EDIT_OWNED;
+
+			for (ii = 0; ii < G_N_ELEMENTS (predefined_levels) - 1; ii++) {
+				if (predefined_levels[ii].rights == rights) {
+					break;
+				}
+			}
+
+			gtk_combo_box_set_active (GTK_COMBO_BOX (widgets->level_combo), ii);
+		}
+	} else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widgets->write_edit_all_check))) {
+		gtk_widget_set_sensitive (widgets->write_edit_own_check, FALSE);
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widgets->write_edit_own_check), TRUE);
+	}
+
+	if (widgets->read_fb_simple_check && widgets->read_fb_detail_check) {
+		if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widgets->read_fb_detail_check)) &&
+		    gtk_widget_get_sensitive (widgets->read_fb_detail_check)) {
+			gtk_widget_set_sensitive (widgets->read_fb_simple_check, TRUE);
+		} else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widgets->read_fb_detail_check))) {
+			gtk_widget_set_sensitive (widgets->read_fb_simple_check, FALSE);
+			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widgets->read_fb_simple_check), TRUE);
+		}
+	}
+
+	update_folder_permissions_tree_view (dialog, widgets);
+
+	widgets->updating = FALSE;
+}
+
+static void
+update_permission_dialog_by_level_combo (GObject *dialog)
+{
+	struct EMapiPermissionsDialogWidgets *widgets;
+	uint32_t rights;
+	gint ii;
+
+	g_return_if_fail (dialog != NULL);
+
+	widgets = g_object_get_data (dialog, E_MAPI_PERM_DLG_WIDGETS);
+	g_return_if_fail (widgets != NULL);
+
+	if (widgets->updating)
+		return;
+
+	ii = gtk_combo_box_get_active (GTK_COMBO_BOX (widgets->level_combo));
+	if (ii < 0 || ii >= G_N_ELEMENTS (predefined_levels) - 1)
+		return;
+
+	rights = folder_permissions_dialog_to_rights (dialog);
+	rights = predefined_levels[ii].rights | (rights & (E_MAPI_PERMISSION_BIT_FREE_BUSY_DETAILED |
+		E_MAPI_PERMISSION_BIT_FREE_BUSY_SIMPLE));
+
+	widgets->updating = TRUE;
+	update_folder_permissions_by_rights (dialog, rights);
+	update_folder_permissions_tree_view (dialog, widgets);
+	widgets->updating = FALSE;
+}
+
+static void
+add_button_clicked_cb (GObject *dialog)
+{
+	struct EMapiPermissionsDialogWidgets *widgets;
+	gchar *display_name = NULL;
+	struct SBinary_short *entry_id = NULL;
+	EMapiGalUserType user_type = E_MAPI_GAL_USER_NONE;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+
+	g_return_if_fail (dialog != NULL);
+
+	widgets = g_object_get_data (dialog, E_MAPI_PERM_DLG_WIDGETS);
+	g_return_if_fail (widgets != NULL);
+
+	if (widgets->updating)
+		return;
+
+	g_return_if_fail (widgets->tree_view != NULL);
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (widgets->tree_view));
+	g_return_if_fail (model != NULL);
+
+	if (e_mapi_search_gal_user_modal (GTK_WINDOW (dialog), widgets->conn, NULL, &user_type, &display_name, NULL, NULL, &entry_id)) {
+		EMapiPermissionEntry *pem;
+		GtkTreeSelection *selection;
+		gboolean found = FALSE;
+
+		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widgets->tree_view));
+		g_return_if_fail (selection != NULL);
+
+		if (gtk_tree_model_get_iter_first (model, &iter)) {
+			do {
+				EMapiGalUserType ut = E_MAPI_GAL_USER_NONE;
+
+				pem = NULL;
+
+				gtk_tree_model_get (model, &iter,
+					COL_E_MAPI_PERMISSION_ENTRY, &pem,
+					COL_E_MAPI_GAL_USER_TYPE, &ut,
+					-1);
+
+				if ((ut != E_MAPI_GAL_USER_REGULAR && ut == user_type) ||
+				    (ut == E_MAPI_GAL_USER_REGULAR && pem && e_mapi_util_recip_entryid_equal (&pem->entry_id, entry_id))) {
+					gtk_tree_selection_select_iter (selection, &iter);
+					found = TRUE;
+					break;
+				}
+			} while (gtk_tree_model_iter_next (model, &iter));
+		}
+
+		if (!found) {
+			GtkListStore *store = GTK_LIST_STORE (model);
+
+			pem = e_mapi_permission_entry_new (display_name, entry_id,
+				user_type == E_MAPI_GAL_USER_ANONYMOUS ? E_MAPI_PERMISSION_MEMBER_ID_ANONYMOUS_CLIENT :
+				user_type == E_MAPI_GAL_USER_DEFAULT ? E_MAPI_PERMISSION_MEMBER_ID_DEFAULT_USER : 0,
+				user_type == E_MAPI_GAL_USER_ANONYMOUS ? 0 : E_MAPI_PERMISSION_BIT_FREE_BUSY_SIMPLE);
+
+			gtk_list_store_append (store, &iter);
+			gtk_list_store_set (store, &iter,
+				COL_NAME, pem->username,
+				COL_PERMISSION_LEVEL, g_dgettext ("PermissionsLevel", predefined_levels[0].name),
+				COL_E_MAPI_PERMISSION_ENTRY, pem,
+				COL_E_MAPI_GAL_USER_TYPE, user_type,
+				COL_IS_NEW, TRUE,
+				-1);
+
+			gtk_tree_selection_select_iter (selection, &iter);
+		}
+	}
+
+	g_free (display_name);
+	if (entry_id) {
+		g_free (entry_id->lpb);
+		g_free (entry_id);
+	}
+}
+
+static void
+remove_button_clicked_cb (GObject *dialog)
+{
+	struct EMapiPermissionsDialogWidgets *widgets;
+	GtkTreeSelection *selection;
+	GtkTreeModel *model = NULL;
+	GtkTreeIter iter;
+
+	g_return_if_fail (dialog != NULL);
+
+	widgets = g_object_get_data (dialog, E_MAPI_PERM_DLG_WIDGETS);
+	g_return_if_fail (widgets != NULL);
+
+	if (widgets->updating)
+		return;
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widgets->tree_view));
+	if (selection && gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		GtkTreeIter select;
+		gboolean can_select = FALSE;
+		EMapiPermissionEntry *pem = NULL;
+
+		select = iter;
+		can_select = gtk_tree_model_iter_next (model, &select);
+		if (!can_select) {
+			select = iter;
+			can_select = gtk_tree_model_iter_previous (model, &select);
+		}
+
+		if (can_select)
+			gtk_tree_selection_select_iter (selection, &select);
+
+		gtk_tree_model_get (model, &iter, COL_E_MAPI_PERMISSION_ENTRY, &pem, -1);
+
+		if (gtk_list_store_remove (GTK_LIST_STORE (model), &iter))
+			e_mapi_permission_entry_free (pem);
+	}
+}
+
+static void
+folder_permissions_free_found_entries (gpointer ptr)
+{
+	GSList **entries = ptr;
+
+	if (!entries)
+		return;
+
+	g_slist_free_full (*entries, (GDestroyNotify) e_mapi_permission_entry_free);
+	*entries = NULL;
+	g_free (entries);
+}
+
+static void
+read_folder_permissions_thread (GObject *dialog,
+				gpointer user_data,
+				GCancellable *cancellable,
+				GError **perror)
+{
+	struct EMapiPermissionsDialogWidgets *widgets;
+	mapi_object_t obj_folder;
+	gboolean opened;
+	GSList **pentries = user_data;
+
+	g_return_if_fail (dialog != NULL);
+	g_return_if_fail (pentries != NULL);
+
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
+
+	widgets = g_object_get_data (dialog, E_MAPI_PERM_DLG_WIDGETS);
+	g_return_if_fail (widgets != NULL);
+	g_return_if_fail (widgets->login_profile != NULL);
+	g_return_if_fail (widgets->login_username != NULL);
+	g_return_if_fail (widgets->login_url != NULL);
+
+	widgets->conn = e_mapi_account_open_connection_for (GTK_WINDOW (dialog),
+		widgets->login_profile,
+		widgets->login_username,
+		widgets->login_url,
+		cancellable,
+		perror);
+
+	if (!widgets->conn)
+		g_cancellable_cancel (cancellable);
+
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
+
+	if (widgets->folder_category == E_MAPI_FOLDER_CATEGORY_FOREIGN)
+		opened = e_mapi_connection_open_foreign_folder (widgets->conn, widgets->foreign_username, widgets->folder_id, &obj_folder, cancellable, perror);
+	else if (widgets->folder_category == E_MAPI_FOLDER_CATEGORY_PUBLIC)
+		opened = e_mapi_connection_open_public_folder (widgets->conn, widgets->folder_id, &obj_folder, cancellable, perror);
+	else
+		opened = e_mapi_connection_open_personal_folder (widgets->conn, widgets->folder_id, &obj_folder, cancellable, perror);
+
+	if (opened) {
+		e_mapi_connection_get_permissions (widgets->conn, &obj_folder, widgets->read_fb_simple_check != NULL, pentries, cancellable, perror);
+		e_mapi_connection_close_folder (widgets->conn, &obj_folder, cancellable, perror);
+	}
+}
+
+static void
+read_folder_permissions_idle (GObject *dialog,
+			      gpointer user_data,
+			      GCancellable *cancellable,
+			      GError **perror)
+{
+	struct EMapiPermissionsDialogWidgets *widgets;
+	GSList **pentries = user_data;
+	GSList *entry;
+	GtkListStore *store;
+	GtkTreeIter iter;
+
+	g_return_if_fail (dialog != NULL);
+	g_return_if_fail (pentries != NULL);
+
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
+
+	widgets = g_object_get_data (dialog, E_MAPI_PERM_DLG_WIDGETS);
+	g_return_if_fail (widgets != NULL);
+	g_return_if_fail (widgets->tree_view != NULL);
+
+	store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (widgets->tree_view)));
+	g_return_if_fail (store != NULL);
+
+	for (entry = *pentries; entry; entry = entry->next) {
+		EMapiPermissionEntry *pem = entry->data;
+		EMapiGalUserType user_type;
+		const gchar *perm_level;
+		uint32_t rights;
+		gint ii;
+
+		if (!pem)
+			continue;
+
+		/* steal the pem */
+		entry->data = NULL;
+
+		if (!pem->username || !*pem->username) {
+			g_free (pem->username);
+
+			if (pem->member_id == E_MAPI_PERMISSION_MEMBER_ID_ANONYMOUS_CLIENT)
+				pem->username = g_strdup (C_("User", "Anonymous"));
+			else if (pem->member_id == E_MAPI_PERMISSION_MEMBER_ID_DEFAULT_USER)
+				pem->username = g_strdup (C_("User", "Default"));
+			else
+				pem->username = g_strdup (C_("User", "Unknown"));
+		}
+
+		rights = pem->member_rights;
+		rights = rights & ~(E_MAPI_PERMISSION_BIT_FREE_BUSY_DETAILED |
+			E_MAPI_PERMISSION_BIT_FREE_BUSY_SIMPLE);
+
+		for (ii = 0; ii < G_N_ELEMENTS (predefined_levels) - 1; ii++) {
+			if (predefined_levels[ii].rights == rights) {
+				break;
+			}
+		}
+
+		perm_level = g_dgettext ("PermissionsLevel", predefined_levels[ii].name);
+
+		user_type = E_MAPI_GAL_USER_REGULAR;
+		if (pem->member_id == E_MAPI_PERMISSION_MEMBER_ID_ANONYMOUS_CLIENT)
+			user_type = E_MAPI_GAL_USER_ANONYMOUS;
+		else if (pem->member_id == E_MAPI_PERMISSION_MEMBER_ID_DEFAULT_USER)
+			user_type = E_MAPI_GAL_USER_DEFAULT;
+
+		gtk_list_store_append (store, &iter);
+		gtk_list_store_set (store, &iter,
+			COL_NAME, pem->username,
+			COL_PERMISSION_LEVEL, perm_level,
+			COL_E_MAPI_PERMISSION_ENTRY, pem,
+			COL_E_MAPI_GAL_USER_TYPE, user_type,
+			COL_IS_NEW, FALSE,
+			-1);
+	}
+
+	gtk_widget_set_sensitive (widgets->add_button, TRUE);
+	gtk_dialog_set_response_sensitive (GTK_DIALOG (widgets->dialog), GTK_RESPONSE_OK, TRUE);
+}
+
+static void
+folder_permissions_tree_selection_changed_cb (GtkTreeSelection *selection,
+					      struct EMapiPermissionsDialogWidgets *widgets)
+{
+	GObject *dialog;
+	GtkTreeModel *model = NULL;
+	GtkTreeIter iter;
+	gboolean has_selected;
+
+	g_return_if_fail (selection != NULL);
+	g_return_if_fail (widgets != NULL);
+	g_return_if_fail (widgets->dialog != NULL);
+	g_return_if_fail (widgets->add_button != NULL);
+	g_return_if_fail (widgets->remove_button != NULL);
+
+	dialog = G_OBJECT (widgets->dialog);
+	has_selected = gtk_tree_selection_get_selected (selection, &model, &iter);
+
+	gtk_widget_set_sensitive (widgets->add_button, TRUE);
+	gtk_widget_set_sensitive (widgets->remove_button, has_selected);
+
+	if (has_selected) {
+		EMapiGalUserType user_type = E_MAPI_GAL_USER_NONE;
+		EMapiPermissionEntry *pem = NULL;
+
+		gtk_tree_model_get (model, &iter,
+			COL_E_MAPI_PERMISSION_ENTRY, &pem,
+			COL_E_MAPI_GAL_USER_TYPE, &user_type,
+			-1);
+
+		update_folder_permissions_sensitivity (dialog, pem != NULL, user_type);
+		update_folder_permissions_by_rights (dialog, pem ? pem->member_rights : 0);
+	} else {
+		update_folder_permissions_sensitivity (dialog, FALSE, E_MAPI_GAL_USER_NONE);
+		update_folder_permissions_by_rights (dialog, 0);
+	}
+
+	update_permission_level_combo_by_dialog (dialog);
+}
+
+static GtkWidget *
+create_permissions_tree_view (GObject *dialog,
+			      struct EMapiPermissionsDialogWidgets *widgets)
+{
+	GtkTreeView *tree_view;
+	GtkTreeSelection *selection;
+	GtkCellRenderer *renderer;
+	GtkTreeViewColumn *column;
+	gint pos;
+
+	g_return_val_if_fail (widgets != NULL, NULL);
+
+	tree_view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (
+		GTK_TREE_MODEL (gtk_list_store_new (5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_BOOLEAN))));
+
+	renderer = gtk_cell_renderer_text_new ();
+	g_object_set (renderer, "editable", FALSE, NULL);
+	pos = gtk_tree_view_insert_column_with_attributes (tree_view, -1, _("Name"), renderer, "text", COL_NAME, NULL);
+	column = gtk_tree_view_get_column (tree_view, pos - 1);
+	gtk_tree_view_column_set_expand (column, TRUE);
+
+	renderer = gtk_cell_renderer_text_new ();
+	g_object_set (renderer, "editable", FALSE, NULL);
+	gtk_tree_view_insert_column_with_attributes (tree_view, -1, _("Permission level"), renderer, "text", COL_PERMISSION_LEVEL, NULL);
+
+	selection = gtk_tree_view_get_selection (tree_view);
+	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+	g_signal_connect (selection, "changed", G_CALLBACK (folder_permissions_tree_selection_changed_cb), widgets);
+
+	widgets->tree_view = GTK_WIDGET (tree_view);
+
+	return widgets->tree_view;
+}
+
+/* Opens dialog to subscribe to folders of other
+   users in the given store */
+void
+e_mapi_edit_folder_permissions (GtkWindow *parent,
+				const gchar *login_profile,
+				const gchar *login_username,
+				const gchar *login_url,
+				const gchar *account_name,
+				const gchar *folder_name,
+				mapi_id_t folder_id,
+				EMapiFolderCategory folder_category,
+				const gchar *foreign_username,
+				gboolean with_freebusy)
+{
+	struct EMapiPermissionsDialogWidgets *widgets;
+	PangoAttrList *attrs;
+	GObject *dialog;
+	GtkWidget *content;
+	GtkWidget *label, *widget, *button, *frame, *hvbox;
+	GtkScrolledWindow *scrolled_window;
+	GtkComboBoxText *combo_text;
+	GtkGrid *grid;
+	GSList *radio_group, **found_entries;
+	gchar *str;
+	gint row, ii;
+
+	g_return_if_fail (login_profile != NULL);
+	g_return_if_fail (login_username != NULL);
+	g_return_if_fail (login_url != NULL);
+	g_return_if_fail (account_name != NULL);
+	g_return_if_fail (folder_name != NULL);
+	g_return_if_fail (folder_id != 0);
+
+	widgets = g_new0 (struct EMapiPermissionsDialogWidgets, 1);
+	widgets->login_profile = g_strdup (login_profile);
+	widgets->login_username = g_strdup (login_username);
+	widgets->login_url = g_strdup (login_url);
+	widgets->folder_id = folder_id;
+	widgets->folder_category = folder_category;
+	widgets->foreign_username = g_strdup (foreign_username);
+
+	widgets->dialog = gtk_dialog_new_with_buttons (
+		_("Edit MAPI folder permissions..."),
+		parent,
+		GTK_DIALOG_DESTROY_WITH_PARENT,
+		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+		GTK_STOCK_OK, GTK_RESPONSE_OK,
+		NULL);
+
+	dialog = G_OBJECT (widgets->dialog);
+	g_signal_connect (dialog, "response", G_CALLBACK (edit_permissions_response_cb), NULL);
+	g_object_set_data_full (dialog, E_MAPI_PERM_DLG_WIDGETS, widgets, edit_permissions_widgets_free);
+
+	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+	content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+	grid = GTK_GRID (gtk_grid_new ());
+	gtk_grid_set_row_homogeneous (grid, FALSE);
+	gtk_grid_set_row_spacing (grid, 6);
+	gtk_grid_set_column_homogeneous (grid, FALSE);
+	gtk_grid_set_column_spacing (grid, 6);
+	gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
+	gtk_container_add (GTK_CONTAINER (content), GTK_WIDGET (grid));
+
+	row = 0;
+
+	label = gtk_label_new (_("Account:"));
+	g_object_set (G_OBJECT (label),
+		"hexpand", FALSE,
+		"vexpand", FALSE,
+		"xalign", 0.0,
+		"halign", GTK_ALIGN_START,
+		NULL);
+
+	attrs = pango_attr_list_new ();
+	pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
+	widget = gtk_label_new (account_name);
+	g_object_set (G_OBJECT (widget),
+		"hexpand", TRUE,
+		"vexpand", FALSE,
+		"use-underline", FALSE,
+		"attributes", attrs,
+		"xalign", 0.0,
+		"halign", GTK_ALIGN_START,
+		NULL);
+	pango_attr_list_unref (attrs);
+
+	gtk_grid_attach (grid, label, 0, row, 1, 1);
+	gtk_grid_attach (grid, widget, 1, row, 1, 1);
+
+	row++;
+
+	label = gtk_label_new (_("Folder name:"));
+	g_object_set (G_OBJECT (label),
+		"hexpand", FALSE,
+		"vexpand", FALSE,
+		"xalign", 0.0,
+		NULL);
+
+	widget = gtk_label_new (folder_name);
+	gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_MIDDLE);
+	gtk_widget_set_tooltip_text (widget, folder_name);
+	g_object_set (G_OBJECT (widget),
+		"hexpand", TRUE,
+		"vexpand", FALSE,
+		"xalign", 0.0,
+		NULL);
+
+	gtk_grid_attach (grid, label, 0, row, 1, 1);
+	gtk_grid_attach (grid, widget, 1, row, 1, 1);
+
+	row++;
+
+	label = gtk_label_new (_("Folder ID:"));
+	g_object_set (G_OBJECT (label),
+		"hexpand", FALSE,
+		"vexpand", FALSE,
+		"xalign", 0.0,
+		NULL);
+
+	str = e_mapi_util_mapi_id_to_string (folder_id);
+	widget = gtk_label_new (str);
+	g_free (str);
+	g_object_set (G_OBJECT (widget),
+		"hexpand", TRUE,
+		"vexpand", FALSE,
+		"xalign", 0.0,
+		"selectable", TRUE,
+		NULL);
+
+	gtk_grid_attach (grid, label, 0, row, 1, 1);
+	gtk_grid_attach (grid, widget, 1, row, 1, 1);
+
+	row++;
+
+	widget = gtk_scrolled_window_new (NULL, NULL);
+	scrolled_window = GTK_SCROLLED_WINDOW (widget);
+	gtk_scrolled_window_set_min_content_width (scrolled_window, 120);
+	gtk_scrolled_window_set_min_content_height (scrolled_window, 120);
+	gtk_container_add (GTK_CONTAINER (widget), create_permissions_tree_view (dialog, widgets));
+	g_object_set (G_OBJECT (widget),
+		"hexpand", TRUE,
+		"vexpand", TRUE,
+		"shadow-type", GTK_SHADOW_IN,
+		NULL);
+
+	gtk_grid_attach (grid, widget, 0, row, 2, 1);
+
+	row++;
+
+	hvbox = gtk_grid_new ();
+	gtk_orientable_set_orientation (GTK_ORIENTABLE (hvbox), GTK_ORIENTATION_HORIZONTAL);
+	gtk_grid_set_column_spacing (GTK_GRID (hvbox), 6);
+	gtk_grid_set_column_homogeneous (GTK_GRID (hvbox), TRUE);
+	g_object_set (G_OBJECT (hvbox),
+		"hexpand", FALSE,
+		"vexpand", FALSE,
+		"halign", GTK_ALIGN_END,
+		NULL);
+
+	button = gtk_button_new_from_stock (GTK_STOCK_ADD);
+	widgets->add_button = button;
+	gtk_container_add (GTK_CONTAINER (hvbox), button);
+
+	button = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
+	widgets->remove_button = button;
+	gtk_container_add (GTK_CONTAINER (hvbox), button);
+
+	gtk_grid_attach (grid, hvbox, 0, row, 2, 1);
+
+	row++;
+
+	widget = gtk_frame_new (_("Permissions"));
+	g_object_set (G_OBJECT (widget),
+		"hexpand", TRUE,
+		"vexpand", FALSE,
+		NULL);
+	gtk_grid_attach (grid, widget, 0, row, 2, 1);
+
+	grid = GTK_GRID (gtk_grid_new ());
+	gtk_grid_set_row_homogeneous (grid, FALSE);
+	gtk_grid_set_row_spacing (grid, 6);
+	gtk_grid_set_column_homogeneous (grid, FALSE);
+	gtk_grid_set_column_spacing (grid, 6);
+	gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
+	gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (grid));
+
+	row = 0;
+
+	hvbox = gtk_grid_new ();
+	gtk_orientable_set_orientation (GTK_ORIENTABLE (hvbox), GTK_ORIENTATION_HORIZONTAL);
+	gtk_grid_set_column_spacing (GTK_GRID (hvbox), 6);
+
+	label = gtk_label_new_with_mnemonic (_("Permi_ssion level:"));
+	g_object_set (G_OBJECT (label),
+		"hexpand", FALSE,
+		"vexpand", FALSE,
+		"xalign", 0.0,
+		NULL);
+
+	widget = GTK_WIDGET (g_object_new (gtk_combo_box_text_get_type (),
+		"has-entry", FALSE,
+		"entry-text-column", 0,
+		"hexpand", TRUE,
+		"vexpand", FALSE,
+		NULL));
+	widgets->level_combo = widget;
+
+	combo_text = GTK_COMBO_BOX_TEXT (widget);
+	for (ii = 0; ii < G_N_ELEMENTS (predefined_levels); ii++)
+		gtk_combo_box_text_append_text (combo_text, g_dgettext ("PermissionsLevel", predefined_levels[ii].name));
+	gtk_combo_box_set_active (GTK_COMBO_BOX (combo_text), 0);
+
+	gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+	gtk_container_add (GTK_CONTAINER (hvbox), label);
+	gtk_container_add (GTK_CONTAINER (hvbox), widget);
+
+	gtk_grid_attach (grid, hvbox, 0, row, 2, 1);
+
+	row++;
+
+	frame = gtk_frame_new (C_("Permissions", "Read"));
+	g_object_set (G_OBJECT (frame),
+		"hexpand", FALSE,
+		"vexpand", FALSE,
+		NULL);
+
+	hvbox = gtk_grid_new ();
+	gtk_orientable_set_orientation (GTK_ORIENTABLE (hvbox), GTK_ORIENTATION_VERTICAL);
+	gtk_grid_set_column_spacing (GTK_GRID (hvbox), 2);
+	gtk_container_add (GTK_CONTAINER (frame), hvbox);
+
+	widget = gtk_radio_button_new_with_label (NULL, C_("Permissions", "None"));
+	widgets->read_none_radio = widget;
+	radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (widget));
+	gtk_container_add (GTK_CONTAINER (hvbox), widget);
+
+	widget = gtk_radio_button_new_with_label (radio_group, C_("Permissions", "Full Details"));
+	widgets->read_full_radio = widget;
+	gtk_container_add (GTK_CONTAINER (hvbox), widget);
+
+	if (with_freebusy) {
+		widget = gtk_check_button_new_with_label (C_("Permissions", "Simple Free/Busy"));
+		widgets->read_fb_simple_check = widget;
+		gtk_container_add (GTK_CONTAINER (hvbox), widget);
+
+		widget = gtk_check_button_new_with_label (C_("Permissions", "Detailed Free/Busy"));
+		widgets->read_fb_detail_check = widget;
+		gtk_container_add (GTK_CONTAINER (hvbox), widget);
+	}
+
+	gtk_grid_attach (grid, frame, 0, row, 1, 1);
+
+	frame = gtk_frame_new (C_("Permissions", "Write"));
+	g_object_set (G_OBJECT (frame),
+		"hexpand", FALSE,
+		"vexpand", FALSE,
+		NULL);
+
+	hvbox = gtk_grid_new ();
+	gtk_orientable_set_orientation (GTK_ORIENTABLE (hvbox), GTK_ORIENTATION_VERTICAL);
+	gtk_grid_set_column_spacing (GTK_GRID (hvbox), 2);
+	gtk_container_add (GTK_CONTAINER (frame), hvbox);
+
+	widget = gtk_check_button_new_with_label (C_("Permissions", "Create items"));
+	widgets->write_create_items_check = widget;
+	gtk_container_add (GTK_CONTAINER (hvbox), widget);
+
+	widget = gtk_check_button_new_with_label (C_("Permissions", "Create subfolders"));
+	widgets->write_create_subfolders_check = widget;
+	gtk_container_add (GTK_CONTAINER (hvbox), widget);
+
+	widget = gtk_check_button_new_with_label (C_("Permissions", "Edit own"));
+	widgets->write_edit_own_check = widget;
+	gtk_container_add (GTK_CONTAINER (hvbox), widget);
+
+	widget = gtk_check_button_new_with_label (C_("Permissions", "Edit all"));
+	widgets->write_edit_all_check = widget;
+	gtk_container_add (GTK_CONTAINER (hvbox), widget);
+
+	gtk_grid_attach (grid, frame, 1, row, 1, 1);
+
+	row++;
+
+	frame = gtk_frame_new (C_("Permissions", "Delete items"));
+	g_object_set (G_OBJECT (frame),
+		"hexpand", FALSE,
+		"vexpand", FALSE,
+		NULL);
+
+	hvbox = gtk_grid_new ();
+	gtk_orientable_set_orientation (GTK_ORIENTABLE (hvbox), GTK_ORIENTATION_VERTICAL);
+	gtk_grid_set_column_spacing (GTK_GRID (hvbox), 2);
+	gtk_container_add (GTK_CONTAINER (frame), hvbox);
+
+	widget = gtk_radio_button_new_with_label (NULL, C_("Permissions", "None"));
+	widgets->delete_none_radio = widget;
+	radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (widget));
+	gtk_container_add (GTK_CONTAINER (hvbox), widget);
+
+	widget = gtk_radio_button_new_with_label (radio_group, C_("Permissions", "Own"));
+	widgets->delete_own_radio = widget;
+	radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (widget));
+	gtk_container_add (GTK_CONTAINER (hvbox), widget);
+
+	widget = gtk_radio_button_new_with_label (radio_group, C_("Permissions", "All"));
+	widgets->delete_all_radio = widget;
+	gtk_container_add (GTK_CONTAINER (hvbox), widget);
+
+	gtk_grid_attach (grid, frame, 0, row, 1, 1);
+
+	frame = gtk_frame_new (C_("Permissions", "Other"));
+	g_object_set (G_OBJECT (frame),
+		"hexpand", FALSE,
+		"vexpand", FALSE,
+		NULL);
+
+	hvbox = gtk_grid_new ();
+	gtk_orientable_set_orientation (GTK_ORIENTABLE (hvbox), GTK_ORIENTATION_VERTICAL);
+	gtk_grid_set_column_spacing (GTK_GRID (hvbox), 2);
+	gtk_container_add (GTK_CONTAINER (frame), hvbox);
+
+	widget = gtk_check_button_new_with_label (C_("Permissions", "Folder owner"));
+	widgets->other_folder_owner_check = widget;
+	gtk_container_add (GTK_CONTAINER (hvbox), widget);
+
+	widget = gtk_check_button_new_with_label (C_("Permissions", "Folder contact"));
+	widgets->other_folder_contact_check = widget;
+	gtk_container_add (GTK_CONTAINER (hvbox), widget);
+
+	widget = gtk_check_button_new_with_label (C_("Permissions", "Folder visible"));
+	widgets->other_folder_visible_check = widget;
+	gtk_container_add (GTK_CONTAINER (hvbox), widget);
+
+	gtk_grid_attach (grid, frame, 1, row, 1, 1);
+
+	row++;
+
+	g_signal_connect_swapped (widgets->add_button,
+		"clicked", G_CALLBACK (add_button_clicked_cb), dialog);
+	g_signal_connect_swapped (widgets->remove_button,
+		"clicked", G_CALLBACK (remove_button_clicked_cb), dialog);
+	g_signal_connect_swapped (widgets->level_combo,
+		"changed", G_CALLBACK (update_permission_dialog_by_level_combo), dialog);
+	g_signal_connect_swapped (widgets->read_none_radio,
+		"toggled", G_CALLBACK (update_permission_level_combo_by_dialog), dialog);
+	g_signal_connect_swapped (widgets->read_full_radio,
+		"toggled", G_CALLBACK (update_permission_level_combo_by_dialog), dialog);
+	if (widgets->read_fb_simple_check)
+		g_signal_connect_swapped (widgets->read_fb_simple_check,
+			"toggled", G_CALLBACK (update_permission_level_combo_by_dialog), dialog);
+	if (widgets->read_fb_detail_check)
+		g_signal_connect_swapped (widgets->read_fb_detail_check,
+			"toggled", G_CALLBACK (update_permission_level_combo_by_dialog), dialog);
+	g_signal_connect_swapped (widgets->write_create_items_check,
+		"toggled", G_CALLBACK (update_permission_level_combo_by_dialog), dialog);
+	g_signal_connect_swapped (widgets->write_create_subfolders_check,
+		"toggled", G_CALLBACK (update_permission_level_combo_by_dialog), dialog);
+	g_signal_connect_swapped (widgets->write_edit_own_check,
+		"toggled", G_CALLBACK (update_permission_level_combo_by_dialog), dialog);
+	g_signal_connect_swapped (widgets->write_edit_all_check,
+		"toggled", G_CALLBACK (update_permission_level_combo_by_dialog), dialog);
+	g_signal_connect_swapped (widgets->delete_none_radio,
+		"toggled", G_CALLBACK (update_permission_level_combo_by_dialog), dialog);
+	g_signal_connect_swapped (widgets->delete_own_radio,
+		"toggled", G_CALLBACK (update_permission_level_combo_by_dialog), dialog);
+	g_signal_connect_swapped (widgets->delete_all_radio,
+		"toggled", G_CALLBACK (update_permission_level_combo_by_dialog), dialog);
+	g_signal_connect_swapped (widgets->other_folder_owner_check,
+		"toggled", G_CALLBACK (update_permission_level_combo_by_dialog), dialog);
+	g_signal_connect_swapped (widgets->other_folder_contact_check,
+		"toggled", G_CALLBACK (update_permission_level_combo_by_dialog), dialog);
+	g_signal_connect_swapped (widgets->other_folder_visible_check,
+		"toggled", G_CALLBACK (update_permission_level_combo_by_dialog), dialog);
+
+	enable_all_widgets (widgets, FALSE);
+
+	gtk_dialog_set_response_sensitive (GTK_DIALOG (widgets->dialog), GTK_RESPONSE_OK, FALSE);
+
+	gtk_widget_show_all (content);
+	gtk_widget_show (GTK_WIDGET (dialog));
+
+	found_entries = g_new0 (GSList *, 1);
+
+	e_mapi_run_in_thread_with_feedback (GTK_WINDOW (dialog), dialog,
+		_("Reading folder permissions, please wait..."),
+		read_folder_permissions_thread,
+		read_folder_permissions_idle,
+		found_entries, folder_permissions_free_found_entries);
+}
diff --git a/src/account-setup-eplugin/e-mapi-edit-folder-permissions.h b/src/account-setup-eplugin/e-mapi-edit-folder-permissions.h
new file mode 100644
index 0000000..8e9d613
--- /dev/null
+++ b/src/account-setup-eplugin/e-mapi-edit-folder-permissions.h
@@ -0,0 +1,43 @@
+/* -*- 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:
+ *    Milan Crha <mcrha redhat com>
+ *
+ * Copyright (C) 2012 Red Hat, Inc. (www.redhat.com)
+ *
+ */
+
+#ifndef E_MAPI_EDIT_FOLDER_PERMISSIONS_H
+#define E_MAPI_EDIT_FOLDER_PERMISSIONS_H
+
+#include <gtk/gtk.h>
+#include <libmapi/libmapi.h>
+
+#include <e-mapi-folder.h>
+
+void	e_mapi_edit_folder_permissions	(GtkWindow *parent,
+					 const gchar *login_profile,
+					 const gchar *login_username,
+					 const gchar *login_url,
+					 const gchar *account_name,
+					 const gchar *folder_name,
+					 mapi_id_t folder_id,
+					 EMapiFolderCategory folder_category,
+					 const gchar *foreign_username,
+					 gboolean with_freebusy);
+
+#endif /* E_MAPI_EDIT_FOLDER_PERMISSIONS_H */
diff --git a/src/account-setup-eplugin/e-mapi-search-gal-user.c b/src/account-setup-eplugin/e-mapi-search-gal-user.c
new file mode 100644
index 0000000..dc3ac5e
--- /dev/null
+++ b/src/account-setup-eplugin/e-mapi-search-gal-user.c
@@ -0,0 +1,756 @@
+/* -*- 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:
+ *    Milan Crha <mcrha redhat com>
+ *
+ * Copyright (C) 2012 Red Hat, Inc. (www.redhat.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include "e-mapi-account-setup.h"
+#include "e-mapi-search-gal-user.h"
+#include "e-mapi-utils.h"
+#include "e-mapi-defs.h"
+
+#define E_MAPI_SEARCH_DLG_DATA "e-mapi-search-dlg-data"
+
+enum {
+	COL_DISPLAY_NAME = 0,
+	COL_EMAIL,
+	COL_USER_DN,
+	COL_ENTRY_ID,
+	COL_USER_TYPE
+};
+
+struct EMapiSearchGalUserData
+{
+	EMapiConnection *conn;
+	GCancellable *cancellable;
+	gchar *search_text;
+	guint32 search_extra;
+	GtkWidget *tree_view;
+	GtkWidget *info_label;
+	guint schedule_search_id;
+};
+
+static void
+e_mapi_search_gal_user_data_free (gpointer ptr)
+{
+	struct EMapiSearchGalUserData *pgu = ptr;
+
+	if (!pgu)
+		return;
+
+	if (pgu->schedule_search_id) {
+		g_source_remove (pgu->schedule_search_id);
+		pgu->schedule_search_id = 0;
+	}
+	if (pgu->cancellable) {
+		g_cancellable_cancel (pgu->cancellable);
+		g_object_unref (pgu->cancellable);
+		pgu->cancellable = NULL;
+	}
+	g_object_unref (pgu->conn);
+	g_free (pgu->search_text);
+	g_free (pgu);
+}
+
+struct EMapiGalSearchUser
+{
+	gchar *display_name;
+	gchar *email;
+	gchar *dn;
+	struct SBinary_short *entry_id;
+};
+
+static void
+e_mapi_search_gal_user_free (gpointer ptr)
+{
+	struct EMapiGalSearchUser *user = ptr;
+
+	if (!user)
+		return;
+
+	g_free (user->display_name);
+	g_free (user->email);
+	g_free (user->dn);
+	if (user->entry_id)
+		g_free (user->entry_id->lpb);
+	g_free (user->entry_id);
+	g_free (user);
+}
+
+struct EMapiSearchIdleData
+{
+	EMapiConnection *conn;
+	gchar *search_text;
+	GCancellable *cancellable;
+
+	GObject *dialog;
+	GSList *found_users; /* struct EMapiGalSearchUser * */
+	guint found_total;
+};
+
+static void
+e_mapi_search_idle_data_free (gpointer ptr)
+{
+	struct EMapiSearchIdleData *sid = ptr;
+
+	if (!sid)
+		return;
+
+	g_object_unref (sid->conn);
+	g_object_unref (sid->cancellable);
+	g_free (sid->search_text);
+	g_slist_free_full (sid->found_users, e_mapi_search_gal_user_free);
+	g_free (sid);
+}
+
+static void
+empty_search_gal_tree_view (GtkWidget *tree_view)
+{
+	GtkListStore *store;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	struct SBinary_short *entry_id;
+
+	g_return_if_fail (tree_view != NULL);
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
+	g_return_if_fail (model != NULL);
+
+	store = GTK_LIST_STORE (model);
+	g_return_if_fail (store != NULL);
+
+	if (!gtk_tree_model_get_iter_first (model, &iter))
+		return;
+
+	do {
+		entry_id = NULL;
+		gtk_tree_model_get (model, &iter,
+			COL_ENTRY_ID, &entry_id,
+			-1);
+
+		if (entry_id) {
+			g_free (entry_id->lpb);
+			g_free (entry_id);
+		}
+	} while (gtk_tree_model_iter_next (model, &iter));
+
+	gtk_list_store_clear (store);
+}
+
+static void
+search_gal_add_user (GtkListStore *store,
+		     const gchar *display_name,
+		     const gchar *email,
+		     const gchar *user_dn,
+		     struct SBinary_short *entry_id, /* takes ownership of the pointer */
+		     EMapiGalUserType user_type)
+{
+	GtkTreeIter iter;
+
+	g_return_if_fail (store != NULL);
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter,
+		COL_DISPLAY_NAME, display_name,
+		COL_EMAIL, email,
+		COL_USER_DN, user_dn,
+		COL_ENTRY_ID, entry_id,
+		COL_USER_TYPE, user_type,
+		-1);
+}
+
+static gboolean
+search_gal_finish_idle (gpointer user_data)
+{
+	struct EMapiSearchIdleData *sid = user_data;
+
+	g_return_val_if_fail (sid != NULL, FALSE);
+	g_return_val_if_fail (sid->dialog != NULL, FALSE);
+
+	if (!g_cancellable_is_cancelled (sid->cancellable)) {
+		struct EMapiSearchGalUserData *pgu;
+		GtkListStore *store;
+		guint added = 0;
+		GSList *fu;
+
+		pgu = g_object_get_data (sid->dialog, E_MAPI_SEARCH_DLG_DATA);
+		g_return_val_if_fail (pgu != NULL, FALSE);
+		g_return_val_if_fail (pgu->tree_view != NULL, FALSE);
+		g_return_val_if_fail (pgu->info_label != NULL, FALSE);
+
+		empty_search_gal_tree_view (pgu->tree_view);
+
+		store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (pgu->tree_view)));
+		g_return_val_if_fail (store != NULL, FALSE);
+
+		for (fu = sid->found_users; fu; fu = fu->next) {
+			struct EMapiGalSearchUser *user = fu->data;
+
+			if (!user)
+				continue;
+
+			search_gal_add_user (store, user->display_name, user->email, user->dn, user->entry_id, E_MAPI_GAL_USER_REGULAR);
+			user->entry_id = NULL;
+
+			added++;
+		}
+
+		if (!added) {
+			gtk_label_set_text (GTK_LABEL (pgu->info_label), _("No users found"));
+		} else if (added == sid->found_total) {
+			gchar *str;
+			str = g_strdup_printf (ngettext ("Found one user", "Found %d users", added), added);
+			gtk_label_set_text (GTK_LABEL (pgu->info_label), str);
+			g_free (str);
+		} else {
+			gchar *str;
+			str = g_strdup_printf (ngettext ("Found %d users, but showing only first %d", "Found %d users, but showing only first %d", sid->found_total), sid->found_total, added);
+			gtk_label_set_text (GTK_LABEL (pgu->info_label), str);
+			g_free (str);
+		}
+	}
+
+	e_mapi_search_idle_data_free (sid);
+
+	return FALSE;
+}
+
+static gboolean
+build_gal_search_restriction_cb (EMapiConnection *conn,
+				 TALLOC_CTX *mem_ctx,
+				 struct mapi_SRestriction **restrictions,
+				 gpointer user_data,
+				 GCancellable *cancellable,
+				 GError **perror)
+{
+	const gchar *search_text = user_data;
+	struct mapi_SRestriction *restriction;
+
+	g_return_val_if_fail (mem_ctx != NULL, FALSE);
+	g_return_val_if_fail (restrictions != NULL, FALSE);
+	g_return_val_if_fail (search_text != NULL, FALSE);
+	g_return_val_if_fail (*search_text, FALSE);
+
+	restriction = talloc_zero (mem_ctx, struct mapi_SRestriction);
+	g_return_val_if_fail (restriction != NULL, FALSE);
+
+	restriction->rt = RES_OR;
+	restriction->res.resOr.cRes = 2;
+	restriction->res.resOr.res = talloc_zero_array (mem_ctx, struct mapi_SRestriction_or, restriction->res.resOr.cRes + 1);
+
+	restriction->res.resOr.res[0].rt = RES_CONTENT;
+	restriction->res.resOr.res[0].res.resContent.fuzzy = FL_SUBSTRING | FL_IGNORECASE;
+	restriction->res.resOr.res[0].res.resContent.ulPropTag = PidTagDisplayName;
+	restriction->res.resOr.res[0].res.resContent.lpProp.ulPropTag = PidTagDisplayName;
+	restriction->res.resOr.res[0].res.resContent.lpProp.value.lpszW = talloc_strdup (mem_ctx, search_text);
+
+	restriction->res.resOr.res[1].rt = RES_CONTENT;
+	restriction->res.resOr.res[1].res.resContent.fuzzy = FL_SUBSTRING | FL_IGNORECASE;
+	restriction->res.resOr.res[1].res.resContent.ulPropTag = PidTagPrimarySmtpAddress;
+	restriction->res.resOr.res[1].res.resContent.lpProp.ulPropTag = PidTagPrimarySmtpAddress;
+	restriction->res.resOr.res[1].res.resContent.lpProp.value.lpszW = talloc_strdup (mem_ctx, search_text);
+
+	*restrictions = restriction;
+
+	return TRUE;
+}
+
+static gboolean
+list_gal_search_mids_cb (EMapiConnection *conn,
+			 TALLOC_CTX *mem_ctx,
+			 const ListObjectsData *object_data,
+			 guint32 obj_index,
+			 guint32 obj_total,
+			 gpointer user_data,
+			 GCancellable *cancellable,
+			 GError **perror)
+{
+	g_return_val_if_fail (object_data != NULL, FALSE);
+	g_return_val_if_fail (user_data != NULL, FALSE);
+
+	if (object_data->obj_type == MAPI_MAILUSER) {
+		GSList **pmids = user_data;
+		mapi_id_t *mid;
+
+		mid = g_new0 (mapi_id_t, 1);
+		*mid = object_data->mid;
+
+		*pmids = g_slist_prepend (*pmids, mid);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+search_gal_build_properties_cb (EMapiConnection *conn,
+				TALLOC_CTX *mem_ctx,
+				struct SPropTagArray *props,
+				gpointer data,
+				GCancellable *cancellable,
+				GError **perror)
+{
+	g_return_val_if_fail (mem_ctx != NULL, FALSE);
+	g_return_val_if_fail (props != NULL, FALSE);
+
+	SPropTagArray_add (mem_ctx, props, PidTagEntryId);
+	SPropTagArray_add (mem_ctx, props, PidTagDisplayName);
+	SPropTagArray_add (mem_ctx, props, PidTagPrimarySmtpAddress);
+	SPropTagArray_add (mem_ctx, props, PidTagEmailAddress);
+
+	return TRUE;
+}
+
+static gboolean
+transfer_gal_search_objects_cb (EMapiConnection *conn,
+				TALLOC_CTX *mem_ctx,
+				/* const */ EMapiObject *object,
+				guint32 obj_index,
+				guint32 obj_total,
+				gpointer user_data,
+				GCancellable *cancellable,
+				GError **perror)
+{
+
+	struct EMapiSearchIdleData *sid = user_data;
+	const gchar *display_name, *email, *user_dn;
+	const struct SBinary_short *entry_id;
+
+	g_return_val_if_fail (object != NULL, FALSE);
+	g_return_val_if_fail (sid != NULL, FALSE);
+
+	display_name = e_mapi_util_find_array_propval (&object->properties, PidTagDisplayName);
+	email = e_mapi_util_find_array_propval (&object->properties, PidTagPrimarySmtpAddress);
+	entry_id = e_mapi_util_find_array_propval (&object->properties, PidTagEntryId);
+	user_dn = e_mapi_util_find_array_propval (&object->properties, PidTagEmailAddress);
+
+	if (entry_id && (display_name || email)) {
+		struct EMapiGalSearchUser *user;
+
+		user = g_new0 (struct EMapiGalSearchUser, 1);
+		user->display_name = g_strdup (display_name);
+		user->email = g_strdup (email);
+		user->dn = g_strdup (user_dn);
+		user->entry_id = g_new0 (struct SBinary_short, 1);
+		user->entry_id->cb = entry_id->cb;
+		if (entry_id->cb > 0)
+			user->entry_id->lpb = g_memdup (entry_id->lpb, entry_id->cb);
+
+		sid->found_users = g_slist_prepend (sid->found_users, user);
+	}
+
+	return TRUE;
+}
+
+static gint
+sort_mids_by_id (gconstpointer pmid1, gconstpointer pmid2)
+{
+	const mapi_id_t *mid1 = pmid1, *mid2 = pmid2;
+
+	if (!mid1 && !mid2)
+		return 0;
+
+	if (!mid1)
+		return -1;
+	if (!mid2)
+		return 1;
+
+	/* simple subtract *mid1 - *mid2 may overflow gint */
+	if (*mid1 < *mid2)
+		return -1;
+	if (*mid1 > *mid2)
+		return 1;
+	return 0;
+}
+
+static gpointer
+search_gal_thread (gpointer user_data)
+{
+	struct EMapiSearchIdleData *sid = user_data;
+
+	g_return_val_if_fail (sid != NULL, NULL);
+
+	if (!g_cancellable_is_cancelled (sid->cancellable)) {
+		GError *error = NULL;
+		GSList *mids = NULL;
+
+		if (e_mapi_connection_list_gal_objects (sid->conn,
+			build_gal_search_restriction_cb, sid->search_text,
+			list_gal_search_mids_cb, &mids,
+			sid->cancellable, &error)) {
+			mids = g_slist_sort (mids, sort_mids_by_id);
+			sid->found_total = g_slist_length (mids);
+			if (sid->found_total > 30) {
+				GSList *tmp = mids, *iter;
+				gint count;
+
+				mids = NULL;
+				for (iter = tmp, count = 0; iter && count < 30; iter = iter->next, count++) {
+					mids = g_slist_prepend (mids, iter->data);
+					iter->data = NULL;
+				}
+
+				g_slist_free_full (tmp, g_free);
+
+				mids = g_slist_reverse (mids);
+			}
+
+			if (mids) {
+				e_mapi_connection_transfer_gal_objects (sid->conn, mids,
+					search_gal_build_properties_cb, NULL,
+					transfer_gal_search_objects_cb, sid,
+					sid->cancellable, &error);
+
+				g_slist_free_full (mids, g_free);
+			}
+
+			sid->found_users = g_slist_reverse (sid->found_users);
+		}
+
+		if (error &&
+		    !g_error_matches (error, E_MAPI_ERROR, MAPI_E_USER_CANCEL) &&
+		    !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+			g_warning ("%s: Failed to search GAL: %s", G_STRFUNC, error->message);
+		}
+
+		g_clear_error (&error);
+
+		g_idle_add (search_gal_finish_idle, sid);
+	} else {
+		e_mapi_search_idle_data_free (sid);
+	}
+
+	return NULL;
+}
+
+static gboolean
+schedule_search_cb (gpointer user_data)
+{
+	struct EMapiSearchIdleData *sid = user_data;
+
+	g_return_val_if_fail (sid != NULL, FALSE);
+	g_return_val_if_fail (sid->dialog != NULL, FALSE);
+
+	if (!g_cancellable_is_cancelled (sid->cancellable)) {
+		struct EMapiSearchGalUserData *pgu;
+		GError *error = NULL;
+
+		pgu = g_object_get_data (sid->dialog, E_MAPI_SEARCH_DLG_DATA);
+		g_return_val_if_fail (pgu != NULL, FALSE);
+		g_return_val_if_fail (pgu->tree_view != NULL, FALSE);
+
+		pgu->schedule_search_id = 0;
+		sid->conn = g_object_ref (pgu->conn);
+		sid->search_text = g_strdup (pgu->search_text);
+
+		if (g_thread_create (search_gal_thread, sid, FALSE, &error)) {
+			sid = NULL;
+		} else {
+			g_object_unref (sid->conn);
+			g_warning ("%s: Failed to create search thread: %s", G_STRFUNC, error ? error->message : "Unknown error");
+		}
+
+		g_clear_error (&error);
+	}
+
+	e_mapi_search_idle_data_free (sid);
+
+	return FALSE;
+}
+
+static void
+search_term_changed_cb (GtkEntry *entry,
+			GObject *dialog)
+{
+	struct EMapiSearchGalUserData *pgu;
+
+	g_return_if_fail (dialog != NULL);
+
+	pgu = g_object_get_data (dialog, E_MAPI_SEARCH_DLG_DATA);
+	g_return_if_fail (pgu != NULL);
+	g_return_if_fail (pgu->tree_view != NULL);
+
+	if (pgu->schedule_search_id) {
+		g_source_remove (pgu->schedule_search_id);
+		pgu->schedule_search_id = 0;
+	}
+
+	if (pgu->cancellable) {
+		g_cancellable_cancel (pgu->cancellable);
+		g_object_unref (pgu->cancellable);
+	}
+
+	pgu->cancellable = g_cancellable_new ();
+
+	if (entry) {
+		g_free (pgu->search_text);
+		pgu->search_text = g_strdup (gtk_entry_get_text (entry));
+	}
+
+	empty_search_gal_tree_view (pgu->tree_view);
+
+	if (!pgu->search_text || !*pgu->search_text) {
+		GtkListStore *store;
+
+		gtk_label_set_text (GTK_LABEL (pgu->info_label), _("Search for a user"));
+
+		store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (pgu->tree_view)));
+
+		if ((pgu->search_extra & E_MAPI_GAL_USER_DEFAULT) != 0)
+			search_gal_add_user (store, C_("User", "Default"), NULL, NULL, NULL, E_MAPI_GAL_USER_DEFAULT);
+
+		if ((pgu->search_extra & E_MAPI_GAL_USER_ANONYMOUS) != 0)
+			search_gal_add_user (store, C_("User", "Anonymous"), NULL, NULL, NULL, E_MAPI_GAL_USER_ANONYMOUS);
+	} else {
+		struct EMapiSearchIdleData *sid;
+
+		sid = g_new0 (struct EMapiSearchIdleData, 1);
+		sid->cancellable = g_object_ref (pgu->cancellable);
+		sid->dialog = dialog;
+
+		gtk_label_set_text (GTK_LABEL (pgu->info_label), _("Searching..."));
+		pgu->schedule_search_id = g_timeout_add (333, schedule_search_cb, sid);
+	}
+}
+
+static void
+dialog_realized_cb (GObject *dialog)
+{
+	struct EMapiSearchGalUserData *pgu;
+
+	g_return_if_fail (dialog != NULL);
+
+	pgu = g_object_get_data (dialog, E_MAPI_SEARCH_DLG_DATA);
+	g_return_if_fail (pgu != NULL);
+	g_return_if_fail (pgu->tree_view != NULL);
+
+	if (pgu->cancellable)
+		return;
+
+	search_term_changed_cb (NULL, dialog);
+}
+
+static void
+search_gal_user_selection_changed_cb (GtkTreeSelection *selection,
+				      GtkDialog *dialog)
+{
+	g_return_if_fail (selection != NULL);
+	g_return_if_fail (dialog != NULL);
+
+	gtk_dialog_set_response_sensitive (dialog,
+		GTK_RESPONSE_OK,
+		gtk_tree_selection_get_selected (selection, NULL, NULL));
+}
+
+static void
+search_gal_user_row_activated_cb (GtkTreeView *tree_view,
+				  GtkTreePath *path,
+				  GtkTreeViewColumn *column,
+				  GtkDialog *dialog)
+{
+	g_return_if_fail (tree_view != NULL);
+	g_return_if_fail (dialog != NULL);
+
+	if (path && column)
+		gtk_dialog_response (dialog, GTK_RESPONSE_OK);
+}
+
+static GtkWidget *
+create_users_tree_view (GtkWidget *dialog,
+			struct EMapiSearchGalUserData *pgu)
+{
+	GtkTreeView *tree_view;
+	GtkTreeSelection *selection;
+	GtkCellRenderer *renderer;
+	GtkTreeViewColumn *column;
+	gint pos;
+
+	g_return_val_if_fail (dialog != NULL, NULL);
+	g_return_val_if_fail (pgu != NULL, NULL);
+
+	tree_view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (
+		GTK_TREE_MODEL (gtk_list_store_new (5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_UINT))));
+
+	renderer = gtk_cell_renderer_text_new ();
+	g_object_set (renderer, "editable", FALSE, NULL);
+	pos = gtk_tree_view_insert_column_with_attributes (tree_view, -1, _("Name"), renderer, "text", COL_DISPLAY_NAME, NULL);
+	column = gtk_tree_view_get_column (tree_view, pos - 1);
+	gtk_tree_view_column_set_expand (column, TRUE);
+
+	renderer = gtk_cell_renderer_text_new ();
+	g_object_set (renderer, "editable", FALSE, NULL);
+	gtk_tree_view_insert_column_with_attributes (tree_view, -1, _("E-mail"), renderer, "text", COL_EMAIL, NULL);
+
+	selection = gtk_tree_view_get_selection (tree_view);
+	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+	search_gal_user_selection_changed_cb (selection, GTK_DIALOG (dialog));
+	g_signal_connect (selection, "changed", G_CALLBACK (search_gal_user_selection_changed_cb), dialog);
+
+	g_signal_connect (tree_view, "row-activated", G_CALLBACK (search_gal_user_row_activated_cb), dialog);
+
+	pgu->tree_view = GTK_WIDGET (tree_view);
+
+	return pgu->tree_view;
+}
+
+gboolean
+e_mapi_search_gal_user_modal (GtkWindow *parent,
+			      EMapiConnection *conn,
+			      const gchar *search_this,
+			      EMapiGalUserType *searched_type,
+			      gchar **display_name,
+			      gchar **email,
+			      gchar **user_dn,
+			      struct SBinary_short **entry_id)
+{
+	gboolean res = FALSE;
+	struct EMapiSearchGalUserData *pgu;
+	GtkWidget *dialog;
+	GtkWidget *content, *label, *widget;
+	GtkGrid *grid;
+	GtkScrolledWindow *scrolled_window;
+	gint row;
+
+	g_return_val_if_fail (conn != NULL, FALSE);
+	g_return_val_if_fail (searched_type != NULL, FALSE);
+	g_return_val_if_fail (display_name || email || entry_id || user_dn, FALSE);
+
+	pgu = g_new0 (struct EMapiSearchGalUserData, 1);
+	pgu->conn = g_object_ref (conn);
+	pgu->search_extra = 0; /* always none, as default/anonymous user cannot be added to permissions */
+
+	dialog = gtk_dialog_new_with_buttons (
+		_("Choose MAPI user..."),
+		parent,
+		GTK_DIALOG_DESTROY_WITH_PARENT,
+		GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+		GTK_STOCK_OK, GTK_RESPONSE_OK,
+		NULL);
+
+	g_object_set_data_full (G_OBJECT (dialog), E_MAPI_SEARCH_DLG_DATA, pgu, e_mapi_search_gal_user_data_free);
+
+	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+	content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+	grid = GTK_GRID (gtk_grid_new ());
+	gtk_grid_set_row_homogeneous (grid, FALSE);
+	gtk_grid_set_row_spacing (grid, 6);
+	gtk_grid_set_column_homogeneous (grid, FALSE);
+	gtk_grid_set_column_spacing (grid, 6);
+	gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
+	gtk_container_add (GTK_CONTAINER (content), GTK_WIDGET (grid));
+
+	row = 0;
+
+	label = gtk_label_new_with_mnemonic (_("_Search:"));
+	g_object_set (G_OBJECT (label),
+		"hexpand", FALSE,
+		"vexpand", FALSE,
+		"xalign", 0.0,
+		NULL);
+
+	widget = gtk_entry_new ();
+	g_object_set (G_OBJECT (widget),
+		"hexpand", TRUE,
+		"vexpand", FALSE,
+		NULL);
+	gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+	if (search_this && *search_this) {
+		gtk_entry_set_text (GTK_ENTRY (widget), search_this);
+		pgu->search_text = g_strdup (search_this);
+	}
+
+	g_signal_connect (widget, "changed", G_CALLBACK (search_term_changed_cb), dialog);
+
+	gtk_grid_attach (grid, label, 0, row, 1, 1);
+	gtk_grid_attach (grid, widget, 1, row, 1, 1);
+
+	row++;
+
+	widget = gtk_scrolled_window_new (NULL, NULL);
+	scrolled_window = GTK_SCROLLED_WINDOW (widget);
+	gtk_scrolled_window_set_min_content_width (scrolled_window, 120);
+	gtk_scrolled_window_set_min_content_height (scrolled_window, 120);
+	gtk_container_add (GTK_CONTAINER (widget), create_users_tree_view (dialog, pgu));
+	g_object_set (G_OBJECT (widget),
+		"hexpand", TRUE,
+		"vexpand", TRUE,
+		"shadow-type", GTK_SHADOW_IN,
+		NULL);
+
+	gtk_grid_attach (grid, widget, 0, row, 2, 1);
+
+	row++;
+
+	label = gtk_label_new (_("Search for a user"));
+	g_object_set (G_OBJECT (label),
+		"hexpand", TRUE,
+		"vexpand", FALSE,
+		"xalign", 0.0,
+		NULL);
+
+	pgu->info_label = label;
+
+	gtk_grid_attach (grid, label, 0, row, 2, 1);
+
+	row++;
+
+	gtk_widget_show_all (content);
+
+	g_signal_connect (dialog, "realize", G_CALLBACK (dialog_realized_cb), NULL);
+
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
+		GtkTreeSelection *selection;
+		GtkTreeModel *model = NULL;
+		GtkTreeIter iter;
+
+		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pgu->tree_view));
+		if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+			guint ut = E_MAPI_GAL_USER_NONE;
+
+			gtk_tree_model_get (model, &iter, COL_USER_TYPE, &ut, -1);
+			
+			*searched_type = ut;
+			if (display_name)
+				gtk_tree_model_get (model, &iter, COL_DISPLAY_NAME, display_name, -1);
+			if (email)
+				gtk_tree_model_get (model, &iter, COL_EMAIL, email, -1);
+			if (user_dn)
+				gtk_tree_model_get (model, &iter, COL_USER_DN, user_dn, -1);
+			if (entry_id) {
+				gtk_tree_model_get (model, &iter, COL_ENTRY_ID, entry_id, -1);
+				gtk_list_store_set (GTK_LIST_STORE (model), &iter, COL_ENTRY_ID, NULL, -1);
+			}
+
+			res = TRUE;
+		}			
+	}
+
+	gtk_widget_destroy (dialog);
+
+	return res;
+}
diff --git a/src/account-setup-eplugin/e-mapi-search-gal-user.h b/src/account-setup-eplugin/e-mapi-search-gal-user.h
new file mode 100644
index 0000000..cec196d
--- /dev/null
+++ b/src/account-setup-eplugin/e-mapi-search-gal-user.h
@@ -0,0 +1,46 @@
+/* -*- 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:
+ *    Milan Crha <mcrha redhat com>
+ *
+ * Copyright (C) 2012 Red Hat, Inc. (www.redhat.com)
+ *
+ */
+
+#ifndef E_MAPI_SEARCH_GAL_USER_H
+#define E_MAPI_SEARCH_GAL_USER_H
+
+#include <gtk/gtk.h>
+#include <e-mapi-connection.h>
+
+typedef enum {
+	E_MAPI_GAL_USER_NONE		= 0,
+	E_MAPI_GAL_USER_DEFAULT		= 1 << 0,
+	E_MAPI_GAL_USER_ANONYMOUS	= 1 << 1,
+	E_MAPI_GAL_USER_REGULAR		= 1 << 2
+} EMapiGalUserType;
+
+gboolean	e_mapi_search_gal_user_modal	(GtkWindow *parent,
+						 EMapiConnection *conn,
+						 const gchar *search_this,
+						 EMapiGalUserType *searched_type, /* one of from the enum */
+						 gchar **display_name,
+						 gchar **email,
+						 gchar **user_dn,
+						 struct SBinary_short **entry_id); /* allocated with GLib, not talloc */
+
+#endif /* E_MAPI_SEARCH_GAL_USER_H */
diff --git a/src/account-setup-eplugin/e-mapi-subscribe-foreign-folder.c b/src/account-setup-eplugin/e-mapi-subscribe-foreign-folder.c
index 44a2167..65dec46 100644
--- a/src/account-setup-eplugin/e-mapi-subscribe-foreign-folder.c
+++ b/src/account-setup-eplugin/e-mapi-subscribe-foreign-folder.c
@@ -35,7 +35,6 @@
 
 #include "e-mapi-account-setup.h"
 #include "e-mapi-subscribe-foreign-folder.h"
-#include "e-mapi-debug.h"
 #include "e-mapi-utils.h"
 
 #ifndef PidTagMailboxOwnerName
diff --git a/src/account-setup-eplugin/org-gnome-exchange-mapi.eplug.xml b/src/account-setup-eplugin/org-gnome-exchange-mapi.eplug.xml
index 849aee0..59ab8b3 100644
--- a/src/account-setup-eplugin/org-gnome-exchange-mapi.eplug.xml
+++ b/src/account-setup-eplugin/org-gnome-exchange-mapi.eplug.xml
@@ -77,22 +77,51 @@
 			</group>
 		</hook>
 
+		<hook class="org.gnome.evolution.mail.popup:1.0">
+			<menu id="org.gnome.evolution.mail.foldertree.popup" target="folder"
+				factory="org_gnome_folder_size_display_popup">
+			</menu>
+		</hook>
+
 		<hook class="org.gnome.evolution.ui:1.0">
-		  <ui-manager id="org.gnome.evolution.mail">
+		  <ui-manager id="org.gnome.evolution.mail" callback="mapi_ui_init_mail">
 		    <popup name="mail-folder-popup">
 		      <placeholder name="mail-folder-popup-actions">
 			<menuitem action="mail-mapi-folder-size"/>
 			<menuitem action="mail-mapi-subscribe-foreign-folder"/>
+			<menuitem action="mail-mapi-folder-permissions"/>
 		      </placeholder>
 		    </popup>
 		  </ui-manager>
-		</hook>
 
-		<hook class="org.gnome.evolution.mail.popup:1.0">
-			<menu id="org.gnome.evolution.mail.foldertree.popup" target="folder"
-				factory="org_gnome_folder_size_display_popup">
-			</menu>
+		  <ui-manager id="org.gnome.evolution.calendars" callback="mapi_ui_init_calendar">
+		    <popup name="calendar-popup">
+		      <placeholder name="calendar-popup-actions">
+			<menuitem action="calendar-mapi-folder-permissions"/>
+		      </placeholder>
+		    </popup>
+		  </ui-manager>
+		  <ui-manager id="org.gnome.evolution.tasks" callback="mapi_ui_init_tasks">
+		    <popup name="task-list-popup">
+		      <placeholder name="task-list-popup-actions">
+			<menuitem action="tasks-mapi-folder-permissions"/>
+		      </placeholder>
+		    </popup>
+		  </ui-manager>
+		  <ui-manager id="org.gnome.evolution.memos" callback="mapi_ui_init_memos">
+		    <popup name="memo-list-popup">
+		      <placeholder name="memo-list-popup-actions">
+			<menuitem action="memos-mapi-folder-permissions"/>
+		      </placeholder>
+		    </popup>
+		  </ui-manager>
+		  <ui-manager id="org.gnome.evolution.contacts" callback="mapi_ui_init_contacts">
+		    <popup name="address-book-popup">
+		      <placeholder name="address-book-popup-actions">
+			<menuitem action="contacts-mapi-folder-permissions"/>
+		      </placeholder>
+		    </popup>
+		  </ui-manager>
 		</hook>
-
 	</e-plugin>
 </e-plugin-list>
diff --git a/src/addressbook/e-book-backend-mapi-gal.c b/src/addressbook/e-book-backend-mapi-gal.c
index 0c7b549..b4711cb 100644
--- a/src/addressbook/e-book-backend-mapi-gal.c
+++ b/src/addressbook/e-book-backend-mapi-gal.c
@@ -219,7 +219,7 @@ ebbm_gal_transfer_contacts (EBookBackendMAPI *ebma,
 	tg.book_view = book_view;
 	tg.notify_contact_data = notify_contact_data;
 
-	status = e_mapi_connection_transfer_gal_objects	(conn, get_mids, transfer_gal_cb, &tg, cancellable, &mapi_error);
+	status = e_mapi_connection_transfer_gal_objects	(conn, get_mids, NULL, NULL, 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"));
diff --git a/src/libexchangemapi/e-mapi-connection.c b/src/libexchangemapi/e-mapi-connection.c
index a3bc2a1..39a3dae 100644
--- a/src/libexchangemapi/e-mapi-connection.c
+++ b/src/libexchangemapi/e-mapi-connection.c
@@ -1145,6 +1145,7 @@ e_mapi_connection_get_folder_properties (EMapiConnection *conn,
 	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 (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
+	e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 
 	LOCK ();
 	mem_ctx = talloc_new (priv->session);
@@ -1335,6 +1336,278 @@ foreach_tablerow (EMapiConnection *conn,
 	return ms;
 }
 
+static gboolean
+gather_folder_permissions_cb (EMapiConnection *conn,
+			      TALLOC_CTX *mem_ctx,
+			      struct SRow *srow,
+			      guint32 row_index,
+			      guint32 rows_total,
+			      gpointer user_data,
+			      GCancellable *cancellable,
+			      GError **perror)
+{
+	GSList **entries = user_data;
+	const gchar *username;
+	const struct Binary_r *pentry_id;
+	const uint64_t *pid;
+	const uint32_t *prights;
+
+	g_return_val_if_fail (srow != NULL, FALSE);
+	g_return_val_if_fail (entries != NULL, FALSE);
+
+	username = e_mapi_util_find_row_propval (srow, PidTagMemberName);
+	pid = e_mapi_util_find_row_propval (srow, PidTagMemberId);
+	pentry_id = e_mapi_util_find_row_propval (srow, PidTagEntryId);
+	prights = e_mapi_util_find_row_propval (srow, PidTagMemberRights);
+
+	if (prights && pid) {
+		EMapiPermissionEntry *pem;
+		struct SBinary_short entry_id;
+
+		entry_id.cb = pentry_id ? pentry_id->cb : 0;
+		entry_id.lpb = pentry_id ? pentry_id->lpb : NULL;
+
+		pem = e_mapi_permission_entry_new (username, pentry_id ? &entry_id : NULL, *pid, *prights);
+		g_return_val_if_fail (pem != NULL, FALSE);
+
+		*entries = g_slist_prepend (*entries, pem);
+	} else {
+		g_debug ("%s: Skipping [%d/%d] (%s) No rights or member ID set", G_STRFUNC, row_index, rows_total, username ? username : "no member name");
+	}
+
+	return TRUE;
+}
+
+gboolean
+e_mapi_connection_get_permissions (EMapiConnection *conn,
+				   mapi_object_t *obj_folder,
+				   gboolean with_freebusy,
+				   GSList **entries, /* EMapiPermissionEntry */
+				   GCancellable *cancellable,
+				   GError **perror)
+{
+	enum MAPISTATUS ms = MAPI_E_RESERVED;
+	struct SPropTagArray *propTagArray;
+	mapi_object_t obj_table;
+	TALLOC_CTX *mem_ctx;
+
+	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 (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
+	e_return_val_mapi_error_if_fail (entries != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
+
+	LOCK ();
+	mem_ctx = talloc_new (priv->session);
+	mapi_object_init (&obj_table);
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
+		ms = MAPI_E_USER_CANCEL;
+		goto cleanup;
+	}
+
+	ms = GetPermissionsTable (obj_folder, with_freebusy ? IncludeFreeBusy : 0, &obj_table);
+	if (ms != MAPI_E_SUCCESS) {
+		make_mapi_error (perror, "GetPermissionsTable", ms);
+		goto cleanup;
+	}
+
+	propTagArray = set_SPropTagArray (mem_ctx, 4,
+					  PidTagMemberId,
+					  PidTagEntryId,
+					  PidTagMemberName,
+					  PidTagMemberRights);
+
+	/* Set primary columns to be fetched */
+	ms = SetColumns (&obj_table, propTagArray);
+	if (ms != MAPI_E_SUCCESS) {
+		make_mapi_error (perror, "SetColumns", ms);
+		goto cleanup;
+	}
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
+		ms = MAPI_E_USER_CANCEL;
+		goto cleanup;
+	}
+
+	*entries = NULL;
+
+	ms = foreach_tablerow (conn, mem_ctx, &obj_table, gather_folder_permissions_cb, entries, cancellable, perror);
+	if (ms == MAPI_E_SUCCESS) {
+		*entries = g_slist_reverse (*entries);
+	} else {
+		g_slist_free_full (*entries, (GDestroyNotify) e_mapi_permission_entry_free);
+		*entries = NULL;
+	}
+
+ cleanup:
+	mapi_object_release (&obj_table);
+	talloc_free (mem_ctx);
+	UNLOCK();
+
+	return ms == MAPI_E_SUCCESS;
+}
+
+gboolean
+e_mapi_connection_set_permissions (EMapiConnection *conn,
+				   mapi_object_t *obj_folder,
+				   gboolean with_freebusy,
+				   const GSList *entries, /* EMapiPermissionEntry */
+				   GCancellable *cancellable,
+				   GError **perror)
+{
+	enum MAPISTATUS ms = MAPI_E_RESERVED;
+	struct mapi_PermissionsData *rows = NULL;
+	GSList *current_entries = NULL;
+	TALLOC_CTX *mem_ctx;
+
+	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 (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
+
+	LOCK ();
+	mem_ctx = talloc_new (priv->session);
+
+	rows = talloc_zero (mem_ctx, struct mapi_PermissionsData);
+	if (!rows) {
+		ms = MAPI_E_NOT_ENOUGH_RESOURCES;
+		make_mapi_error (perror, "talloc_zero", ms);
+		goto cleanup;
+	}
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
+		ms = MAPI_E_USER_CANCEL;
+		goto cleanup;
+	}
+
+	if (!e_mapi_connection_get_permissions (conn, obj_folder, with_freebusy, &current_entries, cancellable, perror)) {
+		ms = MAPI_E_CALL_FAILED;
+		make_mapi_error (perror, "e_mapi_connection_get_permissions", ms);
+		goto cleanup;
+	}
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
+		ms = MAPI_E_USER_CANCEL;
+		goto cleanup;
+	}
+
+	rows->ModifyCount = g_slist_length ((GSList *) entries) + g_slist_length (current_entries);
+	if (rows->ModifyCount > 0) {
+		const GSList *iter, *citer;
+		GSList *removed_entries = g_slist_copy (current_entries);
+		gint row_index = 0;
+
+		rows->PermissionsData = talloc_array (rows, struct PermissionData, rows->ModifyCount);
+		if (!rows->PermissionsData) {
+			ms = MAPI_E_NOT_ENOUGH_RESOURCES;
+			make_mapi_error (perror, "talloc_zero", ms);
+			g_slist_free (removed_entries);
+			goto cleanup;
+		}
+
+		for (iter = entries; iter; iter = iter->next) {
+			const EMapiPermissionEntry *pem = iter->data, *cpem = NULL;
+
+			if (!pem) {
+				ms = MAPI_E_INVALID_PARAMETER;
+				make_mapi_error (perror, "entries::data", ms);
+				g_slist_free (removed_entries);
+				goto cleanup;
+			}
+
+			for (citer = current_entries; citer; citer = citer->next) {
+				cpem = citer->data;
+
+				if (cpem && ((cpem->entry_id.cb == pem->entry_id.cb && cpem->member_id == pem->member_id) ||
+				   (cpem->entry_id.cb > 0 && e_mapi_util_recip_entryid_equal (&cpem->entry_id, &pem->entry_id)))) {
+					removed_entries = g_slist_remove (removed_entries, cpem);
+					break;
+				}
+
+				cpem = NULL;
+			}
+
+			if (cpem == NULL) {
+				rows->PermissionsData[row_index].PermissionDataFlags = ROW_ADD;
+				rows->PermissionsData[row_index].lpProps.cValues = 2;
+				rows->PermissionsData[row_index].lpProps.lpProps = talloc_zero_array (rows, struct mapi_SPropValue, 3);
+				if (!rows->PermissionsData[row_index].lpProps.lpProps) {
+					ms = MAPI_E_NOT_ENOUGH_RESOURCES;
+					make_mapi_error (perror, "talloc_zero", ms);
+					g_slist_free (removed_entries);
+					goto cleanup;
+				}
+
+				rows->PermissionsData[row_index].lpProps.lpProps[0].ulPropTag = PidTagEntryId;
+				rows->PermissionsData[row_index].lpProps.lpProps[0].value.bin = pem->entry_id;
+
+				rows->PermissionsData[row_index].lpProps.lpProps[1].ulPropTag = PidTagMemberRights;
+				rows->PermissionsData[row_index].lpProps.lpProps[1].value.l = pem->member_rights;
+
+				row_index++;
+			} else if (cpem->member_rights != pem->member_rights) {
+				rows->PermissionsData[row_index].PermissionDataFlags = ROW_MODIFY;
+				rows->PermissionsData[row_index].lpProps.cValues = 2;
+				rows->PermissionsData[row_index].lpProps.lpProps = talloc_zero_array (rows, struct mapi_SPropValue, 3);
+				if (!rows->PermissionsData[row_index].lpProps.lpProps) {
+					ms = MAPI_E_NOT_ENOUGH_RESOURCES;
+					make_mapi_error (perror, "talloc_zero", ms);
+					g_slist_free (removed_entries);
+					goto cleanup;
+				}
+
+				rows->PermissionsData[row_index].lpProps.lpProps[0].ulPropTag = PidTagMemberId;
+				rows->PermissionsData[row_index].lpProps.lpProps[0].value.d = pem->member_id;
+
+				rows->PermissionsData[row_index].lpProps.lpProps[1].ulPropTag = PidTagMemberRights;
+				rows->PermissionsData[row_index].lpProps.lpProps[1].value.l = pem->member_rights;
+
+				row_index++;
+			}
+		}
+
+		for (citer = removed_entries; citer; citer = citer->next) {
+			const EMapiPermissionEntry *cpem = citer->data;
+
+			if (cpem) {
+				rows->PermissionsData[row_index].PermissionDataFlags = ROW_REMOVE;
+				rows->PermissionsData[row_index].lpProps.cValues = 1;
+				rows->PermissionsData[row_index].lpProps.lpProps = talloc_zero_array (rows, struct mapi_SPropValue, 2);
+				if (!rows->PermissionsData[row_index].lpProps.lpProps) {
+					ms = MAPI_E_NOT_ENOUGH_RESOURCES;
+					make_mapi_error (perror, "talloc_zero", ms);
+					g_slist_free (removed_entries);
+					goto cleanup;
+				}
+
+				rows->PermissionsData[row_index].lpProps.lpProps[0].ulPropTag = PidTagMemberId;
+				rows->PermissionsData[row_index].lpProps.lpProps[0].value.d = cpem->member_id;
+
+				row_index++;
+			}
+		}
+
+		rows->ModifyCount = row_index;
+
+		g_slist_free (removed_entries);
+	}
+
+	if (rows->ModifyCount > 0) {
+		ms = ModifyPermissions (obj_folder, with_freebusy ? ModifyPerms_IncludeFreeBusy : 0, rows);
+		if (ms != MAPI_E_SUCCESS) {
+			make_mapi_error (perror, "ModifyPermissions", ms);
+			goto cleanup;
+		}
+	}
+
+ cleanup:
+	g_slist_free_full (current_entries, (GDestroyNotify) e_mapi_permission_entry_free);
+	talloc_free (rows);
+	talloc_free (mem_ctx);
+	UNLOCK();
+
+	return ms == MAPI_E_SUCCESS;
+}
+
 struct ListObjectsInternalData
 {
 	ListObjectsCB cb;
@@ -4062,6 +4335,8 @@ fill_reverse_replace_hash (gpointer key,
 gboolean
 e_mapi_connection_transfer_gal_objects (EMapiConnection *conn,
 					const GSList *mids,
+					BuildReadPropsCB brp_cb,
+					gpointer brp_cb_user_data,
 					TransferObjectCB cb,
 					gpointer cb_user_data,
 					GCancellable *cancellable,
@@ -4115,10 +4390,19 @@ e_mapi_connection_transfer_gal_objects (EMapiConnection *conn,
 		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;
+	if (brp_cb) {
+		propTagArray = set_SPropTagArray (mem_ctx, 1, PidTagObjectType);
+		if (!brp_cb (conn, mem_ctx, propTagArray, brp_cb_user_data, cancellable, perror)) {
+			ms = MAPI_E_CALL_FAILED;
+			make_mapi_error (perror, "brp_cb", ms);
+			goto cleanup;
+		}
+	} else {
+		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++) {
@@ -4190,7 +4474,7 @@ e_mapi_connection_transfer_gal_object (EMapiConnection *conn,
 	gboolean res;
 
 	mids = g_slist_append (NULL, &message_id);
-	res = e_mapi_connection_transfer_gal_objects (conn, mids, cb, cb_user_data, cancellable, perror);
+	res = e_mapi_connection_transfer_gal_objects (conn, mids, NULL, NULL, cb, cb_user_data, cancellable, perror);
 	g_slist_free (mids);
 
 	return res;
@@ -4722,6 +5006,11 @@ e_mapi_connection_resolve_named_props  (EMapiConnection *conn,
 		talloc_free (names);
 	}
 
+	if (ms == MAPI_E_NOT_FOUND) {
+		res = TRUE;
+		goto cleanup;
+	}
+
 	if (ms != MAPI_E_SUCCESS) {
 		make_mapi_error (perror, "mapi_nameid_GetIDsFromNames", ms);
 		goto cleanup;
@@ -6484,3 +6773,39 @@ e_mapi_object_add_attachment (EMapiObject *object,
 		attach->next = attachment;
 	}
 }
+
+EMapiPermissionEntry *
+e_mapi_permission_entry_new (const gchar *username,
+			     const struct SBinary_short *entry_id,
+			     uint64_t member_id,
+			     uint32_t member_rights)
+{
+	EMapiPermissionEntry *entry;
+
+	entry = g_new0 (EMapiPermissionEntry, 1);
+	entry->username = g_strdup (username);
+
+	if (entry_id && entry_id->lpb) {
+		entry->entry_id.cb = entry_id->cb;
+		entry->entry_id.lpb = g_memdup (entry_id->lpb, entry_id->cb);
+	} else {
+		entry->entry_id.cb = 0;
+		entry->entry_id.lpb = NULL;
+	}
+
+	entry->member_id = member_id;
+	entry->member_rights = member_rights;
+
+	return entry;
+}
+
+void
+e_mapi_permission_entry_free (EMapiPermissionEntry *entry)
+{
+	if (!entry)
+		return;
+
+	g_free (entry->username);
+	g_free (entry->entry_id.lpb);
+	g_free (entry);
+}
diff --git a/src/libexchangemapi/e-mapi-connection.h b/src/libexchangemapi/e-mapi-connection.h
index 63d4797..6c03899 100644
--- a/src/libexchangemapi/e-mapi-connection.h
+++ b/src/libexchangemapi/e-mapi-connection.h
@@ -105,6 +105,38 @@ void			e_mapi_object_add_recipient	(EMapiObject *object,
 void			e_mapi_object_add_attachment	(EMapiObject *object,
 							 EMapiAttachment *attachment);
 
+#define E_MAPI_PERMISSION_MEMBER_ID_ANONYMOUS_CLIENT	(~((uint64_t) 0))
+#define E_MAPI_PERMISSION_MEMBER_ID_DEFAULT_USER	((uint64_t) 0)
+
+typedef enum {
+	E_MAPI_PERMISSION_BIT_FREE_BUSY_DETAILED	= 0x00001000,
+	E_MAPI_PERMISSION_BIT_FREE_BUSY_SIMPLE		= 0x00000800,
+	E_MAPI_PERMISSION_BIT_FOLDER_VISIBLE		= 0x00000400,
+	E_MAPI_PERMISSION_BIT_FOLDER_CONTACT		= 0x00000200,
+	E_MAPI_PERMISSION_BIT_FOLDER_OWNER		= 0x00000100,
+	E_MAPI_PERMISSION_BIT_CREATE_SUBFOLDER		= 0x00000080,
+	E_MAPI_PERMISSION_BIT_DELETE_ANY		= 0x00000040,
+	E_MAPI_PERMISSION_BIT_EDIT_ANY			= 0x00000020,
+	E_MAPI_PERMISSION_BIT_DELETE_OWNED		= 0x00000010,
+	E_MAPI_PERMISSION_BIT_EDIT_OWNED		= 0x00000008,
+	E_MAPI_PERMISSION_BIT_CREATE			= 0x00000002,
+	E_MAPI_PERMISSION_BIT_READ_ANY			= 0x00000001
+} EMapiPermissionBits;
+
+typedef struct {
+	gchar *username;		/* PidTagMemberName - display name for a user */
+
+	struct SBinary_short entry_id;	/* PidTagEntryId of the user */
+	uint64_t member_id;		/* PidTagMemberId of the user */
+	uint32_t member_rights;		/* PidTagMemberRights of the user */
+} EMapiPermissionEntry;
+
+EMapiPermissionEntry *	e_mapi_permission_entry_new	(const gchar *username,
+							 const struct SBinary_short *entry_id,
+							 uint64_t member_id,
+							 uint32_t member_rights);
+void			e_mapi_permission_entry_free	(EMapiPermissionEntry *entry);
+
 typedef enum {
 	E_MAPI_CREATE_FLAG_NONE		= 0,
 	E_MAPI_CREATE_FLAG_SUBMIT	= 1 << 0
@@ -236,6 +268,20 @@ gboolean		e_mapi_connection_get_folder_properties	(EMapiConnection *conn,
 								 GCancellable *cancellable,
 								 GError **perror);
 
+gboolean		e_mapi_connection_get_permissions	(EMapiConnection *conn,
+								 mapi_object_t *obj_folder,
+								 gboolean with_freebusy,
+								 GSList **entries, /* out, EMapiPermissionEntry */
+								 GCancellable *cancellable,
+								 GError **perror);
+
+gboolean		e_mapi_connection_set_permissions	(EMapiConnection *conn,
+								 mapi_object_t *obj_folder,
+								 gboolean with_freebusy,
+								 const GSList *entries, /* EMapiPermissionEntry */
+								 GCancellable *cancellable,
+								 GError **perror);
+
 gboolean		e_mapi_connection_list_objects		(EMapiConnection *conn,
 								 mapi_object_t *obj_folder,
 								 BuildRestrictionsCB build_rs_cb,
@@ -301,6 +347,8 @@ gboolean		e_mapi_connection_list_gal_objects	(EMapiConnection *conn,
 
 gboolean		e_mapi_connection_transfer_gal_objects	(EMapiConnection *conn,
 								 const GSList *mids,
+								 BuildReadPropsCB brp_cb, /* NULL for all supported */
+								 gpointer brp_cb_user_data,
 								 TransferObjectCB cb,
 								 gpointer cb_user_data,
 								 GCancellable *cancellable,
diff --git a/src/libexchangemapi/e-mapi-utils.c b/src/libexchangemapi/e-mapi-utils.c
index d7b075f..707acfa 100644
--- a/src/libexchangemapi/e-mapi-utils.c
+++ b/src/libexchangemapi/e-mapi-utils.c
@@ -589,6 +589,44 @@ e_mapi_util_recip_entryid_decode (EMapiConnection *conn, const struct Binary_r *
 	return FALSE;
 }
 
+gboolean
+e_mapi_util_recip_entryid_decode_dn (const struct SBinary_short *entryid,
+				     gchar **exchange_dn)
+{
+	struct Binary_r ei;
+
+	if (!entryid)
+		return FALSE;
+
+	ei.cb = entryid->cb;
+	ei.lpb = entryid->lpb;
+
+	return recip_entryid_decode_ex (&ei, exchange_dn);
+}
+
+gboolean
+e_mapi_util_recip_entryid_equal (const struct SBinary_short *entryid1,
+				 const struct SBinary_short *entryid2)
+{
+	gchar *dn1 = NULL, *dn2 = NULL;
+	gboolean same = FALSE;
+
+	if (!entryid1 && !entryid2)
+		return TRUE;
+
+	if (!entryid1 || !entryid2 || !entryid1->lpb || !entryid2->lpb || entryid1->cb != entryid2->cb)
+		return FALSE;
+
+	same = e_mapi_util_recip_entryid_decode_dn (entryid1, &dn1) &&
+	       e_mapi_util_recip_entryid_decode_dn (entryid2, &dn2) &&
+	       dn1 && dn2 && g_ascii_strcasecmp (dn1, dn2) == 0;
+
+	g_free (dn1);
+	g_free (dn2);
+
+	return same;
+}
+
 /**
  * e_mapi_util_profiledata_from_settings:
  * @empd: destination for profile settings
diff --git a/src/libexchangemapi/e-mapi-utils.h b/src/libexchangemapi/e-mapi-utils.h
index 7dc02ed..97d9369 100644
--- a/src/libexchangemapi/e-mapi-utils.h
+++ b/src/libexchangemapi/e-mapi-utils.h
@@ -54,6 +54,10 @@ gboolean	e_mapi_util_recip_entryid_decode	(EMapiConnection *conn,
 							 const struct Binary_r *entyrid,
 							 gchar **display_name,
 							 gchar **email);
+gboolean	e_mapi_util_recip_entryid_decode_dn	(const struct SBinary_short *entryid,
+							 gchar **exchange_dn);
+gboolean	e_mapi_util_recip_entryid_equal		(const struct SBinary_short *entryid1,
+							 const struct SBinary_short *entryid2);
 
 void		e_mapi_util_profiledata_from_settings	(EMapiProfileData *empd,
 							 CamelMapiSettings *settings);



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