changeset 8433:dfe39e9a9e78 HEAD

Initial support for LISTing users with shared mailboxes.
author Timo Sirainen <tss@iki.fi>
date Sun, 16 Nov 2008 19:20:28 +0200
parents 8f083c8482e1
children fa755adddb11
files src/plugins/acl/Makefile.am src/plugins/acl/acl-api-private.h src/plugins/acl/acl-api.c src/plugins/acl/acl-backend-vfile-acllist.c src/plugins/acl/acl-backend-vfile.c src/plugins/acl/acl-lookup-dict.c src/plugins/acl/acl-lookup-dict.h src/plugins/acl/acl-mailbox-list.c src/plugins/acl/acl-mailbox.c src/plugins/acl/acl-plugin.c src/plugins/acl/acl-plugin.h src/plugins/acl/acl-shared-storage.c src/plugins/acl/acl-shared-storage.h src/plugins/acl/acl-storage.c src/plugins/acl/acl-storage.h
diffstat 15 files changed, 563 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/src/plugins/acl/Makefile.am	Sun Nov 16 19:19:26 2008 +0200
+++ b/src/plugins/acl/Makefile.am	Sun Nov 16 19:20:28 2008 +0200
@@ -1,5 +1,6 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-dict \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-imap \
 	-I$(top_srcdir)/src/lib-index \
@@ -16,9 +17,11 @@
 	acl-backend-vfile.c \
 	acl-backend-vfile-acllist.c \
 	acl-cache.c \
+	acl-lookup-dict.c \
 	acl-mailbox.c \
 	acl-mailbox-list.c \
 	acl-plugin.c \
+	acl-shared-storage.c \
 	acl-storage.c
 
 noinst_HEADERS = \
@@ -26,7 +29,9 @@
 	acl-api-private.h \
 	acl-backend-vfile.h \
 	acl-cache.h \
+	acl-lookup-dict.h \
 	acl-plugin.h \
+	acl-shared-storage.h \
 	acl-storage.h
 
 install-exec-local:
--- a/src/plugins/acl/acl-api-private.h	Sun Nov 16 19:19:26 2008 +0200
+++ b/src/plugins/acl/acl-api-private.h	Sun Nov 16 19:20:28 2008 +0200
@@ -82,6 +82,7 @@
 			   const struct acl_mask *mask, pool_t pool);
 int acl_backend_get_default_rights(struct acl_backend *backend,
 				   const struct acl_mask **mask_r);
+void acl_rights_write_id(string_t *dest, const struct acl_rights *right);
 bool acl_rights_has_nonowner_lookup_changes(const struct acl_rights *rights);
 
 #endif
--- a/src/plugins/acl/acl-api.c	Sun Nov 16 19:19:26 2008 +0200
+++ b/src/plugins/acl/acl-api.c	Sun Nov 16 19:20:28 2008 +0200
@@ -1,6 +1,7 @@
 /* Copyright (c) 2006-2008 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "str.h"
 #include "hash.h"
 #include "acl-cache.h"
 #include "acl-api-private.h"
@@ -172,6 +173,35 @@
 	ctx->backend->v.nonowner_lookups_iter_deinit(ctx);
 }
 
+void acl_rights_write_id(string_t *dest, const struct acl_rights *right)
+{
+	switch (right->id_type) {
+	case ACL_ID_ANYONE:
+		str_append(dest, ACL_ID_NAME_ANYONE);
+		break;
+	case ACL_ID_AUTHENTICATED:
+		str_append(dest, ACL_ID_NAME_AUTHENTICATED);
+		break;
+	case ACL_ID_OWNER:
+		str_append(dest, ACL_ID_NAME_OWNER);
+		break;
+	case ACL_ID_USER:
+		str_append(dest, ACL_ID_NAME_USER_PREFIX);
+		str_append(dest, right->identifier);
+		break;
+	case ACL_ID_GROUP:
+		str_append(dest, ACL_ID_NAME_GROUP_PREFIX);
+		str_append(dest, right->identifier);
+		break;
+	case ACL_ID_GROUP_OVERRIDE:
+		str_append(dest, ACL_ID_NAME_GROUP_OVERRIDE_PREFIX);
+		str_append(dest, right->identifier);
+		break;
+	case ACL_ID_TYPE_COUNT:
+		i_unreached();
+	}
+}
+
 bool acl_rights_has_nonowner_lookup_changes(const struct acl_rights *rights)
 {
 	const char *const *p;
--- a/src/plugins/acl/acl-backend-vfile-acllist.c	Sun Nov 16 19:19:26 2008 +0200
+++ b/src/plugins/acl/acl-backend-vfile-acllist.c	Sun Nov 16 19:20:28 2008 +0200
@@ -10,6 +10,7 @@
 #include "mail-storage.h"
 #include "acl-plugin.h"
 #include "acl-cache.h"
+#include "acl-lookup-dict.h"
 #include "acl-backend-vfile.h"
 
 #include <stdio.h>
@@ -241,8 +242,12 @@
 		}
 	}
 	if (ret == 0) {
+		struct acl_user *auser = ACL_USER_CONTEXT(ns->user);
+
 		backend->acllist_mtime = st.st_mtime;
 		backend->acllist_last_check = ioloop_time;
+		/* FIXME: dict reubild is expensive, try to avoid it */
+		(void)acl_lookup_dict_rebuild(auser->acl_lookup_dict);
 	} else {
 		acllist_clear(backend, 0);
 		if (unlink(str_c(path)) < 0 && errno != ENOENT)
--- a/src/plugins/acl/acl-backend-vfile.c	Sun Nov 16 19:19:26 2008 +0200
+++ b/src/plugins/acl/acl-backend-vfile.c	Sun Nov 16 19:20:28 2008 +0200
@@ -999,32 +999,7 @@
 	const char *const *rights = neg ? right->neg_rights : right->rights;
 
 	if (neg) str_append_c(dest,'-');
-
-	switch (right->id_type) {
-	case ACL_ID_ANYONE:
-		str_append(dest, ACL_ID_NAME_ANYONE);
-		break;
-	case ACL_ID_AUTHENTICATED:
-		str_append(dest, ACL_ID_NAME_AUTHENTICATED);
-		break;
-	case ACL_ID_OWNER:
-		str_append(dest, ACL_ID_NAME_OWNER);
-		break;
-	case ACL_ID_USER:
-		str_append(dest, ACL_ID_NAME_USER_PREFIX);
-		str_append(dest, right->identifier);
-		break;
-	case ACL_ID_GROUP:
-		str_append(dest, ACL_ID_NAME_GROUP_PREFIX);
-		str_append(dest, right->identifier);
-		break;
-	case ACL_ID_GROUP_OVERRIDE:
-		str_append(dest, ACL_ID_NAME_GROUP_OVERRIDE_PREFIX);
-		str_append(dest, right->identifier);
-		break;
-	case ACL_ID_TYPE_COUNT:
-		i_unreached();
-	}
+	acl_rights_write_id(dest, right);
 	str_append_c(dest, ' ');
 	vfile_write_rights_list(dest, rights);
 	str_append_c(dest, '\n');
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/acl/acl-lookup-dict.c	Sun Nov 16 19:20:28 2008 +0200
@@ -0,0 +1,332 @@
+/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "dict.h"
+#include "mail-user.h"
+#include "mail-namespace.h"
+#include "acl-api-private.h"
+#include "acl-storage.h"
+#include "acl-plugin.h"
+#include "acl-lookup-dict.h"
+
+#include <stdlib.h>
+
+#define DICT_SHARED_BOXES_PATH "shared-boxes/"
+
+struct acl_lookup_dict {
+	struct mail_user *user;
+};
+
+struct acl_lookup_dict_iter {
+	pool_t pool;
+	struct acl_lookup_dict *dict;
+
+	ARRAY_TYPE(const_string) iter_ids;
+	struct dict_iterate_context *dict_iter;
+	unsigned int iter_idx;
+
+	const char *prefix;
+	unsigned int prefix_len;
+
+	unsigned int failed:1;
+};
+
+static struct dict *acl_dict;
+
+void acl_lookup_dicts_init(void)
+{
+	const char *uri;
+
+	uri = getenv("ACL_SHARED_DICT");
+	if (uri == NULL) {
+		if (getenv("DEBUG") != NULL) {
+			i_info("acl: No acl_shared_dict setting - "
+			       "shared mailbox listing is disabled");
+		}
+		return;
+	}
+
+	acl_dict = dict_init(uri, DICT_DATA_TYPE_STRING, "");
+	if (acl_dict == NULL)
+		i_fatal("acl: dict_init(%s) failed", uri);
+}
+
+void acl_lookup_dicts_deinit(void)
+{
+	if (acl_dict != NULL)
+		dict_deinit(&acl_dict);
+}
+
+struct acl_lookup_dict *acl_lookup_dict_init(struct mail_user *user)
+{
+	struct acl_lookup_dict *dict;
+
+	dict = i_new(struct acl_lookup_dict, 1);
+	dict->user = user;
+	return dict;
+}
+
+void acl_lookup_dict_deinit(struct acl_lookup_dict **_dict)
+{
+	struct acl_lookup_dict *dict = *_dict;
+
+	*_dict = NULL;
+	i_free(dict);
+}
+
+static void
+acl_lookup_dict_write_rights_id(string_t *dest, const struct acl_rights *right)
+{
+	switch (right->id_type) {
+	case ACL_ID_ANYONE:
+	case ACL_ID_AUTHENTICATED:
+		/* don't bother separating these */
+		str_append(dest, "anyone");
+		break;
+	case ACL_ID_USER:
+		str_append(dest, "user/");
+		str_append(dest, right->identifier);
+		break;
+	case ACL_ID_GROUP:
+	case ACL_ID_GROUP_OVERRIDE:
+		str_append(dest, "group/");
+		str_append(dest, right->identifier);
+		break;
+	case ACL_ID_OWNER:
+	case ACL_ID_TYPE_COUNT:
+		i_unreached();
+	}
+}
+
+static int acl_lookup_dict_rebuild_add_backend(struct mail_namespace *ns,
+					       ARRAY_TYPE(const_string) *ids)
+{
+	struct acl_backend *backend;
+	struct acl_mailbox_list_context *ctx;
+	struct acl_object *aclobj;
+	struct acl_object_list_iter *iter;
+	struct acl_rights rights;
+	const char *name, *id_dup;
+	string_t *id;
+	int ret, ret2 = 0;
+
+	id = t_str_new(128);
+	backend = acl_storage_get_backend(ns->storage);
+	ctx = acl_backend_nonowner_lookups_iter_init(backend);
+	while ((ret = acl_backend_nonowner_lookups_iter_next(ctx, &name)) > 0) {
+		aclobj = acl_object_init_from_name(backend, ns->storage, name);
+
+		iter = acl_object_list_init(aclobj);
+		while ((ret = acl_object_list_next(iter, &rights)) > 0) {
+			if (acl_rights_has_nonowner_lookup_changes(&rights)) {
+				str_truncate(id, 0);
+				acl_lookup_dict_write_rights_id(id, &rights);
+				id_dup = t_strdup(str_c(id));
+				array_append(ids, &id_dup, 1);
+			}
+		}
+		acl_object_list_deinit(&iter);
+		if (ret < 0)
+			ret2 = -1;
+		acl_object_deinit(&aclobj);
+	}
+	acl_backend_nonowner_lookups_iter_deinit(&ctx);
+	return ret < 0 || ret2 < 0 ? -1 : 0;
+}
+
+static int
+acl_lookup_dict_rebuild_update(struct acl_lookup_dict *dict,
+			       const ARRAY_TYPE(const_string) *new_ids_arr,
+			       bool no_removes)
+{
+	const char *username = dict->user->username;
+	struct dict_iterate_context *iter;
+	struct dict_transaction_context *dt;
+	const char *prefix, *key, *value, **old_ids, *const *new_ids, *p;
+	ARRAY_TYPE(const_string) old_ids_arr;
+	unsigned int newi, oldi, old_count, new_count;
+	string_t *path;
+	unsigned int prefix_len;
+	int ret;
+
+	/* get all existing identifiers for the user */
+	t_array_init(&old_ids_arr, 128);
+	prefix = DICT_PATH_SHARED DICT_SHARED_BOXES_PATH;
+	prefix_len = strlen(prefix);
+	iter = dict_iterate_init(acl_dict, prefix, DICT_ITERATE_FLAG_RECURSE);
+	while ((ret = dict_iterate(iter, &key, &value)) > 0) {
+		/* prefix/$dest/$source */
+		key += prefix_len;
+		p = strchr(key, '/');
+		if (p != NULL && strcmp(p + 1, username) == 0) {
+			key = t_strdup_until(key, p);
+			array_append(&old_ids_arr, &key, 1);
+		}
+	}
+	dict_iterate_deinit(&iter);
+	if (ret < 0) {
+		i_error("acl: dict iteration failed, can't update dict");
+		return -1;
+	}
+
+	/* sort the existing identifiers */
+	old_ids = array_get_modifiable(&old_ids_arr, &old_count);
+	qsort(old_ids, old_count, sizeof(*old_ids), i_strcmp_p);
+
+	/* sync the identifiers */
+	path = t_str_new(256);
+	str_append(path, prefix);
+
+	dt = dict_transaction_begin(acl_dict);
+	new_ids = array_get(new_ids_arr, &new_count);
+	for (newi = oldi = 0; newi < new_count || oldi < old_count; ) {
+		ret = newi == new_count ? 1 :
+			oldi == old_count ? -1 :
+			strcmp(new_ids[newi], old_ids[oldi]);
+		if (ret == 0) {
+			newi++; oldi++;
+		} else if (ret < 0) {
+			/* new identifier, add it */
+			str_truncate(path, prefix_len);
+			str_append(path, new_ids[newi]);
+			str_append_c(path, '/');
+			str_append(path, username);
+			dict_set(dt, str_c(path), "1");
+			newi++;
+		} else if (!no_removes) {
+			/* old identifier removed */
+			str_truncate(path, prefix_len);
+			str_append(path, old_ids[oldi]);
+			str_append_c(path, '/');
+			str_append(path, username);
+			dict_unset(dt, str_c(path));
+			oldi++;
+		}
+	}
+	if (dict_transaction_commit(&dt) < 0) {
+		i_error("acl: dict commit failed");
+		return -1;
+	}
+	return 0;
+}
+
+int acl_lookup_dict_rebuild(struct acl_lookup_dict *dict)
+{
+	struct mail_namespace *ns;
+	ARRAY_TYPE(const_string) ids_arr;
+	const char **ids;
+	unsigned int i, dest, count;
+	int ret = 0;
+
+	/* get all ACL identifiers with a positive lookup right */
+	t_array_init(&ids_arr, 128);
+	for (ns = dict->user->namespaces; ns != NULL; ns = ns->next) {
+		if (acl_lookup_dict_rebuild_add_backend(ns, &ids_arr) < 0)
+			ret = -1;
+	}
+
+	/* sort identifiers and remove duplicates */
+	ids = array_get_modifiable(&ids_arr, &count);
+	qsort(ids, count, sizeof(*ids), i_strcmp_p);
+
+	for (i = 1, dest = 0; i < count; i++) {
+		if (strcmp(ids[dest], ids[i]) != 0) {
+			if (++dest != i)
+				ids[dest] = ids[i];
+		}
+	}
+	if (++dest < count)
+		array_delete(&ids_arr, dest, count-dest);
+
+	/* if lookup failed at some point we can still add new ids,
+	   but we can't remove any existing ones */
+	if (acl_lookup_dict_rebuild_update(dict, &ids_arr, ret < 0) < 0)
+		ret = -1;
+	return ret;
+}
+
+static void acl_lookup_dict_iterate_start(struct acl_lookup_dict_iter *iter)
+{
+	const char *const *idp;
+
+	idp = array_idx(&iter->iter_ids, iter->iter_idx);
+	iter->iter_idx++;
+	iter->prefix = p_strconcat(iter->pool, DICT_PATH_SHARED
+				   DICT_SHARED_BOXES_PATH, *idp, "/", NULL);
+	iter->prefix_len = strlen(iter->prefix);
+
+	iter->dict_iter = dict_iterate_init(acl_dict, iter->prefix,
+					    DICT_ITERATE_FLAG_RECURSE);
+}
+
+struct acl_lookup_dict_iter *
+acl_lookup_dict_iterate_visible_init(struct acl_lookup_dict *dict)
+{
+	struct acl_user *auser = ACL_USER_CONTEXT(dict->user);
+	struct acl_lookup_dict_iter *iter;
+	const char *id;
+	unsigned int i;
+	pool_t pool;
+
+	pool = pool_alloconly_create("acl lookup dict iter", 512);
+	iter = p_new(pool, struct acl_lookup_dict_iter, 1);
+	iter->pool = pool;
+	iter->dict = dict;
+
+	p_array_init(&iter->iter_ids, pool, 16);
+	id = "anyone";
+	array_append(&iter->iter_ids, &id, 1);
+	id = p_strconcat(pool, "user/", dict->user->username, NULL);
+	array_append(&iter->iter_ids, &id, 1);
+
+	/* get all groups we belong to */
+	if (auser->groups != NULL) {
+		for (i = 0; auser->groups[i] != NULL; i++) {
+			id = p_strconcat(pool, "group/", auser->groups[i],
+					 NULL);
+			array_append(&iter->iter_ids, &id, 1);
+		}
+	}
+
+	/* iterate through all identifiers that match us, start with the
+	   first one */
+	acl_lookup_dict_iterate_start(iter);
+	return iter;
+}
+
+const char *
+acl_lookup_dict_iterate_visible_next(struct acl_lookup_dict_iter *iter)
+{
+	const char *key, *value;
+	int ret;
+
+	ret = dict_iterate(iter->dict_iter, &key, &value);
+	if (ret > 0) {
+		i_assert(iter->prefix_len < strlen(key));
+		return key + iter->prefix_len;
+	}
+	if (ret < 0)
+		iter->failed = TRUE;
+	dict_iterate_deinit(&iter->dict_iter);
+
+	if (iter->iter_idx < array_count(&iter->iter_ids)) {
+		/* get to the next iterator */
+		acl_lookup_dict_iterate_start(iter);
+		return acl_lookup_dict_iterate_visible_next(iter);
+	}
+	return NULL;
+}
+
+int acl_lookup_dict_iterate_visible_deinit(struct acl_lookup_dict_iter **_iter)
+{
+	struct acl_lookup_dict_iter *iter = *_iter;
+	int ret = iter->failed ? -1 : 0;
+
+	*_iter = NULL;
+	if (iter->dict_iter != NULL)
+		dict_iterate_deinit(&iter->dict_iter);
+	pool_unref(&iter->pool);
+	return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/acl/acl-lookup-dict.h	Sun Nov 16 19:20:28 2008 +0200
@@ -0,0 +1,18 @@
+#ifndef ACL_LOOKUP_DICT_H
+#define ACL_LOOKUP_DICT_H
+
+void acl_lookup_dicts_init(void);
+void acl_lookup_dicts_deinit(void);
+
+struct acl_lookup_dict *acl_lookup_dict_init(struct mail_user *user);
+void acl_lookup_dict_deinit(struct acl_lookup_dict **dict);
+
+int acl_lookup_dict_rebuild(struct acl_lookup_dict *dict);
+
+struct acl_lookup_dict_iter *
+acl_lookup_dict_iterate_visible_init(struct acl_lookup_dict *dict);
+const char *
+acl_lookup_dict_iterate_visible_next(struct acl_lookup_dict_iter *iter);
+int acl_lookup_dict_iterate_visible_deinit(struct acl_lookup_dict_iter **iter);
+
+#endif
--- a/src/plugins/acl/acl-mailbox-list.c	Sun Nov 16 19:19:26 2008 +0200
+++ b/src/plugins/acl/acl-mailbox-list.c	Sun Nov 16 19:20:28 2008 +0200
@@ -7,12 +7,11 @@
 #include "mailbox-tree.h"
 #include "mail-namespace.h"
 #include "mailbox-list-private.h"
+#include "acl-api-private.h"
 #include "acl-cache.h"
-#include "acl-api-private.h"
+#include "acl-shared-storage.h"
 #include "acl-plugin.h"
 
-#include <stdlib.h>
-
 #define ACL_LIST_CONTEXT(obj) \
 	MODULE_CONTEXT(obj, acl_mailbox_list_module)
 
@@ -134,6 +133,14 @@
 	ctx->ctx.list = list;
 	ctx->ctx.flags = flags;
 
+	if (list->ns->type == NAMESPACE_SHARED &&
+	    (list->ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) {
+		/* before listing anything add namespaces for all users
+		   who may have visible mailboxes */
+		if (acl_shared_namespaces_add(list->ns) < 0)
+			ctx->ctx.failed = TRUE;
+	}
+
 	T_BEGIN {
 		acl_mailbox_try_list_fast(ctx, patterns);
 	} T_END;
@@ -394,11 +401,12 @@
 
 void acl_mailbox_list_created(struct mailbox_list *list)
 {
+	struct acl_user *auser = ACL_USER_CONTEXT(list->ns->user);
 	struct acl_mailbox_list *alist;
 	struct acl_backend *backend;
 	struct mail_namespace *ns;
 	enum mailbox_list_flags flags;
-	const char *acl_env, *current_username, *owner_username;
+	const char *current_username, *owner_username;
 	bool owner = TRUE;
 
 	if ((list->ns->flags & NAMESPACE_FLAG_INTERNAL) != 0) {
@@ -406,11 +414,8 @@
 		return;
 	}
 
-	acl_env = getenv("ACL");
-	i_assert(acl_env != NULL);
-
 	owner_username = list->ns->user->username;
-	current_username = getenv("MASTER_USER");
+	current_username = auser->master_user;
 	if (current_username == NULL)
 		current_username = owner_username;
 	else
@@ -423,10 +428,8 @@
 	if (ns->type != NAMESPACE_PRIVATE)
 		owner = FALSE;
 
-	backend = acl_backend_init(acl_env, list, current_username,
-				   getenv("ACL_GROUPS") == NULL ? NULL :
-				   t_strsplit(getenv("ACL_GROUPS"), ","),
-				   owner);
+	backend = acl_backend_init(auser->acl_env, list, current_username,
+				   auser->groups, owner);
 	if (backend == NULL)
 		i_fatal("ACL backend initialization failed");
 
--- a/src/plugins/acl/acl-mailbox.c	Sun Nov 16 19:19:26 2008 +0200
+++ b/src/plugins/acl/acl-mailbox.c	Sun Nov 16 19:20:28 2008 +0200
@@ -30,6 +30,13 @@
 static MODULE_CONTEXT_DEFINE_INIT(acl_mail_module, &mail_module_register);
 static struct acl_transaction_context acl_transaction_failure;
 
+struct acl_backend *acl_storage_get_backend(struct mail_storage *storage)
+{
+	struct acl_mail_storage *astorage = ACL_CONTEXT(storage);
+
+	return astorage->rights.backend;
+}
+
 struct acl_object *acl_storage_get_default_aclobj(struct mail_storage *storage)
 {
 	struct acl_mail_storage *astorage = ACL_CONTEXT(storage);
--- a/src/plugins/acl/acl-plugin.c	Sun Nov 16 19:19:26 2008 +0200
+++ b/src/plugins/acl/acl-plugin.c	Sun Nov 16 19:20:28 2008 +0200
@@ -3,12 +3,14 @@
 #include "lib.h"
 #include "mailbox-list-private.h"
 #include "acl-api.h"
+#include "acl-lookup-dict.h"
 #include "acl-plugin.h"
 
 #include <stdlib.h>
 
 void (*acl_next_hook_mail_storage_created)(struct mail_storage *storage);
 void (*acl_next_hook_mailbox_list_created)(struct mailbox_list *list);
+void (*acl_next_hook_mail_user_created)(struct mail_user *user);
 
 const char *acl_plugin_version = PACKAGE_VERSION;
 
@@ -21,6 +23,11 @@
 
 		acl_next_hook_mailbox_list_created = hook_mailbox_list_created;
 		hook_mailbox_list_created = acl_mailbox_list_created;
+
+		acl_next_hook_mail_user_created = hook_mail_user_created;
+		hook_mail_user_created = acl_mail_user_created;
+
+		acl_lookup_dicts_init();
 	} else {
 		if (getenv("DEBUG") != NULL)
 			i_info("acl: No acl setting - ACLs are disabled");
@@ -30,9 +37,9 @@
 void acl_plugin_deinit(void)
 {
 	if (acl_next_hook_mail_storage_created != NULL) {
-		hook_mail_storage_created =
-			acl_next_hook_mail_storage_created;
-		hook_mailbox_list_created =
-			acl_next_hook_mailbox_list_created;
+		acl_lookup_dicts_deinit();
+		hook_mail_storage_created = acl_next_hook_mail_storage_created;
+		hook_mailbox_list_created = acl_next_hook_mailbox_list_created;
+		hook_mail_user_created = acl_next_hook_mail_user_created;
 	}
 }
--- a/src/plugins/acl/acl-plugin.h	Sun Nov 16 19:19:26 2008 +0200
+++ b/src/plugins/acl/acl-plugin.h	Sun Nov 16 19:20:28 2008 +0200
@@ -1,11 +1,25 @@
 #ifndef ACL_PLUGIN_H
 #define ACL_PLUGIN_H
 
+#include "mail-user.h"
 #include "mail-storage-private.h"
 #include "acl-storage.h"
 
 #define ACL_CONTEXT(obj) \
 	MODULE_CONTEXT(obj, acl_storage_module)
+#define ACL_USER_CONTEXT(obj) \
+	MODULE_CONTEXT(obj, acl_user_module)
+
+struct acl_user {
+	union mail_user_module_context module_ctx;
+
+	const char *master_user;
+	const char *acl_env;
+	const char *const *groups;
+
+	struct acl_lookup_dict *acl_lookup_dict;
+	time_t last_shared_add_check;
+};
 
 struct acl_storage_rights_context {
 	struct acl_backend *backend;
@@ -17,13 +31,15 @@
 	struct acl_storage_rights_context rights;
 };
 
-extern void (*acl_next_hook_mail_storage_created)
-	(struct mail_storage *storage);
+extern void (*acl_next_hook_mail_storage_created)(struct mail_storage *storage);
 extern void (*acl_next_hook_mailbox_list_created)(struct mailbox_list *list);
+extern void (*acl_next_hook_mail_user_created)(struct mail_user *user);
 extern MODULE_CONTEXT_DEFINE(acl_storage_module, &mail_storage_module_register);
+extern MODULE_CONTEXT_DEFINE(acl_user_module, &mail_user_module_register);
 
 void acl_mail_storage_created(struct mail_storage *storage);
 void acl_mailbox_list_created(struct mailbox_list *list);
+void acl_mail_user_created(struct mail_user *list);
 
 struct mailbox *acl_mailbox_open_box(struct mailbox *box);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/acl/acl-shared-storage.c	Sun Nov 16 19:20:28 2008 +0200
@@ -0,0 +1,73 @@
+/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "ioloop.h"
+#include "str.h"
+#include "var-expand.h"
+#include "acl-plugin.h"
+#include "acl-lookup-dict.h"
+#include "acl-shared-storage.h"
+#include "index/shared/shared-storage.h"
+
+#define SHARED_NS_RETRY_SECS (60*60)
+
+static void
+acl_shared_namespace_add(struct mail_user *user,
+			 struct shared_storage *sstorage,
+			 const char *userdomain)
+{
+	static struct var_expand_table static_tab[] = {
+		{ 'u', NULL },
+		{ 'n', NULL },
+		{ 'd', NULL },
+		{ '\0', NULL }
+	};
+	struct var_expand_table *tab;
+	struct mail_namespace *ns;
+	const char *p, *mailbox;
+	string_t *str;
+
+	if (strcmp(user->username, userdomain) == 0) {
+		/* skip ourself */
+		return;
+	}
+
+	p = strchr(userdomain, '@');
+
+	tab = t_malloc(sizeof(static_tab));
+	memcpy(tab, static_tab, sizeof(static_tab));
+	tab[0].value = userdomain;
+	tab[1].value = p == NULL ? userdomain : t_strdup_until(userdomain, p);
+	tab[2].value = p == NULL ? "" : p + 1;
+
+	str = t_str_new(128);
+	var_expand(str, sstorage->ns_prefix_pattern, tab);
+	mailbox = str_c(str);
+	shared_storage_get_namespace(&sstorage->storage, &mailbox, &ns);
+}
+
+int acl_shared_namespaces_add(struct mail_namespace *ns)
+{
+	struct shared_storage *sstorage = (struct shared_storage *)ns->storage;
+	struct acl_user *auser = ACL_USER_CONTEXT(ns->user);
+	struct acl_lookup_dict_iter *iter;
+	const char *name;
+
+	i_assert(ns->type == NAMESPACE_SHARED);
+	i_assert(strcmp(ns->storage->name, SHARED_STORAGE_NAME) == 0);
+
+	if (ioloop_time < auser->last_shared_add_check + SHARED_NS_RETRY_SECS) {
+		/* already added, don't bother rechecking */
+		return 0;
+	}
+	auser->last_shared_add_check = ioloop_time;
+
+	iter = acl_lookup_dict_iterate_visible_init(auser->acl_lookup_dict);
+	while ((name = acl_lookup_dict_iterate_visible_next(iter)) != NULL) {
+		T_BEGIN {
+			acl_shared_namespace_add(ns->user, sstorage, name);
+		} T_END;
+	}
+	return acl_lookup_dict_iterate_visible_deinit(&iter);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/acl/acl-shared-storage.h	Sun Nov 16 19:20:28 2008 +0200
@@ -0,0 +1,6 @@
+#ifndef ACL_SHARED_STORAGE_H
+#define ACL_SHARED_STORAGE_H
+
+int acl_shared_namespaces_add(struct mail_namespace *ns);
+
+#endif
--- a/src/plugins/acl/acl-storage.c	Sun Nov 16 19:19:26 2008 +0200
+++ b/src/plugins/acl/acl-storage.c	Sun Nov 16 19:20:28 2008 +0200
@@ -6,10 +6,15 @@
 #include "mail-namespace.h"
 #include "mailbox-list-private.h"
 #include "acl-api-private.h"
+#include "acl-lookup-dict.h"
 #include "acl-plugin.h"
 
+#include <stdlib.h>
+
 struct acl_storage_module acl_storage_module =
 	MODULE_CONTEXT_INIT(&mail_storage_module_register);
+struct acl_user_module acl_user_module =
+	MODULE_CONTEXT_INIT(&mail_user_module_register);
 
 static const char *acl_storage_right_names[ACL_STORAGE_RIGHT_COUNT] = {
 	MAIL_ACL_LOOKUP,
@@ -187,3 +192,37 @@
 		acl_next_hook_mail_storage_created(storage);
 }
 
+static void acl_user_deinit(struct mail_user *user)
+{
+	struct acl_user *auser = ACL_USER_CONTEXT(user);
+
+	acl_lookup_dict_deinit(&auser->acl_lookup_dict);
+	auser->module_ctx.super.deinit(user);
+}
+
+void acl_mail_user_created(struct mail_user *user)
+{
+	struct acl_user *auser;
+	const char *env;
+
+	auser = p_new(user->pool, struct acl_user, 1);
+	auser->module_ctx.super = user->v;
+	user->v.deinit = acl_user_deinit;
+	auser->acl_lookup_dict = acl_lookup_dict_init(user);
+
+	auser->acl_env = getenv("ACL");
+	i_assert(auser->acl_env != NULL);
+	auser->master_user = getenv("MASTER_USER");
+
+	env = getenv("ACL_GROUPS");
+	if (env != NULL) {
+		auser->groups =
+			(const char *const *)p_strsplit(user->pool, env, ",");
+	}
+
+	MODULE_CONTEXT_SET(user, acl_user_module, auser);
+
+	if (acl_next_hook_mail_user_created != NULL)
+		acl_next_hook_mail_user_created(user);
+}
+
--- a/src/plugins/acl/acl-storage.h	Sun Nov 16 19:19:26 2008 +0200
+++ b/src/plugins/acl/acl-storage.h	Sun Nov 16 19:20:28 2008 +0200
@@ -17,6 +17,8 @@
 	ACL_STORAGE_RIGHT_COUNT
 };
 
+/* Returns acl_backend for the given mail storage. */
+struct acl_backend *acl_storage_get_backend(struct mail_storage *storage);
 /* Returns default acl_object for the given mail storage. */
 struct acl_object *acl_storage_get_default_aclobj(struct mail_storage *storage);
 /* Returns acl_object for the given mailbox. */