changeset 13793:ee783a878120

Moved autocreate plugin functionality into lib-storage. The autocreate plugin is still used for backwards compatibility. Mailboxes can be configured like: mailbox Sent { auto = subscribe } mailbox Spam { auto = create }
author Timo Sirainen <tss@iki.fi>
date Fri, 02 Dec 2011 16:22:31 +0200
parents b48fb6a08389
children 32d1a903d42d
files src/config/settings-get.pl src/lib-storage/mail-storage-private.h src/lib-storage/mail-storage-settings.c src/lib-storage/mail-storage-settings.h src/lib-storage/mail-storage.c src/lib-storage/mailbox-list-iter.c src/lib-storage/mailbox-list-private.h src/plugins/autocreate/autocreate-plugin.c
diffstat 8 files changed, 434 insertions(+), 450 deletions(-) [+]
line wrap: on
line diff
--- a/src/config/settings-get.pl	Fri Dec 02 14:49:08 2011 +0200
+++ b/src/config/settings-get.pl	Fri Dec 02 16:22:31 2011 +0200
@@ -8,6 +8,7 @@
 print '#include "file-lock.h"'."\n";
 print '#include "fsync-mode.h"'."\n";
 print '#include "hash-format.h"'."\n";
+print '#include "unichar.h"'."\n";
 print '#include "settings-parser.h"'."\n";
 print '#include "all-settings.h"'."\n";
 print '#include <stddef.h>'."\n";
--- a/src/lib-storage/mail-storage-private.h	Fri Dec 02 14:49:08 2011 +0200
+++ b/src/lib-storage/mail-storage-private.h	Fri Dec 02 16:22:31 2011 +0200
@@ -226,6 +226,8 @@
 
 	/* default vfuncs for new struct mails. */
 	const struct mail_vfuncs *mail_vfuncs;
+	/* Mailbox settings, or NULL if defaults */
+	const struct mailbox_settings *set;
 
 	/* If non-zero, fail mailbox_open() with this error. mailbox_alloc()
 	   can set this to force open to fail. */
--- a/src/lib-storage/mail-storage-settings.c	Fri Dec 02 14:49:08 2011 +0200
+++ b/src/lib-storage/mail-storage-settings.c	Fri Dec 02 16:22:31 2011 +0200
@@ -4,6 +4,7 @@
 #include "array.h"
 #include "hash-format.h"
 #include "var-expand.h"
+#include "unichar.h"
 #include "settings-parser.h"
 #include "mail-index.h"
 #include "mail-user.h"
@@ -15,6 +16,7 @@
 
 static bool mail_storage_settings_check(void *_set, pool_t pool, const char **error_r);
 static bool namespace_settings_check(void *_set, pool_t pool, const char **error_r);
+static bool mailbox_settings_check(void *_set, pool_t pool, const char **error_r);
 static bool mail_user_settings_check(void *_set, pool_t pool, const char **error_r);
 
 #undef DEF
@@ -142,6 +144,37 @@
 };
 
 #undef DEF
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct mailbox_settings, name), NULL }
+
+static const struct setting_define mailbox_setting_defines[] = {
+	DEF(SET_STR, name),
+	{ SET_ENUM, "auto", offsetof(struct mailbox_settings, autocreate), NULL } ,
+
+	SETTING_DEFINE_LIST_END
+};
+
+const struct mailbox_settings mailbox_default_settings = {
+	.name = "",
+	.autocreate = MAILBOX_SET_AUTO_NO":"
+		MAILBOX_SET_AUTO_CREATE":"
+		MAILBOX_SET_AUTO_SUBSCRIBE
+};
+
+const struct setting_parser_info mailbox_setting_parser_info = {
+	.defines = mailbox_setting_defines,
+	.defaults = &mailbox_default_settings,
+
+	.type_offset = offsetof(struct mailbox_settings, name),
+	.struct_size = sizeof(struct mailbox_settings),
+
+	.parent_offset = (size_t)-1,
+	.parent = &mail_user_setting_parser_info,
+
+	.check_func = mailbox_settings_check
+};
+
+#undef DEF
 #undef DEFLIST_UNIQUE
 #define DEF(type, name) \
 	{ type, #name, offsetof(struct mail_user_settings, name), NULL }
@@ -173,6 +206,7 @@
 	DEF(SET_STR, mail_log_prefix),
 
 	DEFLIST_UNIQUE(namespaces, "namespace", &mail_namespace_setting_parser_info),
+	DEFLIST_UNIQUE(mailboxes, "mailbox", &mailbox_setting_parser_info),
 	{ SET_STRLIST, "plugin", offsetof(struct mail_user_settings, plugin_envs), NULL },
 
 	SETTING_DEFINE_LIST_END
@@ -202,6 +236,7 @@
 	.mail_log_prefix = "%s(%u): ",
 
 	.namespaces = ARRAY_INIT,
+	.mailboxes = ARRAY_INIT,
 	.plugin_envs = ARRAY_INIT
 };
 
@@ -425,6 +460,19 @@
 	return TRUE;
 }
 
+static bool mailbox_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+				   const char **error_r)
+{
+	struct mailbox_settings *set = _set;
+
+	if (!uni_utf8_str_is_valid(set->name)) {
+		*error_r = t_strdup_printf("mailbox %s: name isn't valid UTF-8",
+					   set->name);
+		return FALSE;
+	}
+	return TRUE;
+}
+
 static bool mail_user_settings_check(void *_set, pool_t pool ATTR_UNUSED,
 				     const char **error_r ATTR_UNUSED)
 {
--- a/src/lib-storage/mail-storage-settings.h	Fri Dec 02 14:49:08 2011 +0200
+++ b/src/lib-storage/mail-storage-settings.h	Fri Dec 02 16:22:31 2011 +0200
@@ -55,6 +55,16 @@
 	struct mail_user_settings *user_set;
 };
 
+/* <settings checks> */
+#define MAILBOX_SET_AUTO_NO "no"
+#define MAILBOX_SET_AUTO_CREATE "create"
+#define MAILBOX_SET_AUTO_SUBSCRIBE "subscribe"
+/* </settings checks> */
+struct mailbox_settings {
+	const char *name;
+	const char *autocreate;
+};
+
 struct mail_user_settings {
 	const char *base_dir;
 	const char *auth_socket_path;
@@ -77,6 +87,7 @@
 	const char *mail_log_prefix;
 
 	ARRAY_DEFINE(namespaces, struct mail_namespace_settings *);
+	ARRAY_DEFINE(mailboxes, struct mailbox_settings *);
 	ARRAY_DEFINE(plugin_envs, const char *);
 };
 
@@ -84,6 +95,7 @@
 extern const struct setting_parser_info mail_namespace_setting_parser_info;
 extern const struct setting_parser_info mail_storage_setting_parser_info;
 extern const struct mail_namespace_settings mail_namespace_default_settings;
+extern const struct mailbox_settings mailbox_default_settings;
 
 const void *
 mail_user_set_get_driver_settings(const struct setting_parser_info *info,
--- a/src/lib-storage/mail-storage.c	Fri Dec 02 14:49:08 2011 +0200
+++ b/src/lib-storage/mail-storage.c	Fri Dec 02 16:22:31 2011 +0200
@@ -602,6 +602,18 @@
 	return TRUE;
 }
 
+static struct mailbox_settings *
+mailbox_settings_find(struct mail_user *user, const char *vname)
+{
+	struct mailbox_settings *const *box_set;
+
+	array_foreach(&user->set->mailboxes, box_set) {
+		if (strcmp((*box_set)->name, vname) == 0)
+			return *box_set;
+	}
+	return NULL;
+}
+
 struct mailbox *mailbox_alloc(struct mailbox_list *list, const char *vname,
 			      enum mailbox_flags flags)
 {
@@ -628,6 +640,7 @@
 
 	T_BEGIN {
 		box = storage->v.mailbox_alloc(storage, new_list, vname, flags);
+		box->set = mailbox_settings_find(storage->user, vname);
 		hook_mailbox_allocated(box);
 	} T_END;
 
@@ -704,6 +717,14 @@
 	return FALSE;
 }
 
+static bool mailbox_is_autocreated(struct mailbox *box)
+{
+	if (box->inbox_user)
+		return TRUE;
+	return box->set != NULL &&
+		strcmp(box->set->autocreate, MAILBOX_SET_AUTO_NO) != 0;
+}
+
 int mailbox_exists(struct mailbox *box, bool auto_boxes,
 		   enum mailbox_existence *existence_r)
 {
@@ -732,8 +753,7 @@
 		return 0;
 	}
 
-	if (strcmp(box->name, "INBOX") == 0 && box->inbox_user && auto_boxes) {
-		/* INBOX always exists */
+	if (auto_boxes && box->set != NULL && mailbox_is_autocreated(box)) {
 		*existence_r = MAILBOX_EXISTENCE_SELECT;
 		return 0;
 	}
@@ -788,6 +808,47 @@
 	return 0;
 }
 
+static void mailbox_autocreate(struct mailbox *box)
+{
+	const char *errstr;
+	enum mail_error error;
+
+	if (mailbox_create(box, NULL, FALSE) < 0) {
+		errstr = mailbox_get_last_error(box, &error);
+		if (error != MAIL_ERROR_NOTFOUND && !box->inbox_user) {
+			mail_storage_set_critical(box->storage,
+				"Failed to autocreate mailbox %s: %s",
+				box->vname, errstr);
+		}
+	} else if (box->set != NULL &&
+		   strcmp(box->set->autocreate,
+			  MAILBOX_SET_AUTO_SUBSCRIBE) == 0) {
+		if (mailbox_set_subscribed(box, TRUE) < 0) {
+			mail_storage_set_critical(box->storage,
+				"Failed to autosubscribe to mailbox %s: %s",
+				box->vname, mailbox_get_last_error(box, NULL));
+		}
+	}
+}
+
+static int mailbox_autocreate_and_reopen(struct mailbox *box)
+{
+	int ret;
+
+	mailbox_autocreate(box);
+	mailbox_close(box);
+
+	ret = box->v.open(box);
+	if (ret < 0 && box->inbox_user &&
+	    !box->storage->user->inbox_open_error_logged) {
+		box->storage->user->inbox_open_error_logged = TRUE;
+		mail_storage_set_critical(box->storage,
+			"Opening INBOX failed: %s",
+			mailbox_get_last_error(box, NULL));
+	}
+	return ret;
+}
+
 static int mailbox_open_full(struct mailbox *box, struct istream *input)
 {
 	int ret;
@@ -832,16 +893,8 @@
 	} T_END;
 
 	if (ret < 0 && box->storage->error == MAIL_ERROR_NOTFOUND &&
-	    box->input == NULL && box->inbox_user) T_BEGIN {
-		/* INBOX should always exist. try to create it and retry. */
-		(void)mailbox_create(box, NULL, FALSE);
-		mailbox_close(box);
-		ret = box->v.open(box);
-		if (ret < 0 && !box->storage->user->inbox_open_error_logged) {
-			box->storage->user->inbox_open_error_logged = TRUE;
-			i_error("Opening INBOX failed: %s",
-				mailbox_get_last_error(box, NULL));
-		}
+	    box->input == NULL && mailbox_is_autocreated(box)) T_BEGIN {
+		ret = mailbox_autocreate_and_reopen(box);
 	} T_END;
 
 	if (ret < 0) {
--- a/src/lib-storage/mailbox-list-iter.c	Fri Dec 02 14:49:08 2011 +0200
+++ b/src/lib-storage/mailbox-list-iter.c	Fri Dec 02 16:22:31 2011 +0200
@@ -1,10 +1,35 @@
 /* Copyright (c) 2006-2011 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "array.h"
 #include "imap-match.h"
 #include "mailbox-tree.h"
 #include "mailbox-list-private.h"
 
+enum autocreate_match_result {
+	/* list contains the mailbox */
+	AUTOCREATE_MATCH_RESULT_YES		= 0x01,
+	/* list contains children of the mailbox */
+	AUTOCREATE_MATCH_RESULT_CHILDREN	= 0x02,
+	/* list contains parents of the mailbox */
+	AUTOCREATE_MATCH_RESULT_PARENT		= 0x04
+};
+
+struct autocreate_box {
+	const char *name;
+	enum mailbox_info_flags flags;
+	bool child_listed;
+};
+
+ARRAY_DEFINE_TYPE(mailbox_settings, struct mailbox_settings *);
+struct mailbox_list_autocreate_iterate_context {
+	unsigned int idx;
+	struct mailbox_info new_info;
+	ARRAY_DEFINE(boxes, struct autocreate_box);
+	ARRAY_TYPE(mailbox_settings) box_sets;
+	ARRAY_TYPE(mailbox_settings) all_ns_box_sets;
+};
+
 struct ns_list_iterate_context {
 	struct mailbox_list_iterate_context ctx;
 	struct mailbox_list_iterate_context *backend_ctx;
@@ -41,6 +66,47 @@
 	return ns->list->v.subscriptions_refresh(ns->list, list);
 }
 
+static void
+mailbox_list_iter_init_autocreate(struct mailbox_list_iterate_context *ctx)
+{
+	struct mail_user *user = ctx->list->ns->user;
+	struct mailbox_list_autocreate_iterate_context *actx;
+	struct mailbox_settings *const *box_sets;
+	struct mail_namespace *ns;
+	struct autocreate_box *autobox;
+	unsigned int i, count;
+
+	box_sets = array_get(&user->set->mailboxes, &count);
+	if (count == 0)
+		return;
+
+	actx = p_new(ctx->pool, struct mailbox_list_autocreate_iterate_context, 1);
+	ctx->autocreate_ctx = actx;
+
+	/* build the list of mailboxes we need to consider as existing */
+	p_array_init(&actx->boxes, ctx->pool, 16);
+	p_array_init(&actx->box_sets, ctx->pool, 16);
+	p_array_init(&actx->all_ns_box_sets, ctx->pool, 16);
+	for (i = 0; i < count; i++) {
+		if (strcmp(box_sets[i]->autocreate, MAILBOX_SET_AUTO_NO) == 0)
+			continue;
+
+		ns = mail_namespace_find(user->namespaces, box_sets[i]->name);
+		if (ns != ctx->list->ns)
+			continue;
+
+		/* autocreate mailbox belongs to listed namespace */
+		array_append(&actx->all_ns_box_sets, &box_sets[i], 1);
+		if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 ||
+		    strcmp(box_sets[i]->autocreate,
+			   MAILBOX_SET_AUTO_SUBSCRIBE) == 0) {
+			array_append(&actx->box_sets, &box_sets[i], 1);
+			autobox = array_append_space(&actx->boxes);
+			autobox->name = box_sets[i]->name;
+		}
+	}
+}
+
 struct mailbox_list_iterate_context *
 mailbox_list_iter_init_multiple(struct mailbox_list *list,
 				const char *const *patterns,
@@ -58,6 +124,8 @@
 	ctx = list->v.iter_init(list, patterns, flags);
 	if (ret < 0)
 		ctx->failed = TRUE;
+	else if ((flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0)
+		mailbox_list_iter_init_autocreate(ctx);
 	return ctx;
 }
 
@@ -297,14 +365,210 @@
 	return &ctx->ctx;
 }
 
+static enum autocreate_match_result
+autocreate_box_match(const ARRAY_TYPE(mailbox_settings) *boxes,
+		     struct mail_namespace *ns, const char *name,
+		     bool only_subscribed, unsigned int *idx_r)
+{
+	struct mailbox_settings *const *sets;
+	unsigned int i, count, len, name_len = strlen(name);
+	enum autocreate_match_result result = 0;
+	char sep = mail_namespace_get_sep(ns);
+
+	*idx_r = -1U;
+
+	sets = array_get(boxes, &count);
+	for (i = 0; i < count; i++) {
+		if (only_subscribed &&
+		    strcmp(sets[i]->autocreate, MAILBOX_SET_AUTO_SUBSCRIBE) != 0)
+			continue;
+		len = I_MIN(name_len, strlen(sets[i]->name));
+		if (strncmp(name, sets[i]->name, len) != 0)
+			continue;
+
+		if (name[len] == '\0' && sets[i]->name[len] == '\0') {
+			result |= AUTOCREATE_MATCH_RESULT_YES;
+			*idx_r = i;
+		} else if (name[len] == '\0' && sets[i]->name[len] == sep)
+			result |= AUTOCREATE_MATCH_RESULT_CHILDREN;
+		else if (name[len] == sep && sets[i]->name[len] == '\0')
+			result |= AUTOCREATE_MATCH_RESULT_PARENT;
+	}
+	return result;
+}
+
+static const struct mailbox_info *
+autocreate_iter_existing(struct mailbox_list_iterate_context *ctx)
+{
+	struct mailbox_list_autocreate_iterate_context *actx =
+		ctx->autocreate_ctx;
+	struct mailbox_info *info = &actx->new_info;
+	enum autocreate_match_result match, match2;
+	unsigned int idx;
+
+	match = autocreate_box_match(&actx->box_sets, ctx->list->ns,
+				     info->name, FALSE, &idx);
+
+	if ((match & AUTOCREATE_MATCH_RESULT_YES) != 0) {
+		/* we have an exact match in the list.
+		   don't list it at the end. */
+		array_delete(&actx->boxes, idx, 1);
+		array_delete(&actx->box_sets, idx, 1);
+	}
+
+	if ((match & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) {
+		if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
+			info->flags |= MAILBOX_CHILD_SUBSCRIBED;
+		else {
+			info->flags &= ~MAILBOX_NOCHILDREN;
+			info->flags |= MAILBOX_CHILDREN;
+		}
+	}
+
+	/* make sure the mailbox existence flags are correct. */
+	if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0)
+		match2 = match;
+	else {
+		info->flags |= MAILBOX_SUBSCRIBED;
+		match2 = autocreate_box_match(&actx->all_ns_box_sets,
+					      ctx->list->ns, info->name,
+					      FALSE, &idx);
+	}
+	if ((match2 & AUTOCREATE_MATCH_RESULT_YES) != 0)
+		info->flags &= ~MAILBOX_NONEXISTENT;
+	if ((match2 & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) {
+		info->flags &= ~MAILBOX_NOCHILDREN;
+		info->flags |= MAILBOX_CHILDREN;
+	}
+
+	if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 &&
+	    (ctx->flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) != 0) {
+		/* we're listing all mailboxes and want \Subscribed flag */
+		match2 = autocreate_box_match(&actx->all_ns_box_sets,
+					      ctx->list->ns, info->name,
+					      TRUE, &idx);
+		if ((match2 & AUTOCREATE_MATCH_RESULT_YES) != 0) {
+			/* mailbox is also marked as autosubscribe */
+			info->flags |= MAILBOX_SUBSCRIBED;
+		}
+		if ((match2 & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) {
+			/* mailbox also has a children marked as
+			   autosubscribe */
+			info->flags |= MAILBOX_CHILD_SUBSCRIBED;
+		}
+	}
+
+	if ((match & AUTOCREATE_MATCH_RESULT_PARENT) != 0) {
+		/* there are autocreate parent boxes.
+		   set their children flag states. */
+		struct autocreate_box *autobox;
+		unsigned int name_len;
+		char sep = mail_namespace_get_sep(ctx->list->ns);
+
+		array_foreach_modifiable(&actx->boxes, autobox) {
+			name_len = strlen(autobox->name);
+			if (strncmp(info->name, autobox->name, name_len) != 0 ||
+			    info->name[name_len] != sep)
+				continue;
+
+			if ((info->flags & MAILBOX_NONEXISTENT) == 0)
+				autobox->flags |= MAILBOX_CHILDREN;
+			if ((info->flags & MAILBOX_SUBSCRIBED) != 0)
+				autobox->flags |= MAILBOX_CHILD_SUBSCRIBED;
+			autobox->child_listed = TRUE;
+		}
+	}
+	return info;
+}
+
+static bool autocreate_iter_autobox(struct mailbox_list_iterate_context *ctx,
+				    const struct autocreate_box *autobox)
+{
+	struct mailbox_list_autocreate_iterate_context *actx =
+		ctx->autocreate_ctx;
+	enum imap_match_result match;
+
+	memset(&actx->new_info, 0, sizeof(actx->new_info));
+	actx->new_info.ns = ctx->list->ns;
+	actx->new_info.name = autobox->name;
+	actx->new_info.flags = autobox->flags;
+
+	if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
+		actx->new_info.flags |= MAILBOX_SUBSCRIBED;
+
+	if ((actx->new_info.flags & MAILBOX_CHILDREN) == 0)
+		actx->new_info.flags |= MAILBOX_NOCHILDREN;
+
+	match = imap_match(ctx->glob, actx->new_info.name);
+	if (match == IMAP_MATCH_YES)
+		return TRUE;
+	if ((match & IMAP_MATCH_PARENT) != 0 && !autobox->child_listed) {
+		enum mailbox_info_flags old_flags = actx->new_info.flags;
+		char sep = mail_namespace_get_sep(ctx->list->ns);
+		const char *p;
+
+		/* e.g. autocreate=foo/bar and we're listing % */
+		actx->new_info.flags = MAILBOX_NONEXISTENT |
+			(old_flags & (MAILBOX_CHILDREN |
+				      MAILBOX_CHILD_SUBSCRIBED));
+		if ((old_flags & MAILBOX_NONEXISTENT) == 0) {
+			actx->new_info.flags |= MAILBOX_CHILDREN;
+			actx->new_info.flags &= ~MAILBOX_NOCHILDREN;
+		}
+		if ((old_flags & MAILBOX_SUBSCRIBED) != 0)
+			actx->new_info.flags |= MAILBOX_CHILD_SUBSCRIBED;
+		do {
+			p = strrchr(actx->new_info.name, sep);
+			i_assert(p != NULL);
+			actx->new_info.name =
+				t_strdup_until(actx->new_info.name, p);
+			match = imap_match(ctx->glob, actx->new_info.name);
+		} while (match != IMAP_MATCH_YES);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static const struct mailbox_info *
+autocreate_iter_next(struct mailbox_list_iterate_context *ctx)
+{
+	struct mailbox_list_autocreate_iterate_context *actx =
+		ctx->autocreate_ctx;
+	const struct mailbox_info *info;
+	const struct autocreate_box *autoboxes;
+	unsigned int count;
+
+	if (actx->idx == 0) {
+		info = ctx->list->v.iter_next(ctx);
+		if (info != NULL) {
+			ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE;
+			actx->new_info = *info;
+			return autocreate_iter_existing(ctx);
+		}
+	}
+
+	/* list missing mailboxes */
+	autoboxes = array_get(&actx->boxes, &count);
+	while (actx->idx < count) {
+		if (autocreate_iter_autobox(ctx, &autoboxes[actx->idx++]))
+			return &actx->new_info;
+	}
+	i_assert(array_count(&actx->boxes) == array_count(&actx->box_sets));
+	return NULL;
+}
+
 const struct mailbox_info *
 mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx)
 {
 	const struct mailbox_info *info;
 
-	info = ctx->list->v.iter_next(ctx);
-	if (info != NULL)
-		ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE;
+	if (ctx->autocreate_ctx != NULL)
+		info = autocreate_iter_next(ctx);
+	else {
+		info = ctx->list->v.iter_next(ctx);
+		if (info != NULL)
+			ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE;
+	}
 	return info;
 }
 
--- a/src/lib-storage/mailbox-list-private.h	Fri Dec 02 14:49:08 2011 +0200
+++ b/src/lib-storage/mailbox-list-private.h	Fri Dec 02 16:22:31 2011 +0200
@@ -146,6 +146,7 @@
 	bool failed;
 
 	struct imap_match_glob *glob;
+	struct mailbox_list_autocreate_iterate_context *autocreate_ctx;
 
 	ARRAY_DEFINE(module_contexts,
 		     union mailbox_list_iterate_module_context *);
--- a/src/plugins/autocreate/autocreate-plugin.c	Fri Dec 02 14:49:08 2011 +0200
+++ b/src/plugins/autocreate/autocreate-plugin.c	Fri Dec 02 16:22:31 2011 +0200
@@ -1,438 +1,54 @@
 /* Copyright (c) 2007-2011 Dovecot authors, see the included COPYING file */
 
+/* FIXME: this plugin is only for backwards compatibility. log a warning in
+   v2.2 about this and in later versions remove completely */
+
 #include "lib.h"
+#include "array.h"
 #include "unichar.h"
-#include "imap-match.h"
-#include "mailbox-list-private.h"
-#include "mail-storage-private.h"
+#include "mail-user.h"
 #include "mail-storage-hooks.h"
 #include "autocreate-plugin.h"
 
-#include <stdlib.h>
-
-#define AUTOCREATE_USER_CONTEXT(obj) \
-	MODULE_CONTEXT(obj, autocreate_user_module)
-#define AUTOCREATE_LIST_CONTEXT(obj) \
-	MODULE_CONTEXT(obj, autocreate_list_module)
-#define AUTOCREATE_CONTEXT(obj) \
-	MODULE_CONTEXT(obj, autocreate_storage_module)
-
-enum match_result {
-	/* list contains the mailbox */
-	MATCH_RESULT_YES	= 0x01,
-	/* list contains children of the mailbox */
-	MATCH_RESULT_CHILDREN	= 0x02,
-	/* list contains parents of the mailbox */
-	MATCH_RESULT_PARENT	= 0x04
-};
-
-struct autocreate_box {
-	const char *name;
-	unsigned int name_len;
-	enum mailbox_info_flags flags;
-	bool child_listed;
-
-	struct mail_namespace *ns;
-};
-ARRAY_DEFINE_TYPE(autocreate_box, struct autocreate_box);
-
-struct autocreate_user {
-	union mail_user_module_context module_ctx;
-
-	ARRAY_TYPE(autocreate_box) autocreate_mailboxes;
-	ARRAY_TYPE(autocreate_box) autosubscribe_mailboxes;
-};
-
-struct autocreate_mailbox_list_iterate_context {
-	union mailbox_list_iterate_module_context module_ctx;
-
-	pool_t pool;
-	unsigned int idx;
-	struct mailbox_info new_info;
-	ARRAY_TYPE(autocreate_box) boxes;
-};
-
-struct autocreate_mailbox_list {
-	union mailbox_list_module_context module_ctx;
-};
-
-const char *autocreate_plugin_version = DOVECOT_VERSION;
-
-static MODULE_CONTEXT_DEFINE_INIT(autocreate_user_module,
-				  &mail_user_module_register);
-static MODULE_CONTEXT_DEFINE_INIT(autocreate_list_module,
-				  &mailbox_list_module_register);
-static MODULE_CONTEXT_DEFINE_INIT(autocreate_storage_module,
-				  &mail_storage_module_register);
-
-static enum match_result
-autocreate_box_match(const ARRAY_TYPE(autocreate_box) *boxes, const char *name,
-		     unsigned int *idx_r)
-{
-	const struct autocreate_box *autoboxes;
-	unsigned int i, count, len, name_len = strlen(name);
-	enum match_result result = 0;
-	char sep;
-
-	*idx_r = -1U;
-
-	autoboxes = array_get(boxes, &count);
-	for (i = 0; i < count; i++) {
-		len = I_MIN(name_len, autoboxes[i].name_len);
-		if (strncmp(name, autoboxes[i].name, len) != 0)
-			continue;
-
-		sep = mail_namespace_get_sep(autoboxes[i].ns);
-		if (name[len] == '\0' && autoboxes[i].name[len] == '\0') {
-			result |= MATCH_RESULT_YES;
-			*idx_r = i;
-		} else if (name[len] == '\0' && autoboxes[i].name[len] == sep)
-			result |= MATCH_RESULT_CHILDREN;
-		else if (name[len] == sep && autoboxes[i].name[len] == '\0')
-			result |= MATCH_RESULT_PARENT;
-	}
-	return result;
-}
-
-static bool
-is_autocreated(struct mail_user *user, const char *name)
+static struct mailbox_settings *
+mailbox_settings_find(struct mail_user *user, const char *vname)
 {
-	struct autocreate_user *auser = AUTOCREATE_USER_CONTEXT(user);
-	unsigned int idx;
-
-	return autocreate_box_match(&auser->autocreate_mailboxes,
-				    name, &idx) == MATCH_RESULT_YES;
-}
-
-static bool
-is_autosubscribed(struct mail_user *user, const char *name)
-{
-	struct autocreate_user *auser = AUTOCREATE_USER_CONTEXT(user);
-	unsigned int idx;
-
-	return autocreate_box_match(&auser->autosubscribe_mailboxes,
-				    name, &idx) == MATCH_RESULT_YES;
-}
-
-static int autocreate_mailbox_open(struct mailbox *box)
-{
-	union mailbox_module_context *abox = AUTOCREATE_CONTEXT(box);
-	int ret;
-
-	if ((ret = abox->super.open(box)) < 0 &&
-	    mailbox_get_last_mail_error(box) == MAIL_ERROR_NOTFOUND &&
-	    is_autocreated(box->storage->user, box->vname)) {
-		/* autocreate the mailbox */
-		if (mailbox_create(box, NULL, FALSE) < 0) {
-			i_error("autocreate: Failed to create mailbox %s: %s",
-				box->vname, mailbox_get_last_error(box, NULL));
-		}
-		mailbox_close(box);
-		ret = box->v.open(box);
-	}
-	return ret;
-}
-
-static int autocreate_mailbox_exists(struct mailbox *box, bool auto_boxes,
-				     enum mailbox_existence *existence_r)
-{
-	union mailbox_module_context *abox = AUTOCREATE_CONTEXT(box);
-
-	if (auto_boxes && is_autocreated(box->storage->user, box->vname)) {
-		*existence_r = MAILBOX_EXISTENCE_SELECT;
-		return 0;
-	}
-
-	return abox->super.exists(box, auto_boxes, existence_r);
-}
-
-static int
-autocreate_mailbox_create(struct mailbox *box,
-			  const struct mailbox_update *update,
-			  bool directory)
-{
-	union mailbox_module_context *abox = AUTOCREATE_CONTEXT(box);
-
-	if (abox->super.create(box, update, directory) < 0)
-		return -1;
-
-	if (is_autosubscribed(box->storage->user, box->vname)) {
-		if (mailbox_set_subscribed(box, TRUE) < 0) {
-			i_error("autocreate: Failed to subscribe to mailbox %s: %s",
-				box->vname, mailbox_get_last_error(box, NULL));
-		}
-	}
-	return 0;
-}
-
-static void autocreate_mailbox_allocated(struct mailbox *box)
-{
-	struct mailbox_vfuncs *v = box->vlast;
-	union mailbox_module_context *abox;
-
-	abox = p_new(box->pool, union mailbox_module_context, 1);
-	abox->super = *v;
-	box->vlast = &abox->super;
-	v->open = autocreate_mailbox_open;
-	v->exists = autocreate_mailbox_exists;
-	v->create = autocreate_mailbox_create;
-
-	MODULE_CONTEXT_SET_SELF(box, autocreate_storage_module, abox);
-}
-
-static struct mailbox_list_iterate_context *
-autocreate_iter_init(struct mailbox_list *list,
-		     const char *const *patterns,
-		     enum mailbox_list_iter_flags flags)
-{
-	union mailbox_list_module_context *alist =
-		AUTOCREATE_LIST_CONTEXT(list);
-	struct mail_user *user = list->ns->user;
-	struct autocreate_user *auser = AUTOCREATE_USER_CONTEXT(user);
-	struct mailbox_list_iterate_context *ctx;
-	struct autocreate_mailbox_list_iterate_context *actx;
-	const ARRAY_TYPE(autocreate_box) *extra_boxes;
-	const struct autocreate_box *autobox;
-	pool_t pool;
+	struct mailbox_settings *const *box_set;
 
-	ctx = alist->super.iter_init(list, patterns, flags);
-
-	pool = pool_alloconly_create("autocreate list iter", 1024);
-	actx = p_new(pool, struct autocreate_mailbox_list_iterate_context, 1);
-	actx->pool = pool;
-
-	p_array_init(&actx->boxes, pool, 16);
-	if ((flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0) {
-		if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0)
-			extra_boxes = &auser->autocreate_mailboxes;
-		else
-			extra_boxes = &auser->autosubscribe_mailboxes;
-
-		array_foreach(extra_boxes, autobox) {
-			if (autobox->ns == list->ns)
-				array_append(&actx->boxes, autobox, 1);
-		}
-	}
-
-	MODULE_CONTEXT_SET(ctx, autocreate_list_module, actx);
-	return ctx;
-}
-
-static int autocreate_iter_deinit(struct mailbox_list_iterate_context *ctx)
-{
-	union mailbox_list_module_context *alist =
-		AUTOCREATE_LIST_CONTEXT(ctx->list);
-	struct autocreate_mailbox_list_iterate_context *actx =
-		AUTOCREATE_LIST_CONTEXT(ctx);
-
-	pool_unref(&actx->pool);
-	return alist->super.iter_deinit(ctx);
-}
-
-static const struct mailbox_info *
-autocreate_iter_existing(struct mailbox_list_iterate_context *ctx)
-{
-	struct autocreate_mailbox_list_iterate_context *actx =
-		AUTOCREATE_LIST_CONTEXT(ctx);
-	struct autocreate_user *auser =
-		AUTOCREATE_USER_CONTEXT(ctx->list->ns->user);
-	struct mailbox_info *info = &actx->new_info;
-	enum match_result match, match2;
-	unsigned int idx;
-
-	match = autocreate_box_match(&actx->boxes, info->name, &idx);
-
-	if ((match & MATCH_RESULT_YES) != 0) {
-		/* we have an exact match in the list.
-		   don't list it at the end. */
-		array_delete(&actx->boxes, idx, 1);
-	}
-
-	if ((match & MATCH_RESULT_CHILDREN) != 0) {
-		if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
-			info->flags |= MAILBOX_CHILD_SUBSCRIBED;
-		else {
-			info->flags &= ~MAILBOX_NOCHILDREN;
-			info->flags |= MAILBOX_CHILDREN;
-		}
-	}
-
-	/* make sure the mailbox existence flags are correct. */
-	if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0)
-		match2 = match;
-	else {
-		info->flags |= MAILBOX_SUBSCRIBED;
-		match2 = autocreate_box_match(&auser->autocreate_mailboxes,
-					      info->name, &idx);
-	}
-	if ((match2 & MATCH_RESULT_YES) != 0)
-		info->flags &= ~MAILBOX_NONEXISTENT;
-	if ((match2 & MATCH_RESULT_CHILDREN) != 0) {
-		info->flags &= ~MAILBOX_NOCHILDREN;
-		info->flags |= MAILBOX_CHILDREN;
-	}
-
-	if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 &&
-	    (ctx->flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) != 0) {
-		/* we're listing all mailboxes and want \Subscribed flag */
-		match2 = autocreate_box_match(&auser->autosubscribe_mailboxes,
-					      info->name, &idx);
-		if ((match2 & MATCH_RESULT_YES) != 0) {
-			/* mailbox is also marked as autosubscribe */
-			info->flags |= MAILBOX_SUBSCRIBED;
-		}
-		if ((match2 & MATCH_RESULT_CHILDREN) != 0) {
-			/* mailbox also has a children marked as
-			   autosubscribe */
-			info->flags |= MAILBOX_CHILD_SUBSCRIBED;
-		}
-	}
-
-	if ((match & MATCH_RESULT_PARENT) != 0) {
-		/* there are autocreate parent boxes.
-		   set their children flag states. */
-		struct autocreate_box *autobox;
-		char sep;
-
-		array_foreach_modifiable(&actx->boxes, autobox) {
-			sep = mail_namespace_get_sep(autobox->ns);
-
-			if (strncmp(info->name, autobox->name,
-				    autobox->name_len) != 0 ||
-			    info->name[autobox->name_len] != sep)
-				continue;
-
-			if ((info->flags & MAILBOX_NONEXISTENT) == 0)
-				autobox->flags |= MAILBOX_CHILDREN;
-			if ((info->flags & MAILBOX_SUBSCRIBED) != 0)
-				autobox->flags |= MAILBOX_CHILD_SUBSCRIBED;
-			autobox->child_listed = TRUE;
-		}
-	}
-	return info;
-}
-
-static bool autocreate_iter_autobox(struct mailbox_list_iterate_context *ctx,
-				    const struct autocreate_box *autobox)
-{
-	struct autocreate_mailbox_list_iterate_context *actx =
-		AUTOCREATE_LIST_CONTEXT(ctx);
-	enum imap_match_result match;
-
-	memset(&actx->new_info, 0, sizeof(actx->new_info));
-	actx->new_info.ns = ctx->list->ns;
-	actx->new_info.name = autobox->name;
-	actx->new_info.flags = autobox->flags;
-
-	if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
-		actx->new_info.flags |= MAILBOX_SUBSCRIBED;
-
-	if ((actx->new_info.flags & MAILBOX_CHILDREN) == 0)
-		actx->new_info.flags |= MAILBOX_NOCHILDREN;
-
-	match = imap_match(ctx->glob, actx->new_info.name);
-	if (match == IMAP_MATCH_YES)
-		return TRUE;
-	if ((match & IMAP_MATCH_PARENT) != 0 && !autobox->child_listed) {
-		enum mailbox_info_flags old_flags = actx->new_info.flags;
-		char sep = mail_namespace_get_sep(ctx->list->ns);
-		const char *p;
-
-		/* e.g. autocreate=foo/bar and we're listing % */
-		actx->new_info.flags = MAILBOX_NONEXISTENT |
-			(old_flags & (MAILBOX_CHILDREN |
-				      MAILBOX_CHILD_SUBSCRIBED));
-		if ((old_flags & MAILBOX_NONEXISTENT) == 0) {
-			actx->new_info.flags |= MAILBOX_CHILDREN;
-			actx->new_info.flags &= ~MAILBOX_NOCHILDREN;
-		}
-		if ((old_flags & MAILBOX_SUBSCRIBED) != 0)
-			actx->new_info.flags |= MAILBOX_CHILD_SUBSCRIBED;
-		do {
-			p = strrchr(actx->new_info.name, sep);
-			i_assert(p != NULL);
-			actx->new_info.name =
-				t_strdup_until(actx->new_info.name, p);
-			match = imap_match(ctx->glob, actx->new_info.name);
-		} while (match != IMAP_MATCH_YES);
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static const struct mailbox_info *
-autocreate_iter_next(struct mailbox_list_iterate_context *ctx)
-{
-	union mailbox_list_module_context *alist =
-		AUTOCREATE_LIST_CONTEXT(ctx->list);
-	struct autocreate_mailbox_list_iterate_context *actx =
-		AUTOCREATE_LIST_CONTEXT(ctx);
-	const struct mailbox_info *info;
-	const struct autocreate_box *autoboxes;
-	unsigned int count;
-
-	if (actx->idx == 0) {
-		info = alist->super.iter_next(ctx);
-		if (info != NULL) {
-			actx->new_info = *info;
-			return autocreate_iter_existing(ctx);
-		}
-	}
-
-	/* list missing mailboxes */
-	autoboxes = array_get(&actx->boxes, &count);
-	while (actx->idx < count) {
-		if (autocreate_iter_autobox(ctx, &autoboxes[actx->idx++]))
-			return &actx->new_info;
+	array_foreach(&user->set->mailboxes, box_set) {
+		if (strcmp((*box_set)->name, vname) == 0)
+			return *box_set;
 	}
 	return NULL;
 }
 
-static void autocreate_mailbox_list_created(struct mailbox_list *list)
+static void
+add_autobox(struct mail_user *user, const char *vname, bool subscriptions)
 {
-	struct mailbox_list_vfuncs *v = list->vlast;
-	union mailbox_list_module_context *alist;
+	struct mailbox_settings *set;
+
+	if (!uni_utf8_str_is_valid(vname)) {
+		i_error("autocreate: Mailbox name isn't valid UTF-8: %s",
+			vname);
+		return;
+	}
 
-	alist = p_new(list->pool, union mailbox_list_module_context, 1);
-	alist->super = *v;
-	list->vlast = &alist->super;
-	v->iter_init = autocreate_iter_init;
-	v->iter_deinit = autocreate_iter_deinit;
-	v->iter_next = autocreate_iter_next;
-
-	MODULE_CONTEXT_SET_SELF(list, autocreate_list_module, alist);
+	set = mailbox_settings_find(user, vname);
+	if (set == NULL) {
+		set = p_new(user->pool, struct mailbox_settings, 1);
+		set->name = p_strdup(user->pool, vname);
+		set->autocreate = MAILBOX_SET_AUTO_NO;
+		array_append(&user->set->mailboxes, &set, 1);
+	}
+	if (subscriptions)
+		set->autocreate = MAILBOX_SET_AUTO_SUBSCRIBE;
+	else if (strcmp(set->autocreate, MAILBOX_SET_AUTO_SUBSCRIBE) != 0)
+		set->autocreate = MAILBOX_SET_AUTO_CREATE;
 }
 
 static void
-add_autobox(struct mail_user *user, ARRAY_TYPE(autocreate_box) *boxes,
-	    const char *value)
-{
-	struct autocreate_box *autobox;
-	struct mail_namespace *ns;
-
-	if (!uni_utf8_str_is_valid(value)) {
-		i_error("autocreate: Mailbox name isn't valid UTF-8: %s",
-			value);
-		return;
-	}
-
-	if ((ns = mail_namespace_find(user->namespaces, value)) == NULL) {
-		if (user->mail_debug) {
-			i_debug("autocreate: Namespace not found for mailbox: %s",
-				value);
-		}
-		return;
-	}
-
-	autobox = array_append_space(boxes);
-	autobox->name = p_strdup(user->pool, value);
-	autobox->name_len = strlen(value);
-	autobox->ns = ns;
-}
-
-static void read_autobox_settings(struct mail_user *user,
-				  ARRAY_TYPE(autocreate_box) *boxes,
-				  const char *env_name_base)
+read_autobox_settings(struct mail_user *user, const char *env_name_base,
+		      bool subscriptions)
 {
 	const char *value;
 	char env_name[20];
@@ -440,7 +56,7 @@
 
 	value = mail_user_plugin_getenv(user, env_name_base);
 	while (value != NULL) {
-		add_autobox(user, boxes, value);
+		add_autobox(user, value, subscriptions);
 
 		i_snprintf(env_name, sizeof(env_name), "%s%d",
 			   env_name_base, ++i);
@@ -449,27 +65,14 @@
 }
 
 static void
-autocreate_mail_namespaces_created(struct mail_namespace *namespaces)
+autocreate_mail_user_created(struct mail_user *user)
 {
-	struct mail_user *user = namespaces->user;
-	struct autocreate_user *auser;
-
-	auser = p_new(user->pool, struct autocreate_user, 1);
-	p_array_init(&auser->autocreate_mailboxes, user->pool, 8);
-	read_autobox_settings(user, &auser->autocreate_mailboxes, "autocreate");
-
-	p_array_init(&auser->autosubscribe_mailboxes, user->pool, 8);
-	read_autobox_settings(user, &auser->autosubscribe_mailboxes,
-			      "autosubscribe");
-
-	MODULE_CONTEXT_SET(user, autocreate_user_module, auser);
+	read_autobox_settings(user, "autocreate", FALSE);
+	read_autobox_settings(user, "autosubscribe", TRUE);
 }
 
-
 static struct mail_storage_hooks autocreate_mail_storage_hooks = {
-	.mailbox_allocated = autocreate_mailbox_allocated,
-	.mailbox_list_created = autocreate_mailbox_list_created,
-	.mail_namespaces_created = autocreate_mail_namespaces_created
+	.mail_user_created = autocreate_mail_user_created
 };
 
 void autocreate_plugin_init(struct module *module)